From 33e85291b92641cc15fa03413cdfc85e7860b86f Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Wed, 11 Dec 2024 00:05:35 +1100 Subject: [PATCH 1/6] Fix invalid XML in design-time targets --- .../DesignTimeTargets/Microsoft.Managed.DesignTime.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DesignTimeTargets/Microsoft.Managed.DesignTime.targets b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DesignTimeTargets/Microsoft.Managed.DesignTime.targets index 95598dea03..091c34c751 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DesignTimeTargets/Microsoft.Managed.DesignTime.targets +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DesignTimeTargets/Microsoft.Managed.DesignTime.targets @@ -431,7 +431,7 @@ implementation when using older SDKs that don't have it; otherwise our design-time builds will fail. The NuGet.targets file in the SDK is imported _after_ this file, and will override this implementation with the real one (when present). This can (and should) be removed when we no longer need to support SDKs older than 9.0.200. --> - + Date: Wed, 11 Dec 2024 10:41:04 +1100 Subject: [PATCH 2/6] Test that DT targets evaluate and build We recently had an issue where a design-time target was merged containing invalid XML. These targets are crucial to almost everything the project system does, so a failure in them completely breaks the IDE. In this change we add a unit test to validate that our C#/VB/F# design-time targets files can be correctly evaluated, and can build a simple target. This ensures both that they contain valid XML with valid MSBuild semantics. --- .../DesignTimeTargetsTests.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/DesignTimeTargets/DesignTimeTargetsTests.cs diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/DesignTimeTargets/DesignTimeTargetsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/DesignTimeTargets/DesignTimeTargetsTests.cs new file mode 100644 index 0000000000..319b00a695 --- /dev/null +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/DesignTimeTargets/DesignTimeTargetsTests.cs @@ -0,0 +1,63 @@ +// 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.md file in the project root for more information. + +using Microsoft.VisualStudio.Utilities; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; + +namespace Microsoft.VisualStudio.ProjectSystem.DesignTimeTargets; + +public sealed class DesignTimeTargetsTests +{ + [Theory] + [InlineData("Microsoft.CSharp.DesignTime.targets")] + [InlineData("Microsoft.VisualBasic.DesignTime.targets")] + [InlineData("Microsoft.FSharp.DesignTime.targets")] + public void ValidateDesignTimeTargetsEvaluateAndBuild(string targetFileName) + { + // eg: src\Microsoft.VisualStudio.ProjectSystem.Managed\ProjectSystem\DesignTimeTargets\Microsoft.CSharp.DesignTime.targets + + string path = Path.Combine(RepoUtil.FindRepoRootPath(), "src", "Microsoft.VisualStudio.ProjectSystem.Managed", "ProjectSystem", "DesignTimeTargets", targetFileName); + + // Force an evaluation of the project. + Project project = new(path); + + Logger logger = new(); + + // Build a target. This isn't a particularly interesting target, but it's one of the few + // that don't require a target defined elsewhere. If we were to try and build all targets + // in the project (via project.Targets.Keys) we would hit error MSB4057 (target not found) + // because we are not importing the common targets, etc. here. This is just a smoke test + // to make sure we can build a target. + project.Build(["CollectPackageReferences"], [logger]); + + // Unload everything when done. + project.ProjectCollection.UnloadAllProjects(); + + Assert.Empty(logger.Errors); + Assert.True(logger.Succeeded); + } + + private sealed class Logger : ILogger + { + public LoggerVerbosity Verbosity { get; set; } = LoggerVerbosity.Quiet; + public string? Parameters { get; set; } + + public bool? Succeeded { get; private set; } + + public ImmutableList Errors = []; + + public void Initialize(IEventSource eventSource) + { + eventSource.ErrorRaised += (s, e) => ImmutableInterlocked.Update( + ref Errors, + static (errors, e) => errors.Add(e), + e); + + eventSource.BuildFinished += (s, e) => Succeeded = e.Succeeded; + } + + public void Shutdown() + { + } + } +} From b982c3a815fab8f801f4f20def02c2557dc94c6d Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Wed, 11 Dec 2024 11:45:39 +1100 Subject: [PATCH 3/6] Run "dotnet format" Excludes a few changes that break some of our columnar alignment. --- .../IncrementalBuildFailureDetector.cs | 2 +- .../VS/Build/SolutionBuildManager.cs | 4 ++-- .../VS/Debug/StartupProjectRegistrar.cs | 2 +- .../VS/Debug/VBDefineConstantsEncoding.cs | 4 ++-- ...portedTargetPlatformVersionEnumProvider.cs | 6 ++--- .../HotReloadDiagnosticOutputService.cs | 2 +- .../IProjectHotReloadNotificationService.cs | 2 +- .../IProjectHotReloadSessionManager.cs | 2 +- .../ProjectHotReloadSessionManager.cs | 2 +- .../AbstractGenerateNuGetPackageCommand.cs | 2 +- .../VS/PackageRestore/NuGetRestoreService.cs | 6 ++--- .../AuthenticationModeEnumProvider.cs | 4 ++-- .../Properties/DotNetImportsEnumProvider.cs | 5 +++-- .../ApplicationFrameworkValueProvider.cs | 8 +++---- .../TargetPlatformMonikersValueProvider.cs | 2 +- ...ectFileOrAssemblyInfoPropertiesProvider.cs | 2 +- .../VisualBasic/SplashScreenEnumProvider.cs | 2 +- .../CSharpImplicitUsingsControl.cs | 22 +++++++++---------- .../ImplicitUsingsEditor.cs | 2 +- .../PropertyPages/KeyboardFocusBehaviour.cs | 6 ++--- .../IProjectLaunchProfileHandler.cs | 2 +- .../PropertyPageByIdDataProducer.cs | 2 +- .../QueryProjectPropertiesContext.cs | 4 ++-- .../PropertyPages/UIPropertyDataProducer.cs | 7 +++--- .../QueryDataFromProviderStateProducerBase.cs | 2 +- .../VS/References/AbstractReferenceHandler.cs | 4 ++-- .../DesignTimeAssemblyResolution.cs | 2 +- .../VS/References/NullCommand.cs | 2 +- .../VS/References/RemoveReferenceCommand.cs | 2 +- .../SetTreatAsUsedAttributeCommand.cs | 2 +- .../VS/References/UnSetAttributeCommand.cs | 3 ++- .../VS/Rename/FileMoveNotificationListener.cs | 2 +- .../Rename/RenamerProjectTreeActionHandler.cs | 2 +- .../AggregateContainedByRelationCollection.cs | 2 +- .../Commands/ObjectBrowserCommand.cs | 2 +- .../BuildUpToDateCheck.Subscription.cs | 4 ++-- .../VS/UserNotificationServices.cs | 4 ++-- .../ProjectSystem/VS/VsUIService`2.cs | 2 +- .../VS/Waiting/VisualStudioWaitContext.cs | 2 +- .../VS/WindowsForms/MyAppFileAccessor.cs | 2 +- .../WindowsFormsEditorProvider.cs | 2 +- .../WPF/Converters/Converters.cs | 8 +++---- .../Legacy/LegacyDependencySubscriber.cs | 6 ++--- .../DependenciesSnapshotProvider.cs | 9 ++++---- 44 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs index 1349f7276c..0ddf19ac15 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs @@ -1,8 +1,8 @@ // 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.md file in the project root for more information. using Microsoft.VisualStudio.ProjectSystem.Properties; -using Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; using Microsoft.VisualStudio.ProjectSystem.VS.Editor; +using Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs index 369e1697a1..9b8f366df1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs @@ -97,7 +97,7 @@ public int QueryBuildManagerBusy() JoinableFactory.Context.VerifyIsOnMainThread(); ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.QueryBuildManagerBusy(out int flags)); - + return flags; } @@ -108,7 +108,7 @@ public uint QueryBuildManagerBusyEx() JoinableFactory.Context.VerifyIsOnMainThread(); ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager3.QueryBuildManagerBusyEx(out uint flags)); - + return flags; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs index 171617df4f..b8319e7760 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs @@ -116,7 +116,7 @@ private async Task IsDebuggableAsync() { return true; } - } + } } if (!foundStartupProjectProvider) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs index c0f5761981..ae933d937f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs @@ -26,7 +26,7 @@ internal sealed class VBDefineConstantsEncoding : INameValuePairListEncoding } yield return resultingEntry; - + } static IEnumerable ReadEntries(string rawText) @@ -127,7 +127,7 @@ static string EncodePair((string Name, string Value) pair) return $"{EncodeCharacters(pair.Name)}={EncodeCharacters(pair.Value)}"; } } - + static string EncodeCharacters(string value) { int i = value.Length - 1; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs index 22ef48054f..28e93622ea 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs @@ -14,9 +14,9 @@ internal class SdkSupportedTargetPlatformVersionEnumProvider : SingleRuleSupport { [ImportingConstructor] public SdkSupportedTargetPlatformVersionEnumProvider( - ConfiguredProject project, - IProjectSubscriptionService subscriptionService) - : base(project, subscriptionService, ruleName: SdkSupportedTargetPlatformVersion.SchemaName) {} + ConfiguredProject project, + IProjectSubscriptionService subscriptionService) + : base(project, subscriptionService, ruleName: SdkSupportedTargetPlatformVersion.SchemaName) { } protected override IEnumValue ToEnumValue(KeyValuePair> item) { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs index 3e4aea4a8b..f381074d9a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs @@ -22,7 +22,7 @@ public void WriteLine(HotReloadLogMessage hotReloadLogMessage, CancellationToken { _threadingService.RunAndForget(() => _hotReloadLogger.LogAsync(hotReloadLogMessage, cancellationToken).AsTask(), unconfiguredProject: null); } - + public static uint GetProcessId(Process? process = null) { return (uint)(process?.Id ?? 0); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadNotificationService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadNotificationService.cs index 860882eb82..f9a82672a5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadNotificationService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadNotificationService.cs @@ -18,7 +18,7 @@ public interface IProjectHotReloadNotificationService /// /// The current state of hot reload for the project /// - bool IsProjectInHotReload{ get; } + bool IsProjectInHotReload { get; } /// /// Called by project systems when it enters or exits a hot reload session. diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs index c40e111843..2bc4648b3a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs @@ -17,7 +17,7 @@ internal interface IProjectHotReloadSessionManager /// otherwise. /// Task TryCreatePendingSessionAsync(IDictionary environmentVariables); - + /// /// Activates the pending Hot Reload session and associates it with the specified /// process. diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs index 170a6b0299..91fbd3b578 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs @@ -349,7 +349,7 @@ private async Task OnProcessExitedAsync(HotReloadState hotReloadState) HotReloadDiagnosticErrorLevel.Info ), default); - + await StopProjectAsync(hotReloadState, default); hotReloadState.Process.Exited -= hotReloadState.OnProcessExited; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs index 485c5c3dde..35e8eb4080 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs @@ -12,7 +12,7 @@ internal abstract class AbstractGenerateNuGetPackageCommand : AbstractSingleNode private readonly IProjectThreadingService _threadingService; private readonly ISolutionBuildManager _solutionBuildManager; private readonly GeneratePackageOnBuildPropertyProvider _generatePackageOnBuildPropertyProvider; - + private IAsyncDisposable? _subscription; protected AbstractGenerateNuGetPackageCommand( diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PackageRestore/NuGetRestoreService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PackageRestore/NuGetRestoreService.cs index 5713dd4a6a..10d84afed2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PackageRestore/NuGetRestoreService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PackageRestore/NuGetRestoreService.cs @@ -49,9 +49,9 @@ public async Task NominateAsync(ProjectRestoreInfo restoreData, IReadOnlyC try { _restoring = true; - + Task restoreOperation = _solutionRestoreService.NominateProjectAsync(_project.FullPath, new VsProjectRestoreInfo(restoreData), cancellationToken); - + SaveNominatedConfiguredVersions(inputVersions); return await restoreOperation; @@ -199,7 +199,7 @@ private bool IsSavedNominationOutOfDate(ConfiguredProject activeConfiguredProjec { return true; } - + if (_savedNominatedConfiguredVersion.Count == 1) { return false; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs index f59d643832..95d8b2e862 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs @@ -1,8 +1,8 @@ // 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.md file in the project root for more information. using Microsoft.Build.Framework.XamlTypes; -using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.Debug; +using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Threading; namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties @@ -37,7 +37,7 @@ public AuthenticationModeEnumValuesGenerator(IRemoteDebuggerAuthenticationServic public Task> GetListedValuesAsync() { - var enumValues =_remoteDebuggerAuthenticationService + var enumValues = _remoteDebuggerAuthenticationService .GetRemoteAuthenticationModes() .Select(i => new PageEnumValue(new EnumValue { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/DotNetImportsEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/DotNetImportsEnumProvider.cs index 23a93b0f76..4611e331e9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/DotNetImportsEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/DotNetImportsEnumProvider.cs @@ -62,7 +62,7 @@ static bool NamespaceIsReferenceableFromCompilation(INamespaceSymbol namespaceSy foreach (INamedTypeSymbol typeMember in namespaceSymbol.GetTypeMembers()) { if (typeMember.CanBeReferencedByName - && (typeMember.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public + && (typeMember.DeclaredAccessibility == Accessibility.Public || SymbolEqualityComparer.Default.Equals(typeMember.ContainingAssembly, compilation.Assembly) || typeMember.ContainingAssembly.GivesAccessTo(compilation.Assembly))) { @@ -80,7 +80,8 @@ public async Task> GetListedValuesAsync() { return (await GetNamespacesAsync()).Select(namespaceString => (IEnumValue)new PageEnumValue(new EnumValue { - Name = namespaceString, DisplayName = namespaceString + Name = namespaceString, + DisplayName = namespaceString })).ToImmutableList(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs index 80f8c110f8..fb392aa110 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs @@ -26,7 +26,7 @@ internal sealed class ApplicationFrameworkValueProvider : InterceptingPropertyVa { private const string EnabledValue = "WindowsForms"; private const string DisabledValue = "WindowsFormsWithCustomSubMain"; - + private const string WinExeOutputType = "WinExe"; private const string NoneItemType = "None"; private const string ApplicationDefinitionItemType = "ApplicationDefinition"; @@ -65,7 +65,7 @@ public ApplicationFrameworkValueProvider( else { return await SetPropertyValueAsync(propertyName, unevaluatedPropertyValue); - } + } } public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) @@ -151,7 +151,7 @@ private async Task IsWPFApplicationAsync(IProjectProperties defaultPropert private async Task SetPropertyValueForDefaultProjectTypesAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) { string rootNamespace = await defaultProperties.GetEvaluatedPropertyValueAsync("RootNamespace"); - + if (bool.TryParse(unevaluatedPropertyValue, out bool value)) { if (value) @@ -358,7 +358,7 @@ private async Task GetPropertyValueAsync(string propertyName) } } - await (propertyName switch + await (propertyName switch { ApplicationFramework => _myAppXmlFileAccessor.SetMySubMainAsync(unevaluatedPropertyValue), EnableVisualStyles => _myAppXmlFileAccessor.SetEnableVisualStylesAsync(Convert.ToBoolean(unevaluatedPropertyValue)), diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs index 42614014c1..3c0670b10b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs @@ -35,7 +35,7 @@ public override async Task OnGetEvaluatedPropertyValueAsync(string prope string? currentPlatformVersion = (string?)await configuration.TargetPlatformVersion.GetValueAsync(); Assumes.NotNull(currentPlatformMoniker); - builder.Add($"{ currentPlatformMoniker }, Version={ currentPlatformVersion }"); + builder.Add($"{currentPlatformMoniker}, Version={currentPlatformVersion}"); } return string.Join(";", builder.ToArrayAndFree()); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs index 3d44e2510c..8db5a4be53 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs @@ -21,7 +21,7 @@ internal class ProjectFileOrAssemblyInfoPropertiesProvider : AbstractProjectFile public ProjectFileOrAssemblyInfoPropertiesProvider( [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider delegatedProvider, [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, - [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)]IEnumerable> interceptingValueProviders, + [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)] IEnumerable> interceptingValueProviders, UnconfiguredProject project, IWorkspaceWriter workspaceWriter, VisualStudioWorkspace workspace, diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/SplashScreenEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/SplashScreenEnumProvider.cs index 7c3392afb0..2c44e17fd6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/SplashScreenEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/SplashScreenEnumProvider.cs @@ -99,7 +99,7 @@ public async Task> GetListedValuesAsync() // Remove selected startup object (if any) from the list, as a user should not be able to select it again. ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); object? startupObjectObject = await configuration.StartupObject.GetValueAsync(); - + if (startupObjectObject is string { Length: > 0 } startupObject) { enumValues.RemoveAll(ev => StringComparers.PropertyValues.Equals(ev.Name, startupObject)); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs index 4f537d9a3c..897e5cd84d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs @@ -47,13 +47,13 @@ internal sealed class CSharpImplicitUsingsControl : Control typeof(bool), typeof(CSharpImplicitUsingsControl), new PropertyMetadata(false)); - + public static readonly DependencyProperty ShouldShowReadonlyUsingsProperty = DependencyProperty.Register( nameof(ShouldShowReadonlyUsings), typeof(bool), typeof(CSharpImplicitUsingsControl), new PropertyMetadata(true, (o, e) => ((CSharpImplicitUsingsControl)o).OnStringListOrSupportedValuesPropertyChanged())); - + // Used to suppress event handling during our own updates, breaking infinite loops. private bool _isUpdating; @@ -110,13 +110,13 @@ public bool EnteredUserUsingIsStatic get => (bool)GetValue(EnteredUserUsingIsStaticProperty); set => SetValue(EnteredUserUsingIsStaticProperty, value); } - + public bool ShouldShowReadonlyUsings { get => (bool)GetValue(ShouldShowReadonlyUsingsProperty); set => SetValue(ShouldShowReadonlyUsingsProperty, value); } - + public string StringList { get => (string)GetValue(StringListProperty); @@ -144,7 +144,7 @@ private void OnStringListOrSupportedValuesPropertyChanged() { return; } - + rawImplicitUsings.Sort((x, y) => { if ((x.IsReadOnly && y.IsReadOnly) || (!x.IsReadOnly && !y.IsReadOnly)) @@ -161,7 +161,7 @@ private void OnStringListOrSupportedValuesPropertyChanged() } var currentlySetImplicitUsings = rawImplicitUsings.GroupBy(implicitUsing => implicitUsing.Include).Select(group => group.First()).ToList(); - + if (UsingCollectionState.Count == 0) { foreach (ImplicitUsing implicitUsing in currentlySetImplicitUsings) @@ -252,7 +252,7 @@ internal sealed class ImplicitUsingModel : INotifyPropertyChanged private string _alias; private bool _isStatic; private bool _isReadOnly; - + private bool _forceIncludesFocus; private bool _forceAliasFocus; private bool _forceIsStaticFocus; @@ -264,7 +264,7 @@ public ImplicitUsingModel(string include, string alias, bool isStatic, bool isRe _isStatic = isStatic; _isReadOnly = isReadOnly; _parent = parent; - + _forceIncludesFocus = false; _forceAliasFocus = false; _forceIsStaticFocus = false; @@ -351,7 +351,7 @@ public bool IsReadOnly _parent.NotifyPairChanged(); } } - + public bool ForceIncludesFocus { get => _forceIncludesFocus; @@ -361,7 +361,7 @@ public bool ForceIncludesFocus PropertyChanged?.Invoke(this, s_forceIncludesFocusChangeArgs); } } - + public bool ForceAliasFocus { get => _forceAliasFocus; @@ -371,7 +371,7 @@ public bool ForceAliasFocus PropertyChanged?.Invoke(this, s_forceAliasFocusChangeArgs); } } - + public bool ForceIsStaticFocus { get => _forceIsStaticFocus; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/ImplicitUsingsEditor.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/ImplicitUsingsEditor.cs index 1896bd10e3..10c2b7a89c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/ImplicitUsingsEditor.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/ImplicitUsingsEditor.cs @@ -1,4 +1,4 @@ -// 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.md file in the project root for more information. +// 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.md file in the project root for more information. using Microsoft.VisualStudio.ProjectSystem.VS.Implementation.PropertyPages.Designer; using Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages.Designer; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/KeyboardFocusBehaviour.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/KeyboardFocusBehaviour.cs index 14aff5f896..610672a4d1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/KeyboardFocusBehaviour.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/KeyboardFocusBehaviour.cs @@ -1,4 +1,4 @@ -// 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.md file in the project root for more information. +// 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.md file in the project root for more information. using System.Windows; using System.Windows.Controls; @@ -11,8 +11,8 @@ internal static class KeyboardFocusBehaviour public static readonly DependencyProperty IsKeyboardFocusedProperty = DependencyProperty.RegisterAttached( "IsKeyboardFocused", - typeof (bool), - typeof (KeyboardFocusBehaviour), + typeof(bool), + typeof(KeyboardFocusBehaviour), new UIPropertyMetadata(false, OnIsKeyboardFocusedPropertyChanged)); public static bool GetIsKeyboardFocused(DependencyObject obj) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs index 90e4ca9611..c36c87a634 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs @@ -16,7 +16,7 @@ internal interface IProjectLaunchProfileHandler /// Returns entities representing all launch profiles in the project. /// Task> RetrieveAllLaunchProfileEntitiesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ILaunchProfilePropertiesAvailableStatus requestedProperties); - + /// /// Returns the entity representing the launch profile specified by the given . /// diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs index d0aaa3391e..0372b1a62f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs @@ -38,7 +38,7 @@ public PropertyPageByIdDataProducer(IPropertyPagePropertiesAvailableStatus prope } return NullEntityValue; - + } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs index 0364a8ec21..37b965581c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs @@ -66,12 +66,12 @@ public override int GetHashCode() { int hashCode = IsProjectFile.GetHashCode(); hashCode = (hashCode * -1521134295) + StringComparers.Paths.GetHashCode(File); - + if (ItemType is not null) { hashCode = (hashCode * -1521134295) + StringComparers.ItemTypes.GetHashCode(ItemType); } - + if (ItemName is not null) { hashCode = (hashCode * -1521134295) + StringComparers.ItemNames.GetHashCode(ItemName); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs index 3351e60607..ae700c577f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs @@ -116,20 +116,19 @@ public static IEntityValue CreateUIPropertyValue(IQueryExecutionContext queryExe string? dimensionVisibilityCondition = property.GetMetadataValueOrNull("DimensionVisibilityCondition"); newUIProperty.DimensionVisibilityCondition = dimensionVisibilityCondition ?? string.Empty; } - - + if (requestedProperties.ConfiguredValueVisibilityCondition) { string? configuredValueVisibilityCondition = property.GetMetadataValueOrNull("ConfiguredValueVisibilityCondition"); newUIProperty.ConfiguredValueVisibilityCondition = configuredValueVisibilityCondition ?? string.Empty; } - + if (requestedProperties.IsReadOnlyCondition) { string? isReadOnlyCondition = property.GetMetadataValueOrNull("IsReadOnlyCondition"); newUIProperty.IsReadOnlyCondition = isReadOnlyCondition ?? string.Empty; } - + ((IEntityValueFromProvider)newUIProperty).ProviderState = new PropertyProviderState(cache, property.ContainingRule, propertiesContext, property.Name); return newUIProperty; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs index 7e1cd7ab8c..4f1d09e2c4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs @@ -14,7 +14,7 @@ internal abstract class QueryDataFromProviderStateProducerBase : QueryDataPro { public async Task SendRequestAsync(QueryProcessRequest request) { - if ((request.RequestData as IEntityValueFromProvider)?.ProviderState is T providerState) + if (request.RequestData is IEntityValueFromProvider { ProviderState: T providerState }) { try { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs index 10527f19d9..3beed6abdc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs @@ -1,7 +1,7 @@ // 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.md file in the project root for more information. -using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; +using Microsoft.VisualStudio.ProjectSystem.Properties; namespace Microsoft.VisualStudio.ProjectSystem.VS.References { @@ -102,7 +102,7 @@ internal IProjectSystemUpdateReferenceOperation CreateUnsetAttributeCommand(Conf public async Task> GetAttributesAsync(ConfiguredProject selectedConfiguredProject, string itemSpecification) { - Dictionary propertyValues = new (); + Dictionary propertyValues = new(); IProjectItem? items = await GetProjectItemsAsync(selectedConfiguredProject, itemSpecification); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs index dd197525ef..bc995eaafc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs @@ -75,7 +75,7 @@ public int ResolveAssemblyPathInTargetFx(string?[]? prgAssemblySpecs, uint cAsse return HResult.OK; } - private uint ResolveReferences(string?[] originalNames, AssemblyName[] assemblyName, [In, Out]VsResolvedAssemblyPath[] assemblyPaths) + private uint ResolveReferences(string?[] originalNames, AssemblyName[] assemblyName, [In, Out] VsResolvedAssemblyPath[] assemblyPaths) { Assumes.True(originalNames.Length == assemblyName.Length && originalNames.Length == assemblyPaths.Length); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs index 08720c996a..37a64343ea 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs @@ -1,7 +1,7 @@ // 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.md file in the project root for more information. -using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; +using Microsoft.VisualStudio.Threading; namespace Microsoft.VisualStudio.ProjectSystem.VS.References { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs index 884c4c6fb5..53310223fc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs @@ -35,7 +35,7 @@ public async Task RevertAsync(CancellationToken cancellationToken) { await _referenceHandler.SetAttributesAsync(_selectedConfiguredProject, _itemSpecification, _projectPropertiesValues); } - + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs index 2873932dd6..ed2b723778 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs @@ -1,7 +1,7 @@ // 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.md file in the project root for more information. -using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; +using Microsoft.VisualStudio.ProjectSystem.Properties; namespace Microsoft.VisualStudio.ProjectSystem.VS.References { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs index 47432e8edd..64246463fc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs @@ -7,7 +7,8 @@ namespace Microsoft.VisualStudio.ProjectSystem.VS.References internal class UnsetAttributeCommand : SetTreatAsUsedAttributeCommand { public UnsetAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) - : base(abstractReferenceHandler, selectedConfiguredProject, itemSpecification) { + : base(abstractReferenceHandler, selectedConfiguredProject, itemSpecification) + { UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs index aef7b39b74..b93b34f9fc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs @@ -101,7 +101,7 @@ public async Task OnBeforeFilesMovedAsync(IReadOnlyCollection ite static IEnumerable GetFilesToMove(IEnumerable items) { var itemQueue = new Queue(items); - while(itemQueue.Count > 0) + while (itemQueue.Count > 0) { IFileMoveItem item = itemQueue.Dequeue(); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs index 6a6f94f987..69ca2440ee 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs @@ -39,7 +39,7 @@ internal partial class RenamerProjectTreeActionHandler : ProjectTreeActionHandle public RenamerProjectTreeActionHandler( UnconfiguredProject unconfiguredProject, IUnconfiguredProjectVsServices projectVsServices, - [Import(typeof(VisualStudioWorkspace))]Lazy workspace, + [Import(typeof(VisualStudioWorkspace))] Lazy workspace, IEnvironmentOptions environmentOptions, IUserNotificationServices userNotificationServices, IRoslynServices roslynServices, diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs index 9c82e06019..df3b8d0301 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs @@ -27,7 +27,7 @@ internal AggregateContainedByRelationCollection(List parentItems) public bool HasItems => _parentItems.Count != 0; - void IAggregateRelationCollection.EnsureMaterialized() {} + void IAggregateRelationCollection.EnsureMaterialized() { } public IEnumerator GetEnumerator() => _parentItems.GetEnumerator(); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ObjectBrowserCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ObjectBrowserCommand.cs index 9517a4017e..cd6650724d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ObjectBrowserCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ObjectBrowserCommand.cs @@ -54,7 +54,7 @@ internal sealed class ObjectBrowserCommand(UnconfiguredProject project, IVsUISer protected override async Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) { string? path = await TryGetAssemblyPathAsync(node); - + if (path is not null) { // We handle this. diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs index 5c4c1b2c22..f97a3ba176 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs @@ -67,7 +67,7 @@ public Subscription(IUpToDateCheckConfiguredInputDataSource inputDataSource, Con _configuredProject = configuredProject; _host = host; _persistence = persistence; - + _semaphore = ReentrantSemaphore.Create( initialCount: 1, joinableTaskContext: configuredProject.UnconfiguredProject.Services.ThreadingPolicy.JoinableTaskContext.Context, @@ -126,7 +126,7 @@ public async Task RunAsync( public async Task UpdateLastSuccessfulBuildStartTimeUtcAsync(DateTime lastSuccessfulBuildStartTimeUtc, bool isRebuild) { IEnumerable configurations; - + if (isRebuild) { // During a rebuild the fast up-to-date check is not called, so we cannot rely upon diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs index 327750ac65..be14c47a09 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs @@ -1,10 +1,10 @@ // 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.md file in the project root for more information. +using System.Globalization; +using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using static Microsoft.VisualStudio.VSConstants; -using Microsoft.VisualStudio.PlatformUI; -using System.Globalization; namespace Microsoft.VisualStudio.ProjectSystem.VS { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs index 5a10467578..c3ec248633 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs @@ -16,7 +16,7 @@ internal class VsUIService : VsUIService, IVsU where TInterface : class? { [ImportingConstructor] - public VsUIService([Import(typeof(SVsServiceProvider))]IServiceProvider serviceProvider, JoinableTaskContext joinableTaskContext) + public VsUIService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, JoinableTaskContext joinableTaskContext) : base(serviceProvider, joinableTaskContext) { } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs index 5e2e04f8d9..2752f1dc48 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs @@ -90,7 +90,7 @@ public void Update(string? message = null, int? currentStep = null, int? totalSt _totalSteps = totalSteps.Value; hasChange = true; } - + if (currentStep is not null && currentStep != _currentStep) { Requires.Argument(currentStep <= _totalSteps, nameof(currentStep), $"Must be less than or equal to the total number of steps."); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/MyAppFileAccessor.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/MyAppFileAccessor.cs index ddb8215955..856381e774 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/MyAppFileAccessor.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/MyAppFileAccessor.cs @@ -84,7 +84,7 @@ private void DocData_Modifying(object sender, EventArgs e) await writer.FlushAsync(); } } - + await _threadingService.SwitchToUIThread(); try { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs index 402eda7168..53316fa9cf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs @@ -54,7 +54,7 @@ public WindowsFormsEditorProvider( CancellationToken cancellationToken = _projectAsynchronousTasksService.UnloadCancellationToken; - (IProjectSpecificEditorInfo? editor, SubTypeDescriptor ? descriptor) + (IProjectSpecificEditorInfo? editor, SubTypeDescriptor? descriptor) = await (GetDefaultEditorAsync(documentMoniker), GetSubTypeDescriptorAsync(documentMoniker, cancellationToken)); if (editor is null || descriptor is null) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs index 0474c27ab8..c0fc88127c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs @@ -49,7 +49,7 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu internal static partial class Converters { private const string DimensionSeparator = " & "; - + public static ImmutableArray ImmutableStringArray => ImmutableArray.Empty; public static IMultiValueConverter DimensionNames { get; } = new LambdaMultiConverter, ImmutableArray, string>(GetDimensionNames); @@ -57,15 +57,15 @@ internal static partial class Converters public static IValueConverter BoolToVisibilityConverter { get; } = new LambdaConverter(b => b ? Visibility.Visible : Visibility.Collapsed); public static IValueConverter IsEnteredUserAliasStringEnabled { get; } = new LambdaConverter(isStatic => !isStatic); - + public static IMultiValueConverter IsListViewAliasStringEnabled { get; } = new LambdaMultiConverter((isReadOnly, isStatic) => !isReadOnly && !isStatic); public static IMultiValueConverter IsIsStaticCheckboxEnabled { get; } = new LambdaMultiConverter((alias, isReadOnly) => !isReadOnly && alias.Length == 0); - + public static IValueConverter IsStaticCheckboxText { get; } = new LambdaConverter(alias => alias.Length == 0 ? "Yes" : "No"); public static IValueConverter InvertBoolean { get; } = new LambdaConverter(b => !b); - + private static string GetDimensionNames(ImmutableDictionary map, ImmutableArray dimensions) { if (map.IsEmpty || dimensions.IsEmpty) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/LegacyDependencySubscriber.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/LegacyDependencySubscriber.cs index b7192e8ed2..1ae61aeca2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/LegacyDependencySubscriber.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/LegacyDependencySubscriber.cs @@ -1,12 +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.md file in the project root for more information. using System.Threading.Tasks.Dataflow; +using Microsoft.VisualStudio; using Microsoft.VisualStudio.Composition; -using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot; -using Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies; using Microsoft.VisualStudio.Imaging.Interop; -using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot; using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Subscriptions; +using Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies; namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Legacy; diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesSnapshotProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesSnapshotProvider.cs index 1948f1c11a..e34d63a475 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesSnapshotProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesSnapshotProvider.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -using Microsoft.VisualStudio.Composition; -using Microsoft.VisualStudio.ProjectSystem.Utilities; -using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot; -using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Legacy; using System.Threading.Tasks.Dataflow; +using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.ProjectSystem.Properties; - +using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Legacy; +using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot; +using Microsoft.VisualStudio.ProjectSystem.Utilities; using DependenciesSnapshotInput = ( System.Collections.Immutable.ImmutableDictionary> UnconfiguredDependencies, System.Collections.Immutable.ImmutableDictionary ConfiguredDependencies, From e13927f27b47320d8179af850b1ea31e8b24e12b Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Wed, 11 Dec 2024 12:26:52 +1100 Subject: [PATCH 4/6] Use file-scoped namespaces throughout solution This change was applied automatically in VS, via the Roslyn codefix. --- .../ProvideCodeBaseBindingRedirection.cs | 51 +- .../CodeMarkerTimerId.cs | 17 +- .../IO/WindowsFileExplorer.cs | 109 +- .../Input/CommandGroup.cs | 27 +- .../Input/ExternalResourceIds.cs | 59 +- .../Input/FSharpProjectCommandId.cs | 17 +- .../Input/ManagedProjectSystemCommandId.cs | 23 +- .../ManagedProjectSystemOrderCommandId.cs | 21 +- .../Input/UIHierarchyWindowCommandId.cs | 17 +- .../Input/VisualStudioStandard2KCommandId.cs | 15 +- .../Input/VisualStudioStandard97CommandId.cs | 17 +- .../Input/WPFCommandId.cs | 31 +- .../Packaging/DebuggerTraceListener.cs | 75 +- .../Packaging/ManagedProjectSystemPackage.cs | 63 +- .../Packaging/ProjectTypeCapabilities.cs | 101 +- ...rovideDiffSupportedContentTypeAttribute.cs | 41 +- .../ProvideEditorFactoryMappingAttribute.cs | 33 +- .../ProjectSystem/ProjectType.cs | 115 +- .../AbstractExtenderCATIDProvider.cs | 193 +- .../CSharp/CSharpExtenderCATIDProvider.cs | 49 +- .../Automation/DotNetNamespaceImportsList.cs | 315 +- .../VS/Automation/DotNetVSImports.cs | 223 +- .../VisualBasicExtenderCATIDProvider.cs | 49 +- .../VisualBasic/VisualBasicVSProjectEvents.cs | 41 +- .../VS/Automation/VsBuildManager.cs | 207 +- .../ProjectSystem/VS/Automation/VsProject.cs | 197 +- .../VsProject_VsLangProjectProperties.cs | 249 +- .../IIncrementalBuildFailureReporter.cs | 51 +- ...talBuildFailureDetector.IProjectChecker.cs | 29 +- ...ntalBuildFailureDetector.ProjectChecker.cs | 147 +- .../IncrementalBuildFailureDetector.cs | 271 +- ...ementalBuildFailureOutputWindowReporter.cs | 93 +- ...ncrementalBuildFailureTelemetryReporter.cs | 69 +- .../VS/Build/ISolutionBuildManager.cs | 37 +- .../ImplicitlyTriggeredDebugBuildManager.cs | 189 +- ...rviceErrorListProvider.ErrorListDetails.cs | 171 +- .../Build/LanguageServiceErrorListProvider.cs | 291 +- .../VS/Build/SolutionBuildManager.cs | 249 +- .../CSharpProjectCompatibilityProvider.cs | 45 +- .../CSharp/CSharpProjectTypeGuidProvider.cs | 29 +- .../VS/ConnectionPoint/ConnectionPoint.cs | 99 +- .../ConnectionPointContainer.cs | 59 +- .../VS/ConnectionPoint/IEventSource.cs | 19 +- .../VS/CreateFileFromTemplateService.cs | 97 +- .../Debug/ErrorProfileDebugTargetsProvider.cs | 97 +- .../IDebugProfileLaunchTargetsProvider.cs | 65 +- .../IDebugProfileLaunchTargetsProvider2.cs | 25 +- .../IDebugProfileLaunchTargetsProvider3.cs | 17 +- .../IDebugProfileLaunchTargetsProvider4.cs | 39 +- .../VS/Debug/IStartupProjectProvider.cs | 19 +- .../LaunchProfilesDebugLaunchProvider.cs | 567 ++- .../LaunchProfilesDebugPageGuidProvider.cs | 23 +- .../VS/Debug/ProjectLaunchTargetsProvider.cs | 887 ++-- .../RemoteDebuggerAuthenticationService.cs | 75 +- .../VS/Debug/StartupProjectHelper.cs | 105 +- .../VS/Debug/StartupProjectRegistrar.cs | 199 +- .../VS/Debug/VBDefineConstantsEncoding.cs | 213 +- .../ProjectSystem/VS/DteEnvironmentOptions.cs | 53 +- .../VS/Editor/IRunningDocumentTable.cs | 25 +- .../VS/Editor/RunningDocumentTable.cs | 75 +- .../ExportProjectNodeComServiceAttribute.cs | 49 +- .../VS/FSharp/FSharpProjectSelector.cs | 97 +- .../FSharp/FSharpProjectTypeGuidProvider.cs | 29 +- ...tedTargetPlatformIdentifierEnumProvider.cs | 51 +- ...portedTargetPlatformVersionEnumProvider.cs | 49 +- ...pportedTargetFrameworkAliasEnumProvider.cs | 173 +- .../HotReloadDiagnosticOutputService.cs | 39 +- .../IHotReloadDiagnosticOutputService.cs | 23 +- .../VS/HotReload/IProjectHotReloadAgent.cs | 13 +- .../VS/HotReload/IProjectHotReloadSession.cs | 79 +- .../IProjectHotReloadSessionCallback.cs | 31 +- .../IProjectHotReloadSessionInternal.cs | 17 +- .../IProjectHotReloadSessionManager.cs | 39 +- .../IProjectHotReloadUpdateApplier.cs | 33 +- .../VS/HotReload/ProjectHotReloadAgent.cs | 63 +- .../VS/HotReload/ProjectHotReloadSession.cs | 339 +- .../ProjectHotReloadSessionManager.cs | 789 ++- .../ProjectSystem/VS/IEnvironmentOptions.cs | 77 +- .../ProjectSystem/VS/IPackageService.cs | 29 +- .../VS/IProjectServiceExtensions.cs | 63 +- .../ProjectSystem/VS/IRoslynServices.cs | 13 +- .../VS/IServiceProviderExtensions.cs | 21 +- .../VS/IUnconfiguredProjectVsServices.cs | 35 +- .../VS/IUserNotificationServices.cs | 79 +- .../ProjectSystem/VS/IVsOnlineServices.cs | 17 +- .../ProjectSystem/VS/IVsUIService`1.cs | 47 +- .../ProjectSystem/VS/IVsUIService`2.cs | 31 +- .../Commands/AbstractAddItemCommandHandler.cs | 129 +- .../AbstractGenerateNuGetPackageCommand.cs | 245 +- .../AbstractOpenProjectDesignerCommand.cs | 49 +- .../DebugFrameworksDynamicMenuCommand.cs | 177 +- .../DebugFrameworksMenuTextUpdater.cs | 161 +- .../AbstractDependencyExplorerCommand.cs | 71 +- .../BrowseToDependencyInExplorerCommand.cs | 59 +- ...ExploreDependencyFolderInWindowsCommand.cs | 49 +- .../VS/Input/Commands/DynamicMenuCommand.cs | 171 +- ...teNuGetPackageProjectContextMenuCommand.cs | 31 +- ...ateNuGetPackageTopLevelBuildMenuCommand.cs | 33 +- .../Commands/OpenProjectDesignerCommand.cs | 21 +- ...enProjectDesignerOnDefaultActionCommand.cs | 21 +- .../Ordering/AbstractAddItemCommand.cs | 93 +- .../Commands/Ordering/AbstractMoveCommand.cs | 79 +- .../Ordering/AddExistingItemAboveCommand.cs | 47 +- .../Ordering/AddExistingItemBelowCommand.cs | 47 +- .../Ordering/AddExistingItemCommand.cs | 45 +- .../Ordering/AddNewItemAboveCommand.cs | 47 +- .../Ordering/AddNewItemBelowCommand.cs | 47 +- .../Commands/Ordering/AddNewItemCommand.cs | 45 +- .../Commands/Ordering/MoveDownCommand.cs | 31 +- .../Input/Commands/Ordering/MoveUpCommand.cs | 31 +- .../VS/Input/Commands/Ordering/NodeHelper.cs | 139 +- .../Ordering/OrderAddItemHintReceiver.cs | 139 +- .../Input/Commands/Ordering/OrderingHelper.cs | 729 ++- .../Commands/Ordering/OrderingMoveAction.cs | 15 +- .../Input/Commands/Ordering/PasteOrdering.cs | 175 +- .../PackageCommandRegistrationService.cs | 51 +- .../Commands/TemplateDetailsExtensions.cs | 67 +- .../Commands/VS2kAddItemCommandHandler.cs | 39 +- .../Commands/VS97AddItemCommandHandler.cs | 29 +- .../Commands/WPFAddItemCommandHandler.cs | 63 +- .../ProjectSystem/VS/Interop/IVsAppId.cs | 431 +- .../CSharp/CSharpCodeDomProvider.cs | 31 +- .../CSharp/CSharpLanguageFeaturesProvider.cs | 213 +- ...dLineHandler.VersionedProjectChangeDiff.cs | 33 +- .../AbstractEvaluationCommandLineHandler.cs | 405 +- .../LanguageServices/ICommandLineHandler.cs | 59 +- .../IProjectEvaluationHandler.cs | 81 +- .../LanguageServices/ISourceItemsHandler.cs | 47 +- .../IVsContainedLanguageComponentsFactory.cs | 41 +- .../VS/LanguageServices/IWorkspace.cs | 33 +- .../IWorkspaceUpdateHandler.cs | 25 +- .../ProjectContextCodeModelProvider.cs | 131 +- .../VisualBasic/VisualBasicCodeDomProvider.cs | 31 +- .../VsActiveEditorContextTracker.cs | 67 +- .../VsContainedLanguageComponentsFactory.cs | 121 +- ...itiesMissingVetoProjectLoad.ProjectType.cs | 21 +- ...ojectCapabilitiesMissingVetoProjectLoad.cs | 101 +- .../ProjectSystem/VS/ProjectSystemOptions.cs | 189 +- .../VS/ProjectTreeVsExtensions.cs | 23 +- .../AbstractProjectConfigurationProperties.cs | 349 +- .../AuthenticationModeEnumProvider.cs | 75 +- ...oidPersistingProjectGuidStorageProvider.cs | 147 +- .../CSharpProjectConfigurationProperties.cs | 31 +- .../CSharp/CSharpProjectDesignerPage.cs | 29 +- .../CSharpProjectDesignerPageProvider.cs | 61 +- .../FSharp/FSharpProjectDesignerPage.cs | 27 +- .../FSharpProjectDesignerPageProvider.cs | 61 +- ...tValueProvider.AbstractBuildEventHelper.cs | 217 +- .../AbstractBuildEventValueProvider.cs | 161 +- .../ApplicationFrameworkValueProvider.cs | 567 ++- .../OutputTypeExValueProvider.cs | 61 +- .../OutputTypeValueProvider.cs | 53 +- .../OutputTypeValueProviderBase.cs | 63 +- .../PostBuildEventValueProvider.cs | 45 +- .../PreBuildEventValueProvider.cs | 45 +- .../TargetFrameworkMonikerValueProvider.cs | 79 +- .../TargetFrameworkMonikersValueProvider.cs | 53 +- .../TargetPlatformMonikersValueProvider.cs | 55 +- .../Properties/ProjectDesignerPageMetadata.cs | 35 +- .../VS/Properties/ProjectDesignerService.cs | 65 +- ...ectFileOrAssemblyInfoPropertiesProvider.cs | 63 +- .../Properties/StartupObjectsEnumProvider.cs | 163 +- .../MapDynamicEnumValuesProvider.cs | 57 +- .../VisualBasic/OptionStrictEnumProvider.cs | 51 +- ...sualBasicProjectConfigurationProperties.cs | 25 +- .../VisualBasicProjectDesignerPage.cs | 27 +- .../VisualBasicProjectDesignerPageProvider.cs | 63 +- .../VisualBasic/WarningLevelEnumProvider.cs | 31 +- .../VS/PropertyPages/BuildMacroInfo.cs | 101 +- .../CSharpImplicitUsingsControl.cs | 573 ++- .../Editors/PropertyEditorBase.cs | 177 +- .../GetProfileNameDialog.xaml.cs | 119 +- .../NoAuthRemoteAuthenticationProvider.cs | 35 +- .../VS/PropertyPages/ProfileCommandNames.cs | 15 +- .../VS/PropertyPages/PropertyPage.cs | 473 +- .../UniversalRemoteAuthenticationProvider.cs | 39 +- .../WindowsRemoteAuthenticationProvider.cs | 39 +- .../LaunchProfiles/AddLaunchProfileAction.cs | 33 +- .../DuplicateLaunchProfileAction.cs | 33 +- .../ILaunchSettingsVersionPublisher.cs | 21 +- .../IProjectLaunchProfileHandler.cs | 249 +- .../LaunchProfiles/LaunchProfileActionBase.cs | 367 +- .../LaunchProfileActionProvider.cs | 47 +- .../LaunchSettingsQueryVersionProvider.cs | 113 +- ...aunchSettingsQueryVersionProviderExport.cs | 303 +- .../LaunchProfiles/LaunchSettingsTracker.cs | 151 +- .../ProjectLaunchProfileHandler.cs | 439 +- .../RemoveLaunchProfileAction.cs | 33 +- .../RenameLaunchProfileAction.cs | 35 +- .../SetLaunchProfilePropertyAction.cs | 53 +- .../PropertyPages/AbstractProjectState.cs | 175 +- .../PropertyPages/CategoryByIdDataProducer.cs | 57 +- .../PropertyPages/CategoryDataProducer.cs | 145 +- .../PropertyPages/CategoryDataProvider.cs | 61 +- .../CategoryFromRuleDataProducer.cs | 37 +- .../ConfigurationDimensionDataProducer.cs | 65 +- .../ConfigurationDimensionDataProvider.cs | 37 +- ...rationDimensionFromPropertyDataProducer.cs | 43 +- .../ContextAndRuleProviderState.cs | 35 +- .../VS/Query/PropertyPages/DebugUtilities.cs | 29 +- .../VS/Query/PropertyPages/IProjectState.cs | 71 +- .../LaunchProfileByIdDataProducer.cs | 47 +- .../LaunchProfileCategoryDataProvider.cs | 49 +- .../LaunchProfileDataProducer.cs | 23 +- .../LaunchProfileDataProvider.cs | 57 +- .../LaunchProfileFromProjectDataProducer.cs | 27 +- .../LaunchProfileTypeDataProvider.cs | 175 +- .../LaunchProfileUIPropertyDataProvider.cs | 49 +- .../PropertyPages/ProjectActionProvider.cs | 67 +- ...rojectSetEvaluatedUIPropertyValueAction.cs | 43 +- .../ProjectSetUIPropertyValueActionBase.cs | 119 +- .../ProjectSetUIPropertyValueActionCore.cs | 199 +- ...jectSetUnevaluatedUIPropertyValueAction.cs | 43 +- .../PropertyPageByIdDataProducer.cs | 57 +- .../PropertyPages/PropertyPageDataProducer.cs | 189 +- .../PropertyPages/PropertyPageDataProvider.cs | 55 +- .../PropertyPageFromProjectDataProducer.cs | 33 +- .../PropertyPages/PropertyPageProjectState.cs | 55 +- .../PropertyPageQueryExtensions.cs | 105 +- .../PropertyPages/PropertyProviderState.cs | 35 +- .../PropertyValueProviderState.cs | 27 +- .../QueryProjectPropertiesContext.cs | 143 +- .../SupportedValueDataProducer.cs | 61 +- .../SupportedValueDataProvider.cs | 33 +- .../SupportedValueFromPropertyDataProducer.cs | 29 +- .../UIEditorMetadataDataProvider.cs | 37 +- ...UIEditorMetadataFromValueEditorProducer.cs | 37 +- .../PropertyPages/UIEditorMetadataProducer.cs | 47 +- .../PropertyPages/UIPropertyByIdProducer.cs | 59 +- .../PropertyPages/UIPropertyDataProducer.cs | 283 +- .../PropertyPages/UIPropertyDataProvider.cs | 61 +- .../UIPropertyEditorByIdDataProducer.cs | 65 +- .../UIPropertyEditorDataProducer.cs | 133 +- .../UIPropertyEditorDataProvider.cs | 55 +- ...ropertyEditorFromUIPropertyDataProducer.cs | 29 +- .../UIPropertyFromRuleDataProducer.cs | 35 +- .../UIPropertyValueDataProducer.cs | 175 +- .../UIPropertyValueDataProvider.cs | 33 +- ...PropertyValueFromUIPropertyDataProducer.cs | 43 +- .../VS/Query/QueryDataByIdProducerBase.cs | 45 +- .../QueryDataFromProviderStateProducerBase.cs | 47 +- .../VS/Query/QueryDataProviderBase.cs | 23 +- .../VisualStudioRefactorNotifyService.cs | 165 +- .../VS/References/AbstractReferenceHandler.cs | 187 +- .../VS/References/AssemblyReferenceHandler.cs | 83 +- ...imeAssemblyResolution.ResolvedReference.cs | 23 +- .../DesignTimeAssemblyResolution.cs | 271 +- .../VS/References/IReferenceCommand.cs | 21 +- .../VS/References/NullCommand.cs | 19 +- .../VS/References/PackageReferenceHandler.cs | 45 +- .../VS/References/ProjectReferenceHandler.cs | 43 +- .../VS/References/ReferenceCleanupService.cs | 189 +- .../VS/References/RemoveReferenceCommand.cs | 55 +- .../VS/References/SetAttributeCommand.cs | 15 +- .../SetTreatAsUsedAttributeCommand.cs | 77 +- .../VS/References/UnSetAttributeCommand.cs | 15 +- .../VS/Rename/FileMoveNotificationListener.cs | 295 +- ...rojectTreeActionHandler.UndoTransaction.cs | 57 +- .../Rename/RenamerProjectTreeActionHandler.cs | 387 +- .../ProjectSystem/VS/RoslynServices.cs | 61 +- .../BuildAccelerationIncompatiblePackage.cs | 11 +- .../VS/Rules/CopyToOutputDirectoryItem.cs | 11 +- .../VS/Rules/CopyUpToDateMarker.cs | 11 +- .../VS/Rules/ExportVSRuleAttribute.cs | 55 +- .../VS/Rules/ResolvedCompilationReference.cs | 11 +- .../ProjectSystem/VS/Rules/RuleExporter.cs | 111 +- .../VS/Rules/UpToDateCheckBuilt.cs | 11 +- .../VS/Rules/UpToDateCheckInput.cs | 11 +- .../VS/Rules/UpToDateCheckOutput.cs | 11 +- .../VS/SVsSettingsPersistenceManager.cs | 9 +- .../VsProjectSpecialFilesManager.cs | 47 +- .../VS/TempPE/DesignTimeInputFileChange.cs | 21 +- .../VS/TempPE/DesignTimeInputSnapshot.cs | 37 +- .../VS/TempPE/DesignTimeInputs.cs | 19 +- .../DesignTimeInputsBuildManagerBridge.cs | 197 +- .../TempPE/DesignTimeInputsChangeTracker.cs | 345 +- ...signTimeInputsCompiler.CompilationQueue.cs | 115 +- .../DesignTimeInputsCompiler.QueueItem.cs | 31 +- .../VS/TempPE/DesignTimeInputsCompiler.cs | 467 +- .../VS/TempPE/DesignTimeInputsDataSource.cs | 139 +- .../VS/TempPE/DesignTimeInputsFileWatcher.cs | 275 +- .../IDesignTimeInputsBuildManagerBridge.cs | 27 +- .../TempPE/IDesignTimeInputsChangeTracker.cs | 15 +- .../VS/TempPE/IDesignTimeInputsCompiler.cs | 25 +- .../VS/TempPE/IDesignTimeInputsDataSource.cs | 15 +- .../VS/TempPE/IDesignTimeInputsFileWatcher.cs | 15 +- .../AggregateContainedByRelationCollection.cs | 75 +- .../AggregateContainsRelationCollection.cs | 273 +- ...AggregateContainsRelationCollectionSpan.cs | 265 +- .../AggregateRelationCollectionSource.cs | 109 +- .../AttachedCollectionItemBase.cs | 169 +- ...iesAttachedCollectionSourceProviderBase.cs | 105 +- ...nciesTreeConfiguredProjectSearchContext.cs | 153 +- .../DependenciesTreeProjectSearchContext.cs | 115 +- .../DependenciesTreeSearchContext.cs | 79 +- .../DependenciesTreeSearchProvider.cs | 169 +- .../IAggregateRelationCollection.cs | 75 +- ...nciesTreeConfiguredProjectSearchContext.cs | 55 +- .../IDependenciesTreeProjectSearchContext.cs | 43 +- .../IDependenciesTreeSearchProvider.cs | 27 +- .../AttachedCollections/IRelatableItem.cs | 137 +- .../AttachedCollections/IRelation.cs | 71 +- .../AttachedCollections/IRelationProvider.cs | 33 +- .../IVsHierarchyItemExtensions.cs | 107 +- .../BrowseObjectDescriptionAttribute.cs | 39 +- .../BrowseObjectDisplayNameAttribute.cs | 39 +- ...ssemblyAttachedCollectionSourceProvider.cs | 73 +- .../FrameworkReferenceAssemblyItem.cs | 91 +- .../FrameworkReferenceIdentity.cs | 53 +- .../Implementation/FrameworkReferenceItem.cs | 33 +- .../FrameworkToFrameworkAssemblyRelation.cs | 53 +- .../ITargetFrameworkContentCache.cs | 19 +- .../TargetFrameworkContentCache.cs | 111 +- ...leItemBase.DefaultContextMenuController.cs | 105 +- .../AttachedCollections/RelatableItemBase.cs | 75 +- ...elationAttachedCollectionSourceProvider.cs | 79 +- .../AttachedCollections/RelationBase.cs | 65 +- .../AttachedCollections/RelationProvider.cs | 55 +- .../AttachedCollections/Relationships.cs | 29 +- .../DependenciesContextMenuProvider.cs | 259 +- .../Commands/HideAddReferenceCommand.cs | 41 +- .../Commands/NavigateToProjectCommand.cs | 113 +- .../ReferenceManagerCommandHandler.cs | 133 +- .../ImportTreeCommandGroupHandler.cs | 345 +- .../VS/UI/AddItemDialogService.cs | 173 +- .../VS/UI/IAddItemDialogService.cs | 139 +- .../VS/UnconfiguredProjectVsServices.cs | 103 +- .../UpToDate/BuildUpToDateCheck.CopyType.cs | 19 +- .../BuildUpToDateCheck.ItemHashing.cs | 103 +- .../VS/UpToDate/BuildUpToDateCheck.Log.cs | 491 +- .../BuildUpToDateCheck.Subscription.cs | 385 +- .../BuildUpToDateCheck.TimestampCache.cs | 59 +- .../VS/UpToDate/BuildUpToDateCheck.cs | 1813 ++++--- .../UpToDate/IBuildUpToDateCheckValidator.cs | 33 +- ...IUpToDateCheckConfiguredInputDataSource.cs | 23 +- .../VS/UpToDate/IUpToDateCheckHost.cs | 29 +- ...eCheckImplicitConfiguredInputDataSource.cs | 23 +- .../IUpToDateCheckStatePersistence.cs | 99 +- .../UpToDateCheckBuildEventNotifier.cs | 269 +- .../UpToDate/UpToDateCheckConfiguredInput.cs | 37 +- .../UpToDateCheckConfiguredInputDataSource.cs | 79 +- .../VS/UpToDate/UpToDateCheckHost.cs | 81 +- .../UpToDateCheckImplicitConfiguredInput.cs | 1339 +++-- ...eCheckImplicitConfiguredInputDataSource.cs | 237 +- ...tePersistence.ConfiguredProjectComparer.cs | 67 +- .../UpToDate/UpToDateCheckStatePersistence.cs | 451 +- .../VS/UserNotificationServices.cs | 77 +- .../VS/Utilities/EnumerableExtensions.cs | 29 +- .../VS/Utilities/FocusAttacher.cs | 35 +- .../VS/Utilities/KnownEventArgs.cs | 13 +- .../VS/Utilities/UIThreadHelper.cs | 41 +- .../ProjectSystem/VS/Utilities/WpfHelper.cs | 87 +- ...VisualBasicProjectCompatibilityProvider.cs | 45 +- .../VisualBasicProjectTypeGuidProvider.cs | 29 +- .../ProjectSystem/VS/VsOnlineServices.cs | 11 +- .../VS/VsSafeProjectGuidService.cs | 41 +- .../VS/VsSolutionEventListener.cs | 447 +- .../ProjectSystem/VS/VsUIService`1.cs | 61 +- .../ProjectSystem/VS/VsUIService`2.cs | 33 +- .../VS/Waiting/VisualStudioWaitContext.cs | 187 +- .../VS/Waiting/VisualStudioWaitIndicator.cs | 93 +- .../WindowsForms/WindowsFormsAddItemFilter.cs | 107 +- .../WindowsFormsEditorProvider.EditorInfo.cs | 31 +- ...wsFormsEditorProvider.SubTypeDescriptor.cs | 25 +- .../WindowsFormsEditorProvider.cs | 223 +- .../VS/Xproj/XprojProjectFactory.cs | 109 +- .../Shell/HierarchyId.cs | 161 +- .../Shell/IVsShellServices.cs | 45 +- .../Interop/IVsOutputWindowExtensions.cs | 75 +- .../Interop/IVsOutputWindowPaneExtensions.cs | 39 +- .../Shell/Interop/VsHierarchyExtensions.cs | 155 +- .../Shell/Interop/VsProjectExtensions.cs | 83 +- .../Shell/Interop/VsShellExtensions.cs | 27 +- ...viceProviderToOleServiceProviderAdapter.cs | 75 +- .../Shell/SolutionExplorerWindow.cs | 131 +- .../JoinableTaskContextExtensions.cs | 25 +- .../Converters/Converters.LambdaConverter.cs | 91 +- .../Converters.LambdaMultiConverter.cs | 229 +- .../WPF/Converters/Converters.cs | 119 +- .../Annotations.cs | 257 +- .../Build/BuildExtensions.cs | 25 +- .../Build/BuildUtilities.cs | 355 +- .../Collections/ConcurrentHashSet.cs | 93 +- .../Collections/DictionaryEqualityComparer.cs | 137 +- .../Collections/EnumerableExtensions.cs | 131 +- .../Collections/IConcurrentHashSet.cs | 93 +- .../Collections/ImmutableStringDictionary.cs | 15 +- .../Collections/ImmutableStringHashSet.cs | 17 +- .../ComparableExtensions.cs | 141 +- ...derPrecedenceImportCollectionExtensions.cs | 153 +- .../Delimiter.cs | 47 +- .../IO/IFileExplorer.cs | 25 +- .../IO/IFileSystem.cs | 97 +- .../IO/SimpleFileWatcher.cs | 99 +- .../IO/Win32FileSystem.cs | 217 +- .../ImmutableCollectionLinqExtensions.cs | 111 +- .../Interop/Win32Interop.cs | 31 +- .../LinqExtensions.cs | 149 +- .../PooledObjects/InternPool.cs | 75 +- .../PooledObjects/ObjectPool.cs | 217 +- .../PooledArray.DebuggerProxy.cs | 37 +- .../PooledObjects/PooledArray.Enumerator.cs | 51 +- .../PooledObjects/PooledArray.cs | 629 ++- .../PooledObjects/PooledDictionary.cs | 83 +- .../PooledObjects/PooledHashSet.cs | 57 +- .../PooledObjects/PooledStringBuilder.cs | 229 +- .../AbstractActiveConfiguredValue.cs | 95 +- .../AbstractMultiLifetimeComponent.cs | 187 +- .../ProjectSystem/ActiveConfiguredObjects.cs | 109 +- .../ActiveConfiguredProjectsLoader.cs | 103 +- .../ActiveConfiguredProjectsProvider.cs | 271 +- .../ProjectSystem/ActiveConfiguredValue.cs | 31 +- .../ProjectSystem/ActiveConfiguredValues.cs | 65 +- .../ProjectSystem/Build/BuildProperty.cs | 43 +- ...ndLineDesignTimeBuildPropertiesProvider.cs | 41 +- ...eOnBuildDesignTimeBuildPropertyProvider.cs | 49 +- .../GeneratePackageOnBuildPropertyProvider.cs | 73 +- .../IImplicitlyTriggeredBuildManager2.cs | 35 +- .../Build/IImplicitlyTriggeredBuildState.cs | 49 +- ...itlyActiveConfiguredProjectReadyToBuild.cs | 103 +- .../Build/ImplicitlyTriggeredBuildManager.cs | 57 +- .../ProjectSystem/Build/OutputGroup.cs | 55 +- .../Build/PublishItemsOutputGroupProvider.cs | 85 +- .../Build/PublishableProjectConfigProvider.cs | 39 +- .../SkipAnalyzersGlobalPropertiesProvider.cs | 99 +- ...SingleTargetGlobalBuildPropertyProvider.cs | 133 +- ...getFrameworkGlobalBuildPropertyProvider.cs | 75 +- ...pilerIfAvailableBuildPropertiesProvider.cs | 37 +- ...seProjectConfigurationDimensionProvider.cs | 413 +- ...onProjectConfigurationDimensionProvider.cs | 35 +- .../Configuration/DimensionProviderOrder.cs | 23 +- ...tiveConfiguredProjectsDimensionProvider.cs | 21 +- ...urationDimensionDescriptionMetadataView.cs | 33 +- .../IImplicitlyActiveDimensionProvider.cs | 33 +- ...ConfigurationDimensionsProviderInternal.cs | 9 +- .../ImplicitlyActiveDimensionProvider.cs | 71 +- ...rmProjectConfigurationDimensionProvider.cs | 35 +- ...rkProjectConfigurationDimensionProvider.cs | 35 +- ...guredProjectActivationTracking.Instance.cs | 175 +- .../ConfiguredProjectActivationTracking.cs | 89 +- ...jectImplicitActivationTracking.Instance.cs | 193 +- ...iguredProjectImplicitActivationTracking.cs | 87 +- .../ProjectSystem/ContractNames.cs | 97 +- .../CopyPaste/ClipboardFormat.cs | 23 +- .../CopyPaste/DependencyTextPackager.cs | 109 +- .../ProjectSystem/DataflowBlockFactory.cs | 63 +- .../ProjectSystem/DataflowOption.cs | 115 +- .../ProjectSystem/DataflowUtilities.cs | 1139 +++-- .../Debug/ActiveDebugFrameworkServices.cs | 151 +- .../Debug/DebugProfileDebugTargetGenerator.cs | 89 +- .../Debug/DebugProfileEnumValuesGenerator.cs | 105 +- .../ProjectSystem/Debug/DebugTokenReplacer.cs | 79 +- .../Debug/DefaultLaunchProfileProvider.cs | 35 +- .../Debug/IActiveDebugFrameworkServices.cs | 45 +- .../Debug/IDebugTokenReplacer.cs | 57 +- .../Debug/IDefaultLaunchProfileProvider.cs | 19 +- .../ProjectSystem/Debug/IJsonSection.cs | 21 +- .../ProjectSystem/Debug/ILaunchProfile.cs | 31 +- .../ProjectSystem/Debug/ILaunchProfile2.cs | 19 +- .../ProjectSystem/Debug/ILaunchSettings.cs | 41 +- .../Debug/ILaunchSettingsProvider.cs | 131 +- .../Debug/ILaunchSettingsProvider2.cs | 27 +- .../Debug/ILaunchSettingsProvider3.cs | 83 +- .../ILaunchSettingsSerializationProvider.cs | 21 +- .../Debug/ILaunchSettingsUIProvider.cs | 91 +- .../ProjectSystem/Debug/IPersistOption.cs | 15 +- .../Debug/IRemoteAuthenticationProvider.cs | 49 +- .../IRemoteDebuggerAuthenticationService.cs | 15 +- .../Debug/IStartupProjectHelper.cs | 27 +- .../Debug/IVersionedLaunchSettings.cs | 15 +- .../Debug/IVersionedLaunchSettingsProvider.cs | 23 +- .../Debug/IWritableLaunchProfile.cs | 37 +- .../Debug/IWritableLaunchSettings.cs | 27 +- .../Debug/IWritablePersistOption.cs | 15 +- .../ProjectSystem/Debug/LaunchProfile.cs | 293 +- ...aunchProfileEnvironmentVariableEncoding.cs | 49 +- .../Debug/LaunchProfileExtensions.cs | 441 +- .../ProjectSystem/Debug/LaunchSettings.cs | 169 +- .../Debug/LaunchSettingsProvider.cs | 1477 +++--- .../Debug/LaunchSettingsProviderExtensions.cs | 43 +- .../Debug/UIProfilePropertyName.cs | 25 +- .../Debug/WritableLaunchProfile.cs | 115 +- .../Debug/WritableLaunchSettings.cs | 51 +- .../Debug/WritableLaunchSettingsExtensions.cs | 71 +- .../Extensibility/IProjectExportProvider.cs | 27 +- .../Extensibility/ProjectExportProvider.cs | 43 +- .../ProjectSystem/FaultExtensions.cs | 455 +- .../ProjectSystem/FileItemServices.cs | 75 +- .../IActiveConfigurationComponent.cs | 33 +- .../IActiveConfiguredProjectsProvider.cs | 99 +- .../ProjectSystem/IActiveConfiguredValue.cs | 45 +- .../ProjectSystem/IActiveConfiguredValues.cs | 23 +- .../ICreateFileFromTemplateService.cs | 25 +- ...IImplicitlyActiveConfigurationComponent.cs | 33 +- .../ProjectSystem/ILoadedInHostListener.cs | 25 +- .../ProjectSystem/IMultiLifetimeInstance.cs | 27 +- .../ProjectSystem/IPhysicalProjectTree.cs | 43 +- .../IPhysicalProjectTreeStorage.cs | 263 +- .../ProjectSystem/IProjectAccessor.cs | 369 +- .../IProjectCapabilitiesService.cs | 37 +- .../IProjectItemProviderExtensions.cs | 35 +- .../ProjectSystem/IProjectSystemOptions.cs | 117 +- .../ProjectSystem/IProjectTreeExtensions.cs | 119 +- .../ProjectSystem/ISafeProjectGuidService.cs | 61 +- .../ProjectSystem/ISolutionService.cs | 65 +- .../IUnconfiguredProjectCommonServices.cs | 51 +- .../AppDesignerFolderProjectImageProvider.cs | 45 +- .../CSharp/CSharpProjectImageProvider.cs | 41 +- .../FSharp/FSharpProjectImageProvider.cs | 35 +- .../FSharp/FSharpSourcesIconProvider.cs | 53 +- .../Imaging/IProjectImageProvider.cs | 21 +- .../Imaging/ImageMonikerDebuggerDisplay.cs | 53 +- .../ProjectSystem/Imaging/ProjectImageKey.cs | 49 +- .../Imaging/ProjectImageProviderAggregator.cs | 53 +- .../VisualBasicProjectImageProvider.cs | 41 +- .../Input/AbstractProjectCommand.cs | 75 +- .../Input/AbstractSingleNodeProjectCommand.cs | 43 +- .../Input/GetCommandStatusResult.cs | 17 +- .../Input/ProjectCommandAttribute.cs | 39 +- .../ActiveEditorContextTracker.cs | 89 +- .../LanguageServices/BuildOptions.cs | 73 +- .../CSharp/CSharpCommandLineParserService.cs | 21 +- .../CSharp/CSharpSyntaxFactsService.cs | 25 +- .../LanguageServices/ContextState.cs | 55 +- .../FSharp/FSharpBuildOptions.cs | 25 +- .../FSharp/FSharpCommandLineParserService.cs | 145 +- .../IActiveEditorContextTracker.cs | 97 +- .../ICommandLineParserService.cs | 59 +- .../LanguageServices/ISyntaxFactsService.cs | 17 +- .../VisualBasicCommandLineParserService.cs | 21 +- .../VisualBasicSyntaxFactsService.cs | 25 +- .../ProjectSystem/LogLevel.cs | 15 +- .../ProjectSystem/Logging/BatchLogger.cs | 117 +- ...rojectDiagnosticOutputServiceExtensions.cs | 189 +- ...ceInitializedOnceDisposedUnderLockAsync.cs | 281 +- .../DataProgressTrackerServiceExtensions.cs | 25 +- .../OperationProgressStageId.cs | 15 +- .../ProgressTrackerOutputDataSource.cs | 33 +- .../ProjectSystem/Order.cs | 55 +- ...PackageRestoreConfiguredInputDataSource.cs | 19 +- .../IPackageRestoreDataSource.cs | 15 +- ...ckageRestoreUnconfiguredInputDataSource.cs | 19 +- ...PackageRestoreConfiguredInputDataSource.cs | 107 +- .../PackageRestoreDataSource.cs | 439 +- ...r.PackageRestoreProgressTrackerInstance.cs | 201 +- .../PackageRestoreProgressTracker.cs | 97 +- ...kageRestoreSharedJoinableTaskCollection.cs | 27 +- ...ckageRestoreUnconfiguredInputDataSource.cs | 345 +- .../PackageRestoreConfiguredInput.cs | 47 +- .../PackageRestoreUnconfiguredInput.cs | 41 +- .../Snapshots/ProjectProperty.cs | 29 +- .../Snapshots/ProjectRestoreInfo.cs | 41 +- .../PackageRestore/Snapshots/ReferenceItem.cs | 33 +- .../Snapshots/ReferenceProperty.cs | 29 +- .../Snapshots/RestoreBuilder.cs | 91 +- ...eComparer.ReferenceItemEqualityComparer.cs | 61 +- .../Snapshots/RestoreComparer.cs | 9 +- .../PackageRestore/Snapshots/RestoreData.cs | 49 +- .../PackageRestore/Snapshots/RestoreHasher.cs | 89 +- .../PackageRestore/Snapshots/RestoreLogger.cs | 139 +- .../Snapshots/TargetFrameworkInfo.cs | 91 +- .../ProjectSystem/PhysicalProjectTree.cs | 63 +- ...calProjectTreeStorage.ConfiguredImports.cs | 25 +- .../PhysicalProjectTreeStorage.cs | 97 +- .../ProjectSystem/ProjectAccessor.cs | 195 +- .../ProjectCapabilitiesService.cs | 39 +- .../ProjectSystem/ProjectCapability.cs | 101 +- .../ProjectSystem/ProjectChangeDiff.cs | 67 +- .../ProjectSystem/ProjectCheckoutOption.cs | 23 +- .../ProjectConfigurationExtensions.cs | 145 +- .../ProjectSystem/ProjectFileClassifier.cs | 295 +- .../ProjectTreeFlagsExtensions.cs | 81 +- .../ProjectTreeProviderExtensions.cs | 69 +- ...ectFileOrAssemblyInfoPropertiesProvider.cs | 77 +- .../AssemblyInfoProperties.cs | 195 +- ...eAssemblyAttributePropertyValueProvider.cs | 189 +- .../Properties/BasePropertyExtensions.cs | 77 +- .../DelegatedProjectPropertiesBase.cs | 65 +- .../DelegatedProjectPropertiesProviderBase.cs | 139 +- .../Properties/IProjectDesignerService.cs | 39 +- .../Properties/ITemporaryPropertyStorage.cs | 27 +- .../ApplicationManifestValueProvider.cs | 141 +- .../ApplicationIconValueProvider.cs | 135 +- .../ApplicationManifestKindValueProvider.cs | 177 +- .../ApplicationManifestPathValueProvider.cs | 91 +- .../CompoundTargetFrameworkValueProvider.cs | 515 +- .../InstallOtherFrameworksValueProvider.cs | 9 +- .../ResourceSpecificationKindValueProvider.cs | 143 +- .../TargetMultipleFrameworksValueProvider.cs | 95 +- .../DebugTypeValueProvider.cs | 33 +- .../DefaultPlatformPropertyValueProvider.cs | 61 +- .../GenerateDocumentationFileValueProvider.cs | 63 +- .../RunPostBuildEventValueProvider.cs | 47 +- .../TreatWarningsAsErrorsValueProvider.cs | 61 +- ...PagePlaceholderDescriptionValueProvider.cs | 9 +- .../LaunchSettingsValueProviderBase.cs | 147 +- .../OpenLaunchProfilesEditorValueProvider.cs | 9 +- ...erceptingPropertyValueProviderAttribute.cs | 107 +- .../IInterceptingPropertyValueProvider.cs | 37 +- .../IInterceptingPropertyValueProvider2.cs | 19 +- ...terceptingPropertyValueProviderMetadata.cs | 23 +- ...erceptingPropertyValueProviderMetadata2.cs | 19 +- .../InterceptedProjectProperties.cs | 127 +- ...nterceptedProjectPropertiesProviderBase.cs | 51 +- .../InterceptedPropertiesProviderBase.cs | 113 +- .../InterceptingPropertyValueProviderBase.cs | 53 +- .../NoOpInterceptingPropertyValueProvider.cs | 27 +- .../AssemblyVersionValueProvider.cs | 33 +- .../BaseVersionValueProvider.cs | 87 +- .../FileVersionValueProvider.cs | 33 +- .../NeutralLanguageValueProvider.cs | 41 +- .../PackageFilePropertyValueProviderBase.cs | 217 +- .../PackageIconValueProvider.cs | 59 +- .../PackageLicenseFileValueProvider.cs | 59 +- .../PackageLicenseKindValueProvider.cs | 131 +- .../PackageReadmeFileValueProvider.cs | 59 +- .../ReadAboutSpdxExpressionsValueProvider.cs | 9 +- ...ileInterceptedProjectPropertiesProvider.cs | 33 +- ...tedViaSnapshotProjectPropertiesProvider.cs | 55 +- .../ImplicitUsingsEnabledValueProvider.cs | 79 +- .../OpenLaunchProfilesEditorValueProvider.cs | 9 +- ...PagePlaceholderDescriptionValueProvider.cs | 9 +- .../OpenLaunchProfilesEditorValueProvider.cs | 9 +- ...PagePlaceholderDescriptionValueProvider.cs | 9 +- .../AssemblyOriginatorKeyFileValueProvider.cs | 41 +- .../TargetFrameworkValueProvider.cs | 51 +- ...ileInterceptedProjectPropertiesProvider.cs | 49 +- ...ltsInterceptedProjectPropertiesProvider.cs | 37 +- ...hProfileExtensionValueProviderAttribute.cs | 63 +- ...aunchProfileExtensionValueProviderScope.cs | 33 +- .../IGlobalSettingExtensionValueProvider.cs | 145 +- .../ILaunchProfileExtensionValueProvider.cs | 149 +- ...chProfileExtensionValueProviderMetadata.cs | 23 +- .../LaunchProfileProjectItemProvider.cs | 497 +- .../LaunchProfileProjectProperties.cs | 551 +- .../LaunchProfileProjectPropertiesProvider.cs | 159 +- ...jectLaunchProfileExtensionValueProvider.cs | 191 +- .../LaunchTargetPropertyPageEnumProvider.cs | 93 +- .../Properties/MetadataExtensions.cs | 197 +- .../Properties/NeutralLanguageEnumProvider.cs | 47 +- ...formTargetBuildPropertyPageEnumProvider.cs | 71 +- .../Properties/ProjectPropertiesExtensions.cs | 47 +- ...shotExtensions.EmptyProjectRuleSnapshot.cs | 33 +- .../ProjectRuleSnapshotExtensions.cs | 219 +- .../Properties/PropertyExtensions.cs | 39 +- .../SingleRuleSupportedValuesProvider.cs | 59 +- .../Properties/SupportedValuesProvider.cs | 193 +- .../Properties/TemporaryPropertyStorage.cs | 49 +- .../Refactor/IRefactorNotifyService.cs | 11 +- ...AlwaysAllowValidProjectReferenceChecker.cs | 81 +- .../ProjectSystem/Rules/AppDesigner.xaml.cs | 13 +- .../Rules/CollectedFrameworkReference.cs | 11 +- .../Rules/CollectedNuGetAuditSuppressions.cs | 11 +- .../Rules/CollectedPackageDownload.cs | 11 +- .../Rules/CollectedPackageReference.cs | 11 +- .../Rules/CollectedPackageVersion.cs | 11 +- .../Rules/CollectedPrunePackageReference.cs | 11 +- .../Rules/CompilerCommandLineArgs.cs | 11 +- .../Rules/ConfigurationGeneral.xaml.cs | 11 +- .../Rules/ConfiguredBrowseObject.cs | 11 +- .../Dependencies/AnalyzerReference.xaml.cs | 11 +- .../Rules/Dependencies/AssemblyReference.cs | 11 +- .../Rules/Dependencies/COMReference.cs | 11 +- .../Rules/Dependencies/FrameworkReference.cs | 11 +- .../Rules/Dependencies/PackageReference.cs | 11 +- .../Rules/Dependencies/ProjectReference.cs | 11 +- .../ResolvedAnalyzerReference.xaml.cs | 11 +- .../Dependencies/ResolvedAssemblyReference.cs | 11 +- .../Dependencies/ResolvedCOMReference.cs | 11 +- .../ResolvedFrameworkReference.cs | 11 +- .../Dependencies/ResolvedPackageReference.cs | 11 +- .../Dependencies/ResolvedProjectReference.cs | 11 +- .../Dependencies/ResolvedSdkReference.cs | 11 +- .../Rules/Dependencies/SdkReference.xaml.cs | 11 +- .../Rules/DotNetCliToolReference.cs | 11 +- .../Rules/EvaluatedProjectReference.cs | 11 +- .../Rules/ExportRuleAttribute.cs | 55 +- .../Rules/GeneralBrowseObject.cs | 11 +- .../Rules/Items/AdditionalFiles.cs | 11 +- .../ProjectSystem/Rules/Items/Compile.cs | 11 +- .../ProjectSystem/Rules/Items/Content.cs | 11 +- .../ProjectSystem/Rules/Items/Folder.cs | 11 +- .../ProjectSystem/Rules/Items/None.cs | 11 +- .../ProjectSystem/Rules/Items/Resource.cs | 11 +- .../ProjectSystem/Rules/LanguageService.cs | 17 +- .../ProjectSystem/Rules/NuGetRestore.cs | 11 +- .../Rules/ProjectDebugger.xaml.cs | 11 +- .../ProjectSystem/Rules/ProjectProperties.cs | 77 +- .../ProjectSystem/Rules/RuleExporter.cs | 437 +- .../SdkSupportedTargetPlatformIdentifier.cs | 9 +- .../SdkSupportedTargetPlatformVersion.cs | 9 +- .../SupportedNETCoreAppTargetFramework.cs | 11 +- .../SupportedNETFrameworkTargetFramework.cs | 11 +- .../SupportedNETStandardTargetFramework.cs | 11 +- .../Rules/SupportedTargetFramework.cs | 11 +- .../Rules/SupportedTargetFrameworkAlias.cs | 11 +- .../Rules/WindowsFormsConfiguration.cs | 11 +- .../ProjectSystem/ServiceCapability.cs | 17 +- .../AbstractAppXamlSpecialFileProvider.cs | 55 +- .../AbstractFindByNameSpecialFileProvider.cs | 59 +- ...NameUnderAppDesignerSpecialFileProvider.cs | 123 +- .../AbstractSpecialFileProvider.cs | 151 +- .../AppDesignerFolderSpecialFileProvider.cs | 105 +- .../AppManifestSpecialFileProvider.cs | 107 +- .../AppSettingsSpecialFileProvider.cs | 47 +- .../AssemblyResourcesSpecialFileProvider.cs | 47 +- .../CSharpAppConfigSpecialFileProvider.cs | 37 +- .../CSharpAppXamlSpecialFileProvider.cs | 39 +- .../CSharpAssemblyInfoSpecialFileProvider.cs | 47 +- .../IAppDesignerFolderSpecialFileProvider.cs | 7 +- .../ISpecialFilesManager.cs | 41 +- .../LicensesSpecialFileProvider.cs | 27 +- ...VisualBasicAppConfigSpecialFileProvider.cs | 37 +- .../VisualBasicAppXamlSpecialFileProvider.cs | 39 +- ...ualBasicAssemblyInfoSpecialFileProvider.cs | 47 +- .../TargetFrameworkIdentifiers.cs | 23 +- ...cialFolderProjectTreePropertiesProvider.cs | 157 +- ...gnerFolderProjectTreePropertiesProvider.cs | 135 +- .../Tree/AppDesignerFolderRenameHandler.cs | 45 +- .../Legacy/IDependenciesChanges.cs | 49 +- .../Dependencies/Legacy/IDependencyModel.cs | 273 +- .../IProjectDependenciesSubTreeProvider.cs | 139 +- .../Snapshot/DependenciesSnapshot.cs | 251 +- .../PackageDependencyFactory.cs | 147 +- .../Subscriptions/ProjectItemMetadata.cs | 29 +- .../ProjectSystem/Tree/FileIconProvider.cs | 51 +- .../ProjectSystem/Tree/IFileIconProvider.cs | 25 +- ...ageContentProjectTreePropertiesProvider.cs | 31 +- .../Tree/ProjectImports/ImportTreeProvider.cs | 555 ++- ...tRootImageProjectTreePropertiesProvider.cs | 75 +- .../UnconfiguredProjectCommonServices.cs | 95 +- .../UnconfiguredProjectTasksService.cs | 175 +- .../Utilities/AsyncDisposable.cs | 21 +- .../ProjectSystem/Utilities/DisposableBag.cs | 101 +- .../Utilities/DisposableDelegate.cs | 37 +- .../Utilities/EmptyDisposable.cs | 11 +- .../Utilities/EnvironmentHelper.cs | 39 +- .../Utilities/IEnvironmentHelper.cs | 19 +- .../ProjectSystem/Utilities/TraceUtilities.cs | 225 +- .../ProjectSystem/Waiting/IWaitContext.cs | 45 +- .../ProjectSystem/Waiting/IWaitIndicator.cs | 13 +- .../Waiting/WaitIndicatorResult.cs | 97 +- .../StringComparers.cs | 141 +- .../StringExtensions.cs | 17 +- .../Telemetry/ComplexPropertyValue.cs | 23 +- .../Telemetry/TelemetryEventName.cs | 125 +- .../Telemetry/TelemetryPropertyName.cs | 535 +- .../Text/IncrementalHasher.cs | 123 +- .../Text/LazyStringSplit.cs | 185 +- ...maphoreSlimExtensions.SemaphoreDisposer.cs | 25 +- .../Threading/SemaphoreSlimExtensions.cs | 103 +- .../Threading/Tasks/CancellationSeries.cs | 183 +- .../Threading/Tasks/ITaskDelayScheduler.cs | 47 +- .../Threading/Tasks/SequentialTaskExecutor.cs | 173 +- .../Threading/Tasks/TaskCompletionSource.cs | 95 +- .../Threading/Tasks/TaskDelayScheduler.cs | 95 +- .../Threading/Tasks/TaskExtensions.cs | 73 +- .../Threading/Tasks/TaskResult.cs | 93 +- .../Utilities/DeconstructionExtensions.cs | 15 +- .../Utilities/SetDiff.cs | 119 +- .../Utilities/Strings.cs | 17 +- tests/Common/ThrowingTraceListener.cs | 75 +- .../Construction/GlobalJson.cs | 27 +- .../Construction/Project.cs | 243 +- .../Construction/Solution.cs | 121 +- .../DependencyNodeIntegrationTests.cs | 305 +- .../Diagnostics/WarningIgnorerLogger.cs | 87 +- .../Environment/AssemblyLifetime.cs | 25 +- .../ProjectSystemHostConfiguration.cs | 45 +- .../ProjectSystemOperationsConfiguration.cs | 55 +- .../Environment/TestEnvironment.cs | 55 +- .../IntegrationTestBase.cs | 43 +- .../MakeOmniLogTestSpecificLifetimeAction.cs | 23 +- ...VisualStudioAfterLastTestLifetimeAction.cs | 41 +- .../OpenProjectTests.cs | 95 +- .../ProjectFileEditorTests.cs | 389 +- .../ProjectLayoutTestBase.cs | 439 +- .../Utilities/AssertExtensions.cs | 55 +- .../ProjectSystem/ProjectTreeParserTests.cs | 847 ++-- .../AssertEx.cs | 63 +- .../Build/MSBuildAssert.cs | 57 +- .../Build/ProjectInstanceFactory.cs | 13 +- .../Build/ProjectRootElementFactory.cs | 17 +- .../FuncWithOut.cs | 15 +- .../LazyExtensions.cs | 11 +- .../Moq/AbstractMock.cs | 15 +- .../Moq/ReturnsExtensions.cs | 61 +- ...recedenceImportCollectionTestExtensions.cs | 19 +- .../ProjectTreeParser/Delimiters.cs | 21 +- .../ProjectTreeFormatError.cs | 31 +- .../ProjectTreeFormatException.cs | 17 +- ...rojectTreeParser.MutableProjectItemTree.cs | 175 +- ...eParser.MutableProjectPropertiesContext.cs | 17 +- ...eeParser.MutableProjectTree.SubTypeRule.cs | 81 +- .../ProjectTreeParser.MutableProjectTree.cs | 321 +- .../ProjectTreeParser/ProjectTreeParser.cs | 563 ++- .../ProjectTreeParser/SimpleStringReader.cs | 93 +- .../Tokenizer.IdentifierParseOptions.cs | 13 +- .../ProjectTreeParser/Tokenizer.Token.cs | 79 +- .../ProjectTreeParser/Tokenizer.TokenType.cs | 31 +- .../ProjectTreeParser/Tokenizer.cs | 241 +- ...ProjectTreePropertiesProviderExtensions.cs | 165 +- .../ProjectSystem/ProjectTreeProvider.cs | 139 +- .../ProjectSystem/ProjectTreeWriter.cs | 319 +- .../ProjectSystem/ProjectTreeWriterOptions.cs | 27 +- .../Threading/Tasks/TaskTimeoutExtensions.cs | 43 +- .../Workspaces/WorkspaceFactory.cs | 31 +- .../Build/BuildUtilitiesTests.cs | 581 ++- .../IO/Win32FileSystemTests.cs | 41 +- .../AbstractMultiLifetimeComponentFactory.cs | 73 +- .../Mocks/ConfiguredProjectFactory.cs | 51 +- .../Mocks/ConfiguredProjectServicesFactory.cs | 51 +- .../Mocks/ExportFactoryFactory.cs | 59 +- ...IActiveConfigurationGroupServiceFactory.cs | 17 +- .../Mocks/IActiveConfiguredProjectFactory.cs | 19 +- ...IActiveConfiguredProjectProviderFactory.cs | 65 +- ...figuredProjectsDimensionProviderFactory.cs | 17 +- ...ActiveConfiguredProjectsProviderFactory.cs | 65 +- .../Mocks/IActiveConfiguredValuesFactory.cs | 19 +- .../IActiveDebugFrameworkServicesFactory.cs | 27 +- .../IActiveDebugFrameworkServicesMock.cs | 75 +- .../IActiveEditorContextTrackerFactory.cs | 41 +- ...esignerFolderSpecialFileProviderFactory.cs | 17 +- .../Mocks/ICommandLineParserServiceFactory.cs | 31 +- ...DimensionDescriptionMetadataViewFactory.cs | 21 +- .../Mocks/IConfigurationGroupFactory.cs | 33 +- .../ICreateFileFromTemplateServiceFactory.cs | 23 +- .../IDataProgressTrackerServiceFactory.cs | 33 +- ...ogressTrackerServiceRegistrationFactory.cs | 25 +- .../Mocks/IEnumValueFactory.cs | 29 +- .../Mocks/IEnvironmentHelperFactory.cs | 17 +- .../Mocks/IFileSystemFactory.cs | 81 +- .../Mocks/IFileSystemMock.cs | 305 +- .../Mocks/IFolderManagerFactory.cs | 39 +- ...balSettingExtensionValueProviderFactory.cs | 37 +- ...mplicitlyActiveDimensionProviderFactory.cs | 25 +- ...IImplicitlyTriggeredBuildManagerFactory.cs | 49 +- .../IImplicityTriggeredBuildStateFactory.cs | 21 +- ...nterceptingPropertyValueProviderFactory.cs | 89 +- ...ingPropertyValueProviderMetadataFactory.cs | 17 +- .../Mocks/IJsonModel.cs | 29 +- ...nchProfileExtensionValueProviderFactory.cs | 37 +- ...leExtensionValueProviderMetadataFactory.cs | 29 +- .../Mocks/ILaunchSettingsProviderFactory.cs | 283 +- .../Mocks/ILoadedInHostListenerFactory.cs | 25 +- .../IOrderPrecedenceMetadataViewFactory.cs | 21 +- .../Mocks/IPackageRestoreServiceFactory.cs | 17 +- ...storeUnconfiguredInputDataSourceFactory.cs | 47 +- .../Mocks/IPhysicalProjectTreeFactory.cs | 37 +- .../IPhysicalProjectTreeStorageFactory.cs | 83 +- .../Mocks/IProjectAccessorFactory.cs | 107 +- ...IProjectAsynchronousTasksServiceFactory.cs | 25 +- .../IProjectCapabilitiesServiceFactory.cs | 25 +- .../Mocks/IProjectCatalogSnapshotFactory.cs | 41 +- .../Mocks/IProjectChangeDescriptionFactory.cs | 105 +- .../Mocks/IProjectChangeDiffFactory.cs | 123 +- .../Mocks/IProjectCommonServicesFactory.cs | 31 +- .../IProjectConfigurationsServiceFactory.cs | 17 +- ...ojectDependenciesSubTreeProviderFactory.cs | 35 +- .../Mocks/IProjectDesignerServiceFactory.cs | 39 +- .../IProjectFaultHandlerServiceFactory.cs | 39 +- .../Mocks/IProjectImageProviderFactory.cs | 33 +- ...rojectInstancePropertiesProviderFactory.cs | 37 +- .../Mocks/IProjectItemFactory.cs | 35 +- .../Mocks/IProjectItemProviderFactory.cs | 93 +- .../Mocks/IProjectItemSchemaFactory.cs | 21 +- .../Mocks/IProjectItemSchemaServiceFactory.cs | 17 +- .../Mocks/IProjectLockServiceFactory.cs | 11 +- .../Mocks/IProjectLoggerFactory.cs | 11 +- .../Mocks/IProjectPropertiesContextFactory.cs | 17 +- .../Mocks/IProjectPropertiesFactory.cs | 125 +- .../IProjectPropertiesProviderFactory.cs | 31 +- .../Mocks/IProjectRuleSnapshotFactory.cs | 109 +- .../Mocks/IProjectRuleSnapshotsFactory.cs | 27 +- .../Mocks/IProjectServiceAccessorFactory.cs | 31 +- .../Mocks/IProjectServiceFactory.cs | 49 +- .../Mocks/IProjectSnapshot2Factory.cs | 35 +- .../IProjectSubscriptionServiceFactory.cs | 35 +- .../IProjectSubscriptionUpdateFactory.cs | 153 +- .../Mocks/IProjectSystemOptionsFactory.cs | 83 +- ...gServiceFactory.ProjectThreadingService.cs | 81 +- .../Mocks/IProjectThreadingServiceFactory.cs | 11 +- ...tTreeCustomizablePropertyContextFactory.cs | 41 +- ...ctTreeCustomizablePropertyValuesFactory.cs | 11 +- .../Mocks/IProjectTreeProviderFactory.cs | 91 +- .../Mocks/IProjectTreeServiceFactory.cs | 53 +- .../Mocks/IProjectTreeServiceStateFactory.cs | 47 +- .../Mocks/IProjectVersionedValueFactory.cs | 87 +- .../Mocks/IPropertyFactory.cs | 81 +- .../Mocks/IPropertyPagesCatalogFactory.cs | 99 +- .../IPropertyPagesCatalogProviderFactory.cs | 37 +- .../IRemoteAuthenticationProviderFactory.cs | 17 +- .../Mocks/IRuleFactory.cs | 87 +- .../Mocks/ISafeProjectGuidServiceFactory.cs | 17 +- .../Mocks/ISolutionServiceFactory.cs | 25 +- .../Mocks/ISpecialFilesManagerFactory.cs | 25 +- .../Mocks/ITelemetryServiceFactory.cs | 131 +- .../Mocks/ITemporaryPropertyStorageFactory.cs | 23 +- ...nconfiguredProjectCommonServicesFactory.cs | 59 +- .../IUnconfiguredProjectServicesFactory.cs | 53 +- ...IUnconfiguredProjectTasksServiceFactory.cs | 95 +- .../Mocks/ImmutableOrderedDictionary.cs | 69 +- .../Mocks/ItemType.cs | 21 +- .../Mocks/JoinableTaskContextNodeFactory.cs | 13 +- .../Mocks/MetadataFactory.cs | 29 +- .../PackageRestoreConfiguredInputFactory.cs | 15 +- .../Mocks/ProjectConfigurationFactory.cs | 141 +- .../Mocks/ProjectFactory.cs | 11 +- .../Mocks/ProjectPropertiesFactory.cs | 87 +- .../Mocks/ProjectRestoreInfoFactory.cs | 15 +- .../Mocks/ProjectServicesFactory.cs | 33 +- .../Mocks/ProjectValueDataSource.cs | 85 +- .../Mocks/ProjectValueDataSourceFactory.cs | 13 +- .../Mocks/PropertyPageData.cs | 27 +- .../Mocks/UnconfiguredProjectFactory.cs | 213 +- .../UnconfiguredProjectServicesFactory.cs | 55 +- ...derPrecedenceImportCollectionExtensions.cs | 27 +- .../AbstractMultiLifetimeComponentTests.cs | 251 +- .../ActiveConfiguredProjectsLoaderTests.cs | 199 +- .../ActiveConfiguredProjectsProviderTests.cs | 239 +- .../PublishableProjectConfigProviderTests.cs | 57 +- ...pAnalyzersGlobalPropertiesProviderTests.cs | 53 +- ...eTargetGlobalBuildPropertyProviderTests.cs | 101 +- ...ameworkGlobalBuildPropertyProviderTests.cs | 61 +- ...jectConfigurationDimensionProviderTests.cs | 29 +- .../ImplicitlyActiveDimensionProviderTests.cs | 95 +- ...jectConfigurationDimensionProviderTests.cs | 29 +- ...tConfigurationDimensionProviderTestBase.cs | 987 ++-- ...jectConfigurationDimensionProviderTests.cs | 29 +- .../ActiveDebugFrameworkServicesTests.cs | 159 +- .../Debug/DebugTokenReplacerTests.cs | 177 +- .../Debug/LaunchProfileExtensionsTests.cs | 317 +- .../ProjectSystem/Debug/LaunchProfileTests.cs | 113 +- .../Debug/LaunchSettingsJsonEncodingTests.cs | 435 +- .../Debug/LaunchSettingsProviderTests.cs | 1851 ++++--- .../Debug/LaunchSettingsTests.cs | 51 +- .../Debug/OutputTypeCheckerTest.cs | 109 +- .../ProjectSystem/FaultExtensionTests.cs | 45 +- .../ProjectSystem/FileItemServicesTests.cs | 143 +- .../CSharp/CSharpProjectImageProviderTests.cs | 89 +- .../FSharp/FSharpProjectImageProviderTests.cs | 85 +- .../ProjectImageProviderAggregatorTests.cs | 157 +- .../VisualBasicProjectImageProviderTests.cs | 89 +- .../CSharpCommandLineParserServiceTests.cs | 95 +- .../CSharp/CSharpSyntaxFactsServiceTests.cs | 19 +- .../CommandLineParserServiceTestBase.cs | 61 +- .../FSharpCommandLineParserServiceTests.cs | 205 +- ...isualBasicCommandLineParserServiceTests.cs | 95 +- .../VisualBasicSyntaxFactsServiceTests.cs | 19 +- .../Logging/ProjectLoggingExtensionsTests.cs | 169 +- ...tializedOnceDisposedUnderLockAsyncTests.cs | 597 ++- .../PackageRestoreDataSourceMocked.cs | 43 +- .../PackageRestoreDataSourceTests.cs | 175 +- ...kageRestoreProgressTrackerInstanceTests.cs | 213 +- .../PhysicalProjectTreeStorageTests.cs | 529 +- .../ProjectSystem/PhysicalProjectTreeTests.cs | 159 +- .../ProjectConfigurationExtensionsTests.cs | 21 +- .../AssemblyInfoPropertiesProviderTests.cs | 663 ++- .../Properties/BasePropertyExtensionTests.cs | 127 +- .../ApplicationManifestValueProviderTests.cs | 75 +- ...plicationManifestKindValueProviderTests.cs | 113 +- ...urceSpecificationKindValueProviderTests.cs | 171 +- ...mblyOriginatorKeyFileValueProviderTests.cs | 79 +- ...terceptedProjectPropertiesProviderTests.cs | 241 +- .../NeutralLanguageValueProviderTests.cs | 75 +- ...ckageFilePropertyValueProviderBaseTests.cs | 155 +- .../PackageLicenseKindValueProviderTests.cs | 113 +- .../TargetFrameworkValueProviderTests.cs | 71 +- .../LaunchProfilesProjectItemProviderTests.cs | 963 ++-- ...hProfilesProjectPropertiesProviderTests.cs | 315 +- .../LaunchProfilesProjectPropertiesTests.cs | 1281 +++-- ...aunchProfileExtensionValueProviderTests.cs | 403 +- .../Properties/MetadataExtensionsTests.cs | 277 +- .../NeutralLanguageEnumProviderTests.cs | 59 +- .../ProjectPropertiesExtensionTests.cs | 143 +- ...sAllowValidProjectReferenceCheckerTests.cs | 203 +- ...tractAppXamlSpecialFileProviderTestBase.cs | 107 +- ...ctFindByNameSpecialFileProviderTestBase.cs | 279 +- ...rAppDesignerSpecialFileProviderTestBase.cs | 423 +- ...pDesignerFolderSpecialFileProviderTests.cs | 465 +- .../AppManifestSpecialFileProviderTests.cs | 37 +- .../AppSettingsSpecialFileProviderTests.cs | 19 +- ...semblyResourcesSpecialFileProviderTests.cs | 19 +- ...CSharpAppConfigSpecialFileProviderTests.cs | 19 +- .../CSharpAppXamlSpecialFileProviderTests.cs | 19 +- ...arpAssemblyInfoSpecialFileProviderTests.cs | 19 +- .../LicensesSpecialFileProviderTests.cs | 19 +- ...lBasicAppConfigSpecialFileProviderTests.cs | 19 +- ...ualBasicAppXamlSpecialFileProviderTests.cs | 19 +- ...sicAssemblyInfoSpecialFileProviderTests.cs | 19 +- ...olderProjectTreePropertiesProviderTests.cs | 1285 +++-- .../TreeItemOrderPropertyProviderTests.cs | 439 +- ...rojectRootImageProjectTreeModifierTests.cs | 401 +- .../UnconfiguredProjectCommonServicesTests.cs | 151 +- .../UnconfiguredProjectTasksServiceTests.cs | 227 +- .../Resources/ResourceTests.cs | 71 +- .../Resources/XliffTests.cs | 55 +- .../Setup/PackageContentTests.cs | 51 +- .../Telemetry/ManagedTelemetryServiceTests.cs | 333 +- .../Text/LazyStringSplitTests.cs | 81 +- .../Tasks/CancellationSeriesTests.cs | 157 +- .../Tasks/SequentialTaskExecutorTests.cs | 245 +- .../Tasks/TaskDelaySchedulerTests.cs | 167 +- .../Threading/Tasks/TaskExtensionsTests.cs | 37 +- .../Threading/Tasks/TaskResultTests.cs | 41 +- .../Utilities/RepoUtil.cs | 47 +- .../Utilities/SetDiffTests.cs | 47 +- .../Mocks/DteFactory.cs | 29 +- ...iguredProjectSubscriptionServiceFactory.cs | 27 +- .../Mocks/IAddItemDialogServiceFactory.cs | 35 +- .../Mocks/IAsyncServiceProviderFactory.cs | 25 +- .../Mocks/IDebugLaunchProviderFactory.cs | 19 +- .../Mocks/IEntityRuntimeModelFactory.cs | 13 +- .../Mocks/IEntityWithIdFactory.cs | 19 +- .../Mocks/IEnvironmentOptionsFactory.cs | 25 +- ...HotReloadDiagnosticOutputServiceFactory.cs | 23 +- .../Mocks/IHotReloadOptionServiceFactory.cs | 17 +- .../Mocks/IOleAsyncServiceProviderFactory.cs | 17 +- .../Mocks/IProjectCapabilitiesScopeFactory.cs | 25 +- .../Mocks/IProjectEvaluationHandlerFactory.cs | 37 +- .../Mocks/IProjectGuidService2Factory.cs | 27 +- .../Mocks/IProjectGuidServiceFactory.cs | 17 +- .../Mocks/IProjectHotReloadAgentFactory.cs | 25 +- ...jectHotReloadNotificationServiceFactory.cs | 13 +- .../Mocks/IProjectHotReloadSessionFactory.cs | 17 +- .../IProjectHotReloadSessionManagerFactory.cs | 21 +- .../IProjectSpecificEditorProviderFactory.cs | 31 +- .../Mocks/IProjectStateFactory.cs | 47 +- .../Mocks/IProjectValueDataSourceFactory.cs | 15 +- .../Mocks/IQueryExecutionContextFactory.cs | 23 +- ...oteDebuggerAuthenticationServiceFactory.cs | 15 +- .../Mocks/IRoslynServicesFactory.cs | 17 +- .../Mocks/IServiceProviderFactory.cs | 17 +- .../Mocks/ISolutionBuildManagerFactory.cs | 77 +- .../Mocks/IStartupProjectHelperFactory.cs | 23 +- .../Mocks/ITokenReplacerFactory.cs | 17 +- .../IUnconfiguredProjectVsServicesFactory.cs | 89 +- .../IUnconfiguredProjectVsServicesMock.cs | 51 +- .../Mocks/IUserNotificationServicesFactory.cs | 11 +- .../Mocks/IVsAddProjectItemDlgFactory.cs | 43 +- .../Mocks/IVsAsyncFileChangeExMock.cs | 129 +- .../IVsContainedLanguageFactoryFactory.cs | 11 +- .../Mocks/IVsDebugger10Factory.cs | 15 +- .../Mocks/IVsHierarchyFactory.cs | 113 +- ...nguageServiceBuildErrorReporter2Factory.cs | 35 +- .../Mocks/IVsOnlineServicesFactory.cs | 17 +- .../Mocks/IVsProjectDesignerPageService.cs | 25 +- .../Mocks/IVsProjectSpecialFilesFactory.cs | 21 +- .../Mocks/IVsProject_Factory.cs | 99 +- .../Mocks/IVsServiceFactory.cs | 37 +- .../Mocks/IVsSolutionFactory.cs | 87 +- .../Mocks/IVsSolutionRestoreServiceFactory.cs | 45 +- .../Mocks/IVsTaskFactory.cs | 21 +- .../IVsThreadedWaitDialogFactoryFactory.cs | 103 +- .../Mocks/IVsUIServiceFactory.cs | 33 +- .../Mocks/IVsUpdateSolutionEvents3Factory.cs | 39 +- .../Mocks/IVsUpdateSolutionEventsFactory.cs | 39 +- .../Mocks/IVsUpgradeLoggerFactory.cs | 69 +- .../Mocks/IVsWindowFrameFactory.cs | 17 +- .../Mocks/IWorkspaceMockFactory.cs | 59 +- .../IWorkspaceProjectContextFactoryFactory.cs | 35 +- .../Mocks/IWorkspaceProjectContextMock.cs | 19 +- .../IWorkspaceProjectContextMockFactory.cs | 111 +- .../Mocks/IWorkspaceWriterFactory.cs | 81 +- .../Mocks/ProjectFactory.cs | 59 +- .../Mocks/PropertiesAvailableStatusFactory.cs | 261 +- .../Mocks/Reference3Factory.cs | 77 +- .../Mocks/SVsServiceProviderFactory.cs | 15 +- .../Mocks/SolutionFactory.cs | 43 +- .../Mocks/VSProjectFactory.cs | 61 +- .../VisualBasicNamespaceImportsListFactory.cs | 123 +- .../Mocks/VsStartupProjectsListService.cs | 25 +- .../CSharpExtenderCATIDProviderTests.cs | 77 +- .../VisualBasicExtenderCATIDProviderTests.cs | 77 +- .../VisualBasic/VsProjectEventsTests.cs | 93 +- .../VisualBasicNamespaceImportsListTests.cs | 191 +- .../VS/Automation/VsImportsTests.cs | 171 +- .../VsLangProjectPropertiesTests.cs | 329 +- ...plicitlyTriggeredDebugBuildManagerTests.cs | 169 +- .../LanguageServiceErrorListProviderTests.cs | 583 ++- ...CSharpProjectCompatibilityProviderTests.cs | 43 +- .../CSharpProjectTypeGuidProviderTests.cs | 27 +- .../ComponentComposition.ContractMetadata.cs | 15 +- .../ComponentComposition.ScopeComponents.cs | 71 +- .../ProjectSystem/VS/ComponentComposition.cs | 365 +- ...entVerificationTests.AllExportsTestData.cs | 23 +- ...nTests.ComposablePartDefinitionTestData.cs | 19 +- ...tVerificationTests.ComposedPartTestData.cs | 21 +- .../VS/ComponentVerificationTests.cs | 543 +- .../VS/CreateFileFromTemplateServiceTests.cs | 151 +- .../DebugProfileEnumValuesGeneratorTests.cs | 113 +- .../LaunchProfilesDebugLaunchProviderTests.cs | 209 +- ...aunchProfilesDebugPageGuidProviderTests.cs | 15 +- .../ProjectLaunchTargetsProviderTests.cs | 1275 +++-- .../StartupProjectRegistrarTests.cs | 297 +- .../VS/FSharp/FSharpProjectSelectorTests.cs | 35 +- .../ProjectHotReloadSessionManagerTests.cs | 321 +- .../AbstractAddItemCommandHandlerTests.cs | 303 +- ...bstractGenerateNuGetPackageCommandTests.cs | 287 +- ...AbstractOpenProjectDesignerCommandTests.cs | 269 +- .../DebugFrameworksDynamicMenuCommandTests.cs | 631 ++- .../DebugFrameworksMenuTextUpdaterTests.cs | 599 ++- ...etPackageProjectContextMenuCommandTests.cs | 75 +- ...GetPackageTopLevelBuildMenuCommandTests.cs | 75 +- .../OpenProjectDesignerCommandTests.cs | 35 +- ...jectDesignerOnDefaultActionCommandTests.cs | 35 +- .../Ordering/AbstractMoveCommandTests.cs | 93 +- .../Commands/Ordering/MoveDownCommandTests.cs | 339 +- .../Commands/Ordering/MoveUpCommandTests.cs | 339 +- .../Commands/Ordering/OrderingHelperTests.cs | 1781 ++++--- .../ActiveEditorContextTrackerTests.cs | 287 +- .../CSharp/CSharpCodeDomProviderTests.cs | 13 +- .../CSharpLanguageFeaturesProviderTests.cs | 229 +- ...ndlerTests.EvaluationCommandLineHandler.cs | 61 +- ...stractEvaluationCommandLineHandlerTests.cs | 795 ++- .../CompileItemHandler_CommandLineTests.cs | 95 +- .../CompileItemHandler_EvaluationTests.cs | 25 +- .../Handlers/DynamicItemHandlerTests.cs | 195 +- .../Handlers/EvaluationHandlerTestBase.cs | 29 +- .../MetadataReferenceItemHandlerTests.cs | 107 +- ...athAndDisplayNameEvaluationHandlerTests.cs | 231 +- .../ProjectPropertiesItemHandlerTests.cs | 217 +- .../Handlers/SourceItemsHandlerTestBase.cs | 15 +- .../VisualBasicCodeDomProviderTests.cs | 13 +- ...ContainedLanguageComponentsFactoryTests.cs | 221 +- ...CapabilitiesMissingVetoProjectLoadTests.cs | 89 +- .../AuthenticationModeEnumProviderTests.cs | 65 +- ...rsistingProjectGuidStorageProviderTests.cs | 819 ++- ...harpProjectConfigurationPropertiesTests.cs | 121 +- .../CSharpProjectDesignerPageProviderTests.cs | 101 +- .../FSharpProjectDesignerPageProviderTests.cs | 101 +- ...unchTargetPropertyPageEnumProviderTests.cs | 125 +- .../OutputTypeExValueProviderTests.cs | 95 +- .../OutputTypeValueProviderTests.cs | 95 +- .../PostBuildEventValueProviderTests.cs | 1589 +++--- .../PreBuildEventValueProviderTests.cs | 1565 +++--- .../ProjectDesignerPageMetadataTests.cs | 99 +- .../Properties/ProjectDesignerServiceTests.cs | 209 +- .../VBDefineConstantsEncodingTests.cs | 233 +- .../MapDynamicEnumValuesProviderTests.cs | 205 +- ...asicProjectConfigurationPropertiesTests.cs | 121 +- ...alBasicProjectDesignerPageProviderTests.cs | 101 +- .../VS/PropertyPages/BuildMacroInfoTests.cs | 75 +- .../VS/PropertyPages/PropertyPageTests.cs | 109 +- .../ProjectLaunchProfileHandlerTests.cs | 351 +- .../CategoryDataProducerTests.cs | 145 +- ...ConfigurationDimensionDataProducerTests.cs | 93 +- .../ProjectActionProviderTests.cs | 299 +- .../PropertyPageDataProducerTests.cs | 103 +- .../SupportedValueDataProducerTests.cs | 77 +- .../UIEditorMetadataProducerTests.cs | 93 +- .../UIPropertyDataProducerTests.cs | 461 +- .../UIPropertyEditorDataProducerTests.cs | 225 +- .../UIPropertyValueDataProducerTests.cs | 259 +- .../QueryProjectPropertiesContextTests.cs | 71 +- .../DesignTimeAssemblyResolutionTests.cs | 491 +- .../ReferenceCleanupServiceTests.cs | 305 +- .../VS/Rename/CSharp/RenamerTests.cs | 281 +- .../VS/Rename/RenamerTestsBase.cs | 203 +- .../VS/Rename/VisualBasic/RenamerTests.cs | 449 +- .../VS/Rules/DependencyRuleTests.cs | 417 +- .../VS/Rules/ExportedRuleTests.cs | 217 +- .../ProjectSystem/VS/Rules/ItemRuleTests.cs | 365 +- .../VS/Rules/MiscellaneousRuleTests.cs | 419 +- .../VS/Rules/ProjectPropertiesRuleTests.cs | 99 +- .../ProjectSystem/VS/Rules/RuleServices.cs | 89 +- .../VS/Rules/XamlRuleTestBase.cs | 139 +- .../VsProjectSpecialFilesManagerTests.cs | 89 +- ...DesignTimeInputsBuildManagerBridgeTests.cs | 219 +- .../DesignTimeInputsChangeTrackerTests.cs | 555 ++- ...imeInputsCompiler.CompilationQueueTests.cs | 173 +- .../TempPE/DesignTimeInputsCompilerTests.cs | 527 +- .../TempPE/DesignTimeInputsDataSourceTests.cs | 279 +- .../DesignTimeInputsFileWatcherTests.cs | 239 +- .../VS/UnconfiguredProjectVsServicesTests.cs | 133 +- .../VS/UpToDate/BuildUpToDateCheckTestBase.cs | 163 +- .../VS/UpToDate/BuildUpToDateCheckTests.cs | 4419 ++++++++--------- ...ToDateCheckImplicitConfiguredInputTests.cs | 79 +- .../VS/Utilities/StringExtensions.cs | 37 +- .../ProjectSystem/VS/Utilities/TestUtil.cs | 43 +- ...lBasicProjectCompatibilityProviderTests.cs | 43 +- ...VisualBasicProjectTypeGuidProviderTests.cs | 27 +- .../ProjectSystem/VS/VsUIServiceTests.cs | 143 +- .../Waiting/VisualStudioWaitContextTests.cs | 141 +- .../Waiting/VisualStudioWaitIndicatorTests.cs | 135 +- .../WindowsFormsAddItemFilterTests.cs | 101 +- .../WindowsFormsEditorProviderTests.cs | 501 +- .../VS/Xproj/XprojProjectFactoryTests.cs | 53 +- .../Setup/PackageContentTests.cs | 97 +- .../Setup/SwrTests.cs | 247 +- .../Shell/HierarchyIdTests.cs | 335 +- 1191 files changed, 70343 insertions(+), 71534 deletions(-) diff --git a/setup/Common/ProvideCodeBaseBindingRedirection.cs b/setup/Common/ProvideCodeBaseBindingRedirection.cs index edb323c44c..381dded44c 100644 --- a/setup/Common/ProvideCodeBaseBindingRedirection.cs +++ b/setup/Common/ProvideCodeBaseBindingRedirection.cs @@ -2,36 +2,35 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +/// +/// A that provides code-base binding redirects. +/// +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class ProvideCodeBaseBindingRedirectionAttribute : RegistrationAttribute { - /// - /// A that provides code-base binding redirects. - /// - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class ProvideCodeBaseBindingRedirectionAttribute : RegistrationAttribute - { - private readonly ProvideBindingRedirectionAttribute _redirectionAttribute; + private readonly ProvideBindingRedirectionAttribute _redirectionAttribute; - public ProvideCodeBaseBindingRedirectionAttribute(string assemblyName) + public ProvideCodeBaseBindingRedirectionAttribute(string assemblyName) + { + // ProvideBindingRedirectionAttribute is sealed, so we can't inherit from it to provide defaults. + // Instead, we'll do more of an aggregation pattern here. + _redirectionAttribute = new ProvideBindingRedirectionAttribute { - // ProvideBindingRedirectionAttribute is sealed, so we can't inherit from it to provide defaults. - // Instead, we'll do more of an aggregation pattern here. - _redirectionAttribute = new ProvideBindingRedirectionAttribute - { - AssemblyName = assemblyName, - OldVersionLowerBound = "0.0.0.0", - CodeBase = assemblyName + ".dll", - }; - } + AssemblyName = assemblyName, + OldVersionLowerBound = "0.0.0.0", + CodeBase = assemblyName + ".dll", + }; + } - public override void Register(RegistrationContext context) - { - _redirectionAttribute.Register(context); - } + public override void Register(RegistrationContext context) + { + _redirectionAttribute.Register(context); + } - public override void Unregister(RegistrationContext context) - { - _redirectionAttribute.Unregister(context); - } + public override void Unregister(RegistrationContext context) + { + _redirectionAttribute.Unregister(context); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/CodeMarkerTimerId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/CodeMarkerTimerId.cs index e0d9597fe3..8e2d4d9a68 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/CodeMarkerTimerId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/CodeMarkerTimerId.cs @@ -2,16 +2,15 @@ #pragma warning disable CA1200 // Avoid using cref tags with a prefix -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +/// +/// Represents timer IDs that are passed to . +/// +internal static class CodeMarkerTimerId { /// - /// Represents timer IDs that are passed to . + /// Indicates that NuGet package restore has finished. /// - internal static class CodeMarkerTimerId - { - /// - /// Indicates that NuGet package restore has finished. - /// - public const int PerfPackageRestoreEnd = 7343; - } + public const int PerfPackageRestoreEnd = 7343; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/IO/WindowsFileExplorer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/IO/WindowsFileExplorer.cs index 126b95fe73..923cb47418 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/IO/WindowsFileExplorer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/IO/WindowsFileExplorer.cs @@ -3,76 +3,75 @@ using System.Runtime.InteropServices; using Path = Microsoft.IO.Path; -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +[Export(typeof(IFileExplorer))] +internal class WindowsFileExplorer : IFileExplorer { - [Export(typeof(IFileExplorer))] - internal class WindowsFileExplorer : IFileExplorer + private readonly IFileSystem _fileSystem; + + [ImportingConstructor] + public WindowsFileExplorer(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + public void OpenContainingFolder(string path) { - private readonly IFileSystem _fileSystem; + Requires.NotNullOrEmpty(path); - [ImportingConstructor] - public WindowsFileExplorer(IFileSystem fileSystem) + // When 'path' doesn't exist, Explorer just opens the default + // "Quick Access" page, so try to something better than that. + if (_fileSystem.PathExists(path)) { - _fileSystem = fileSystem; + // Tell Explorer to open the parent folder of the item, selecting the item + ShellExecute( + string.Empty, + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "explorer.exe"), + parameters: $"/select,\"{path}\""); } - - public void OpenContainingFolder(string path) + else { - Requires.NotNullOrEmpty(path); - - // When 'path' doesn't exist, Explorer just opens the default - // "Quick Access" page, so try to something better than that. - if (_fileSystem.PathExists(path)) - { - // Tell Explorer to open the parent folder of the item, selecting the item - ShellExecute( - string.Empty, - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "explorer.exe"), - parameters: $"/select,\"{path}\""); - } - else + string? parentPath = GetParentPath(path); + if (parentPath is not null && _fileSystem.DirectoryExists(parentPath)) { - string? parentPath = GetParentPath(path); - if (parentPath is not null && _fileSystem.DirectoryExists(parentPath)) - { - OpenFolder(parentPath); - } + OpenFolder(parentPath); } } + } - public void OpenFolder(string path) - { - Requires.NotNullOrEmpty(path); + public void OpenFolder(string path) + { + Requires.NotNullOrEmpty(path); - // Tell Explorer just open the contents of the folder, selecting nothing - ShellExecute("explore", path); - } + // Tell Explorer just open the contents of the folder, selecting nothing + ShellExecute("explore", path); + } - protected static void ShellExecute(string operation, string filePath, string? parameters = null) - { - // Workaround of CLR bug 1134711; System.Diagnostics.Process.Start() does not support GB18030 - _ = ShellExecute(IntPtr.Zero, operation, filePath, parameters, lpDirectory: null, 1); - } + protected static void ShellExecute(string operation, string filePath, string? parameters = null) + { + // Workaround of CLR bug 1134711; System.Diagnostics.Process.Start() does not support GB18030 + _ = ShellExecute(IntPtr.Zero, operation, filePath, parameters, lpDirectory: null, 1); + } - private static string? GetParentPath(string path) + private static string? GetParentPath(string path) + { + // Remove trailing slashes, so that GetDirectoryName returns + // "Foo" in C:\Foo\Project\" instead of "C:\Foo\Project". + if (Path.EndsInDirectorySeparator(path)) { - // Remove trailing slashes, so that GetDirectoryName returns - // "Foo" in C:\Foo\Project\" instead of "C:\Foo\Project". - if (Path.EndsInDirectorySeparator(path)) - { - path = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - } - - return Path.GetDirectoryName(path); + path = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); } - [DllImport("shell32.dll", EntryPoint = "ShellExecuteW")] - internal static extern IntPtr ShellExecute( - IntPtr hwnd, - [MarshalAs(UnmanagedType.LPWStr)] string lpOperation, - [MarshalAs(UnmanagedType.LPWStr)] string lpFile, - [MarshalAs(UnmanagedType.LPWStr)] string? lpParameters, - [MarshalAs(UnmanagedType.LPWStr)] string? lpDirectory, - int nShowCmd); + return Path.GetDirectoryName(path); } + + [DllImport("shell32.dll", EntryPoint = "ShellExecuteW")] + internal static extern IntPtr ShellExecute( + IntPtr hwnd, + [MarshalAs(UnmanagedType.LPWStr)] string lpOperation, + [MarshalAs(UnmanagedType.LPWStr)] string lpFile, + [MarshalAs(UnmanagedType.LPWStr)] string? lpParameters, + [MarshalAs(UnmanagedType.LPWStr)] string? lpDirectory, + int nShowCmd); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/CommandGroup.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/CommandGroup.cs index 9fc4abf3ba..b6b5daab9a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/CommandGroup.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/CommandGroup.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known command groups. +/// +internal static class CommandGroup { - /// - /// Provides common well-known command groups. - /// - internal static class CommandGroup - { - public const string UIHierarchyWindow = VSConstants.CMDSETID.UIHierarchyWindowCommandSet_string; - public const string VisualStudioStandard97 = VSConstants.CMDSETID.StandardCommandSet97_string; - public const string VisualStudioStandard2k = VSConstants.CMDSETID.StandardCommandSet2K_string; - public const string FSharpProject = "{75AC5611-A912-4195-8A65-457AE17416FB}"; - public const string ManagedProjectSystemOrder = "{6C4806E9-034E-4B64-99DE-29A6F837B993}"; - public const string ManagedProjectSystem = "{568ABDF7-D522-474D-9EED-34B5E5095BA5}"; - public const string WPF = "{A8878AC2-6163-4C15-9767-1871DD750C6A}"; - } + public const string UIHierarchyWindow = VSConstants.CMDSETID.UIHierarchyWindowCommandSet_string; + public const string VisualStudioStandard97 = VSConstants.CMDSETID.StandardCommandSet97_string; + public const string VisualStudioStandard2k = VSConstants.CMDSETID.StandardCommandSet2K_string; + public const string FSharpProject = "{75AC5611-A912-4195-8A65-457AE17416FB}"; + public const string ManagedProjectSystemOrder = "{6C4806E9-034E-4B64-99DE-29A6F837B993}"; + public const string ManagedProjectSystem = "{568ABDF7-D522-474D-9EED-34B5E5095BA5}"; + public const string WPF = "{A8878AC2-6163-4C15-9767-1871DD750C6A}"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ExternalResourceIds.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ExternalResourceIds.cs index 34a2071a27..d42a3f0345 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ExternalResourceIds.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ExternalResourceIds.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +// from VS: src\vsproject\cool\coolpkg\resource.h +internal enum LegacyCSharpStringResourceIds : uint { - // from VS: src\vsproject\cool\coolpkg\resource.h - internal enum LegacyCSharpStringResourceIds : uint - { - IDS_TEMPLATE_NEWWFCWIN32FORM = 2237, - IDS_TEMPLATE_DIRLOCALITEMS = 2339, - IDS_TEMPLATE_NEWCSharpCLASS = 2245, - IDS_TEMPLATE_NEWWFCCOMPONENT = 2246, - IDS_TEMPLATE_NEWUSERCONTROL = 2295, - IDS_PROJECTITEMTYPE_STR = 2346, - } + IDS_TEMPLATE_NEWWFCWIN32FORM = 2237, + IDS_TEMPLATE_DIRLOCALITEMS = 2339, + IDS_TEMPLATE_NEWCSharpCLASS = 2245, + IDS_TEMPLATE_NEWWFCCOMPONENT = 2246, + IDS_TEMPLATE_NEWUSERCONTROL = 2295, + IDS_PROJECTITEMTYPE_STR = 2346, +} - // from VS: src\vsproject\vb\vbprj\vbprjstr.h - internal enum LegacyVBStringResourceIds : uint - { - IDS_VSDIR_ITEM_CLASS = 3020, - IDS_VSDIR_ITEM_COMPONENT = 3024, - IDS_VSDIR_ITEM_MODULE = 3028, - IDS_VSDIR_ITEM_USERCTRL = 3048, - IDS_VSDIR_ITEM_WINFORM = 3050, - IDS_VSDIR_CLIENTPROJECTITEMS = 3081, - IDS_VSDIR_VBPROJECTFILES = 3082, - } +// from VS: src\vsproject\vb\vbprj\vbprjstr.h +internal enum LegacyVBStringResourceIds : uint +{ + IDS_VSDIR_ITEM_CLASS = 3020, + IDS_VSDIR_ITEM_COMPONENT = 3024, + IDS_VSDIR_ITEM_MODULE = 3028, + IDS_VSDIR_ITEM_USERCTRL = 3048, + IDS_VSDIR_ITEM_WINFORM = 3050, + IDS_VSDIR_CLIENTPROJECTITEMS = 3081, + IDS_VSDIR_VBPROJECTFILES = 3082, +} - // from VS: src\vsproject\fidalgo\WPF\Flavor\WPFFlavor\WPFProject.cs - internal enum WPFTemplateNames : uint - { - WPFPage = 4658, - WPFResourceDictionary = 4662, - WPFUserControl = 4664, - WPFWindow = 4666, - } +// from VS: src\vsproject\fidalgo\WPF\Flavor\WPFFlavor\WPFProject.cs +internal enum WPFTemplateNames : uint +{ + WPFPage = 4658, + WPFResourceDictionary = 4662, + WPFUserControl = 4664, + WPFWindow = 4666, } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/FSharpProjectCommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/FSharpProjectCommandId.cs index 22c27a8b70..cfd077468e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/FSharpProjectCommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/FSharpProjectCommandId.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known F# command IDs. +/// +internal static class FSharpProjectCommandId { - /// - /// Provides common well-known F# command IDs. - /// - internal static class FSharpProjectCommandId - { - public const int MoveUp = 0x3002; - public const int MoveDown = 0x3007; - } + public const int MoveUp = 0x3002; + public const int MoveDown = 0x3007; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemCommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemCommandId.cs index 4d7743d990..612ed7b388 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemCommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemCommandId.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known project-system command IDs. +/// +internal static class ManagedProjectSystemCommandId { - /// - /// Provides common well-known project-system command IDs. - /// - internal static class ManagedProjectSystemCommandId - { - public const long GenerateNuGetPackageProjectContextMenu = 0x2000; - public const long GenerateNuGetPackageTopLevelBuild = 0x2001; - public const long NavigateToProject = 0x2002; - public const int DebugTargetMenuDebugFrameworkMenu = 0x3000; - public const int DebugFrameworks = 0x3050; - } + public const long GenerateNuGetPackageProjectContextMenu = 0x2000; + public const long GenerateNuGetPackageTopLevelBuild = 0x2001; + public const long NavigateToProject = 0x2002; + public const int DebugTargetMenuDebugFrameworkMenu = 0x3000; + public const int DebugFrameworks = 0x3050; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemOrderCommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemOrderCommandId.cs index f387be209d..6764cd4413 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemOrderCommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/ManagedProjectSystemOrderCommandId.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known project-system order related command IDs. +/// +internal static class ManagedProjectSystemOrderCommandId { - /// - /// Provides common well-known project-system order related command IDs. - /// - internal static class ManagedProjectSystemOrderCommandId - { - public const int AddNewItemAbove = 0x2002; - public const int AddExistingItemAbove = 0x2003; - public const int AddNewItemBelow = 0x2004; - public const int AddExistingItemBelow = 0x2005; - } + public const int AddNewItemAbove = 0x2002; + public const int AddExistingItemAbove = 0x2003; + public const int AddNewItemBelow = 0x2004; + public const int AddExistingItemBelow = 0x2005; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/UIHierarchyWindowCommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/UIHierarchyWindowCommandId.cs index 75352f7045..4c5541deef 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/UIHierarchyWindowCommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/UIHierarchyWindowCommandId.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known command IDs. +/// +internal static class UIHierarchyWindowCommandId { - /// - /// Provides common well-known command IDs. - /// - internal static class UIHierarchyWindowCommandId - { - public const long DoubleClick = (long)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick; - public const long EnterKey = (long)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey; - } + public const long DoubleClick = (long)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick; + public const long EnterKey = (long)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard2KCommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard2KCommandId.cs index 1868adffff..68ab0ef6c9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard2KCommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard2KCommandId.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +internal static class VisualStudioStandard2KCommandId { - internal static class VisualStudioStandard2KCommandId - { - public const long AddForm = (long)VSConstants.VSStd2KCmdID.ADDWFCFORM; - public const long AddUserControl = (long)VSConstants.VSStd2KCmdID.ADDUSERCONTROL; - public const long AddComponent = (long)VSConstants.VSStd2KCmdID.ADDTBXCOMPONENT; - public const long AddModule = (long)VSConstants.VSStd2KCmdID.ADDMODULE; - } + public const long AddForm = (long)VSConstants.VSStd2KCmdID.ADDWFCFORM; + public const long AddUserControl = (long)VSConstants.VSStd2KCmdID.ADDUSERCONTROL; + public const long AddComponent = (long)VSConstants.VSStd2KCmdID.ADDTBXCOMPONENT; + public const long AddModule = (long)VSConstants.VSStd2KCmdID.ADDMODULE; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard97CommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard97CommandId.cs index 7cc8be6436..0a00446e4f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard97CommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/VisualStudioStandard97CommandId.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known command IDs. +/// +internal static class VisualStudioStandard97CommandId { - /// - /// Provides common well-known command IDs. - /// - internal static class VisualStudioStandard97CommandId - { - public const long Open = (long)VSConstants.VSStd97CmdID.Open; - public const long AddClass = (long)VSConstants.VSStd97CmdID.AddClass; - } + public const long Open = (long)VSConstants.VSStd97CmdID.Open; + public const long AddClass = (long)VSConstants.VSStd97CmdID.AddClass; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/WPFCommandId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/WPFCommandId.cs index fe6ac74ede..b143dea75d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/WPFCommandId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Input/WPFCommandId.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Input +namespace Microsoft.VisualStudio.Input; + +/// +/// Provides common well-known command IDs from the WPF flavor. +/// +internal static class WPFCommandId { - /// - /// Provides common well-known command IDs from the WPF flavor. - /// - internal static class WPFCommandId - { - // from VS: src\vsproject\fidalgo\WPF\Flavor\WPFFlavor\Guids.cs - public const long AddWPFWindow = 0x100; - public const long AddWPFPage = 0x200; - public const long AddWPFUserControl = 0x300; - public const long AddWPFResourceDictionary = 0x400; - public const long WPFWindow = 0x600; - public const long WPFPage = 0x700; - public const long WPFUserControl = 0x800; - public const long WPFResourceDictionary = 0x900; - } + // from VS: src\vsproject\fidalgo\WPF\Flavor\WPFFlavor\Guids.cs + public const long AddWPFWindow = 0x100; + public const long AddWPFPage = 0x200; + public const long AddWPFUserControl = 0x300; + public const long AddWPFResourceDictionary = 0x400; + public const long WPFWindow = 0x600; + public const long WPFPage = 0x700; + public const long WPFUserControl = 0x800; + public const long WPFResourceDictionary = 0x900; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs index ae8aaa9799..7e09831b04 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/DebuggerTraceListener.cs @@ -8,53 +8,52 @@ using Microsoft.VisualStudio.ProjectSystem.VS; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.Packaging +namespace Microsoft.VisualStudio.Packaging; + +[Export(typeof(IPackageService))] +internal sealed class DebuggerTraceListener : TraceListener, IPackageService { - [Export(typeof(IPackageService))] - internal sealed class DebuggerTraceListener : TraceListener, IPackageService + public Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) { - public Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) + // There's no public API registering a trace listener for a + // non-public trace source, so we need to use reflection + string assemblyName = typeof(AppliesToAttribute).Assembly.FullName; + string typeName = $"Microsoft.VisualStudio.ProjectSystem.TraceUtilities, {assemblyName}"; + + var type = Type.GetType(typeName); + if (type is null) + { + Assumes.Fail($"Could not find type '{typeName}'"); + } + + const string sourcePropertyName = "Source"; + PropertyInfo? property = type.GetProperty(sourcePropertyName, BindingFlags.NonPublic | BindingFlags.Static); + if (property is null) { - // There's no public API registering a trace listener for a - // non-public trace source, so we need to use reflection - string assemblyName = typeof(AppliesToAttribute).Assembly.FullName; - string typeName = $"Microsoft.VisualStudio.ProjectSystem.TraceUtilities, {assemblyName}"; - - var type = Type.GetType(typeName); - if (type is null) - { - Assumes.Fail($"Could not find type '{typeName}'"); - } - - const string sourcePropertyName = "Source"; - PropertyInfo? property = type.GetProperty(sourcePropertyName, BindingFlags.NonPublic | BindingFlags.Static); - if (property is null) - { - Assumes.Fail($"Could not find property '{sourcePropertyName}' in type '{typeName}'"); - } - - var source = (TraceSource)property.GetValue(null); - - source.Switch.Level = SourceLevels.Warning; - source.Listeners.Add(this); - - return Task.CompletedTask; + Assumes.Fail($"Could not find property '{sourcePropertyName}' in type '{typeName}'"); } - public override void Write(string message) + var source = (TraceSource)property.GetValue(null); + + source.Switch.Level = SourceLevels.Warning; + source.Listeners.Add(this); + + return Task.CompletedTask; + } + + public override void Write(string message) + { + if (System.Diagnostics.Debugger.IsLogging()) { - if (System.Diagnostics.Debugger.IsLogging()) - { - System.Diagnostics.Debugger.Log(0, null, message); - } + System.Diagnostics.Debugger.Log(0, null, message); } + } - public override void WriteLine(string message) + public override void WriteLine(string message) + { + if (System.Diagnostics.Debugger.IsLogging()) { - if (System.Diagnostics.Debugger.IsLogging()) - { - System.Diagnostics.Debugger.Log(0, null, message + Environment.NewLine); - } + System.Diagnostics.Debugger.Log(0, null, message + Environment.NewLine); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ManagedProjectSystemPackage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ManagedProjectSystemPackage.cs index acd5676af1..cbf3a6d8d6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ManagedProjectSystemPackage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ManagedProjectSystemPackage.cs @@ -7,45 +7,44 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Xproj; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.Packaging +namespace Microsoft.VisualStudio.Packaging; + +[Guid(PackageGuid)] +[PackageRegistration(AllowsBackgroundLoading = true, RegisterUsing = RegistrationMethod.Assembly, UseManagedResourcesOnly = true)] +[ProvideProjectFactory(typeof(XprojProjectFactory), null, "#27", "xproj", "xproj", null)] +[ProvideAutoLoad(ActivationContextGuid, PackageAutoLoadFlags.BackgroundLoad)] +[ProvideUIContextRule( + contextGuid: ActivationContextGuid, + name: "Load Managed Project Package", + expression: "dotnetcore", + termNames: ["dotnetcore"], + termValues: ["SolutionHasProjectCapability:.NET & CPS"])] +[ProvideMenuResource("Menus.ctmenu", 5)] +internal sealed class ManagedProjectSystemPackage : AsyncPackage { - [Guid(PackageGuid)] - [PackageRegistration(AllowsBackgroundLoading = true, RegisterUsing = RegistrationMethod.Assembly, UseManagedResourcesOnly = true)] - [ProvideProjectFactory(typeof(XprojProjectFactory), null, "#27", "xproj", "xproj", null)] - [ProvideAutoLoad(ActivationContextGuid, PackageAutoLoadFlags.BackgroundLoad)] - [ProvideUIContextRule( - contextGuid: ActivationContextGuid, - name: "Load Managed Project Package", - expression: "dotnetcore", - termNames: ["dotnetcore"], - termValues: ["SolutionHasProjectCapability:.NET & CPS"])] - [ProvideMenuResource("Menus.ctmenu", 5)] - internal sealed class ManagedProjectSystemPackage : AsyncPackage - { - public const string ActivationContextGuid = "E7DF1626-44DD-4E8C-A8A0-92EAB6DDC16E"; - public const string PackageGuid = "860A27C0-B665-47F3-BC12-637E16A1050A"; + public const string ActivationContextGuid = "E7DF1626-44DD-4E8C-A8A0-92EAB6DDC16E"; + public const string PackageGuid = "860A27C0-B665-47F3-BC12-637E16A1050A"; - protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) - { - // Here we initialize our internal IPackageService implementations, both in global and project services scope. + protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + // Here we initialize our internal IPackageService implementations, both in global and project services scope. - // Get access to global MEF services. - IComponentModel componentModel = await this.GetServiceAsync(); + // Get access to global MEF services. + IComponentModel componentModel = await this.GetServiceAsync(); - // Get access to project services scope services. - IProjectServiceAccessor projectServiceAccessor = componentModel.GetService(); + // Get access to project services scope services. + IProjectServiceAccessor projectServiceAccessor = componentModel.GetService(); - // Find package services in global scope. - IEnumerable globalPackageServices = componentModel.GetExtensions(); + // Find package services in global scope. + IEnumerable globalPackageServices = componentModel.GetExtensions(); - // Find package services in project service scope. - IEnumerable projectServicesPackageServices = projectServiceAccessor.GetProjectService().Services.ExportProvider.GetExportedValues(ExportContractNames.Scopes.ProjectService); + // Find package services in project service scope. + IEnumerable projectServicesPackageServices = projectServiceAccessor.GetProjectService().Services.ExportProvider.GetExportedValues(ExportContractNames.Scopes.ProjectService); - // We initialize these on the main thread. - await JoinableTaskFactory.SwitchToMainThreadAsync(); + // We initialize these on the main thread. + await JoinableTaskFactory.SwitchToMainThreadAsync(); - // Initialize all services concurrently. - await Task.WhenAll(globalPackageServices.Concat(projectServicesPackageServices).Select(s => s.InitializeAsync(this))); - } + // Initialize all services concurrently. + await Task.WhenAll(globalPackageServices.Concat(projectServicesPackageServices).Select(s => s.InitializeAsync(this))); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProjectTypeCapabilities.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProjectTypeCapabilities.cs index 1ffdccfaa3..2419bff6bd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProjectTypeCapabilities.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProjectTypeCapabilities.cs @@ -2,60 +2,59 @@ using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.Packaging +namespace Microsoft.VisualStudio.Packaging; + +/// +/// Represents set of capabilities for .NET-based projects that are always present ("fixed"). +/// +/// +/// These capabilities (along with any active IProjectCapabilitiesProvider) are combined with +/// the "dynamic" capabilities inherited from the active configuration. These are typically +/// defined in Microsoft.Managed.DesignTime.targets, but could come from other locations such +/// as packages or other target files. +/// +internal static class ProjectTypeCapabilities { /// - /// Represents set of capabilities for .NET-based projects that are always present ("fixed"). + /// Represent set of capabilities for all .NET-based project that are always present ("fixed"). /// - /// - /// These capabilities (along with any active IProjectCapabilitiesProvider) are combined with - /// the "dynamic" capabilities inherited from the active configuration. These are typically - /// defined in Microsoft.Managed.DesignTime.targets, but could come from other locations such - /// as packages or other target files. - /// - internal static class ProjectTypeCapabilities - { - /// - /// Represent set of capabilities for all .NET-based project that are always present ("fixed"). - /// - public const string Default = ProjectCapability.AppDesigner + "; " + - ProjectCapability.EditAndContinue + "; " + - ProjectCapability.HandlesOwnReload + "; " + - ProjectCapability.OpenProjectFile + "; " + - ProjectCapability.PreserveFormatting + "; " + - ProjectCapability.ProjectConfigurationsDeclaredDimensions + "; " + - ProjectCapability.LanguageService + "; " + - ProjectCapability.DotNet; + public const string Default = ProjectCapability.AppDesigner + "; " + + ProjectCapability.EditAndContinue + "; " + + ProjectCapability.HandlesOwnReload + "; " + + ProjectCapability.OpenProjectFile + "; " + + ProjectCapability.PreserveFormatting + "; " + + ProjectCapability.ProjectConfigurationsDeclaredDimensions + "; " + + ProjectCapability.LanguageService + "; " + + ProjectCapability.DotNet; - /// - /// Represents F#'s (fsproj) set of capabilities that are always present ("fixed"). - /// - public const string FSharp = Default + "; " + - ProjectCapability.FSharp + "; " + - ProjectCapability.SortByDisplayOrder + "; " + - ProjectCapability.EditableDisplayOrder; + /// + /// Represents F#'s (fsproj) set of capabilities that are always present ("fixed"). + /// + public const string FSharp = Default + "; " + + ProjectCapability.FSharp + "; " + + ProjectCapability.SortByDisplayOrder + "; " + + ProjectCapability.EditableDisplayOrder; - /// - /// Represents C#'s (csproj) set of capabilities that are always present ("fixed"). - /// - /// - /// NOTE: C# Shared Project's (shproj) see a limited set of fixed capabilities defined - /// in CPS under codesharingproj.pkgdef. - /// - public const string CSharp = Default + "; " + - ProjectCapability.CSharp + "; " + - ProjectCapabilities.SharedImports + "; " + - ProjectCapability.UseProjectEvaluationCache; - /// - /// Represents Visual Basic's (vbproj) set of capabilities that are always present ("fixed"). - /// - /// - /// NOTE: Visual Basic Shared Project's (shproj) see a limited set of fixed capabilities defined - /// in CPS under codesharingproj.pkgdef. - /// - public const string VisualBasic = Default + "; " + - ProjectCapability.VisualBasic + "; " + - ProjectCapabilities.SharedImports + "; " + - ProjectCapability.UseProjectEvaluationCache; - } + /// + /// Represents C#'s (csproj) set of capabilities that are always present ("fixed"). + /// + /// + /// NOTE: C# Shared Project's (shproj) see a limited set of fixed capabilities defined + /// in CPS under codesharingproj.pkgdef. + /// + public const string CSharp = Default + "; " + + ProjectCapability.CSharp + "; " + + ProjectCapabilities.SharedImports + "; " + + ProjectCapability.UseProjectEvaluationCache; + /// + /// Represents Visual Basic's (vbproj) set of capabilities that are always present ("fixed"). + /// + /// + /// NOTE: Visual Basic Shared Project's (shproj) see a limited set of fixed capabilities defined + /// in CPS under codesharingproj.pkgdef. + /// + public const string VisualBasic = Default + "; " + + ProjectCapability.VisualBasic + "; " + + ProjectCapabilities.SharedImports + "; " + + ProjectCapability.UseProjectEvaluationCache; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideDiffSupportedContentTypeAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideDiffSupportedContentTypeAttribute.cs index bd8230ecb8..f9a40a9153 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideDiffSupportedContentTypeAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideDiffSupportedContentTypeAttribute.cs @@ -2,32 +2,31 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class ProvideDiffSupportedContentTypeAttribute : RegistrationAttribute { - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class ProvideDiffSupportedContentTypeAttribute : RegistrationAttribute - { - private const string RegistryKey = @"Diff\SupportedContentTypes"; + private const string RegistryKey = @"Diff\SupportedContentTypes"; - private readonly string _contentType; - private readonly string _extension; + private readonly string _contentType; + private readonly string _extension; - public ProvideDiffSupportedContentTypeAttribute(string extension, string contentType) - { - _extension = extension; - _contentType = contentType; - } + public ProvideDiffSupportedContentTypeAttribute(string extension, string contentType) + { + _extension = extension; + _contentType = contentType; + } - public override void Register(RegistrationContext context) - { - using Key key = context.CreateKey(RegistryKey); + public override void Register(RegistrationContext context) + { + using Key key = context.CreateKey(RegistryKey); - key.SetValue(_extension, _contentType); - } + key.SetValue(_extension, _contentType); + } - public override void Unregister(RegistrationContext context) - { - context.RemoveValue(RegistryKey, _extension); - } + public override void Unregister(RegistrationContext context) + { + context.RemoveValue(RegistryKey, _extension); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideEditorFactoryMappingAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideEditorFactoryMappingAttribute.cs index b74e095fc9..1504c6db5c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideEditorFactoryMappingAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Packaging/ProvideEditorFactoryMappingAttribute.cs @@ -2,26 +2,25 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.Packaging +namespace Microsoft.VisualStudio.Packaging; + +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class ProvideEditorFactoryMappingAttribute : RegistrationAttribute { - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class ProvideEditorFactoryMappingAttribute : RegistrationAttribute - { - private readonly ProvideLanguageExtensionAttribute _wrapped; + private readonly ProvideLanguageExtensionAttribute _wrapped; - public ProvideEditorFactoryMappingAttribute(string editorFactoryGuid, string extension) - { - _wrapped = new ProvideLanguageExtensionAttribute(editorFactoryGuid, extension); - } + public ProvideEditorFactoryMappingAttribute(string editorFactoryGuid, string extension) + { + _wrapped = new ProvideLanguageExtensionAttribute(editorFactoryGuid, extension); + } - public override void Register(RegistrationContext context) - { - _wrapped.Register(context); - } + public override void Register(RegistrationContext context) + { + _wrapped.Register(context); + } - public override void Unregister(RegistrationContext context) - { - _wrapped.Unregister(context); - } + public override void Unregister(RegistrationContext context) + { + _wrapped.Unregister(context); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/ProjectType.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/ProjectType.cs index 11aa5b92a7..29b2a8410e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/ProjectType.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/ProjectType.cs @@ -2,76 +2,75 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +/// +/// Represents the managed project types in Visual Studio. +/// +/// +/// A project type points to a factory that implements and is responsible for creating an in-memory +/// representation (implementing , ) that features through out Visual Studio +/// interact with. +/// +internal static class ProjectType { /// - /// Represents the managed project types in Visual Studio. + /// A representing the Visual Basic project type based on the Common Project System (CPS). /// - /// - /// A project type points to a factory that implements and is responsible for creating an in-memory - /// representation (implementing , ) that features through out Visual Studio - /// interact with. - /// - internal static class ProjectType - { - /// - /// A representing the Visual Basic project type based on the Common Project System (CPS). - /// - public const string VisualBasic = "778DAE3C-4631-46EA-AA77-85C1314464D9"; + public const string VisualBasic = "778DAE3C-4631-46EA-AA77-85C1314464D9"; - /// - /// A representing the legacy Visual Basic project type based on the native project system in msvbprj.dll. - /// - public const string LegacyVisualBasic = "F184B08F-C81C-45F6-A57F-5ABD9991F28F"; + /// + /// A representing the legacy Visual Basic project type based on the native project system in msvbprj.dll. + /// + public const string LegacyVisualBasic = "F184B08F-C81C-45F6-A57F-5ABD9991F28F"; - /// - /// A representing the legacy Visual Basic project type based on the native project system in msvbprj.dll. - /// - public static readonly Guid LegacyVisualBasicGuid = new(LegacyVisualBasic); + /// + /// A representing the legacy Visual Basic project type based on the native project system in msvbprj.dll. + /// + public static readonly Guid LegacyVisualBasicGuid = new(LegacyVisualBasic); - /// - /// A representing the C# project type based on the Common Project System (CPS). - /// - public const string CSharp = "9A19103F-16F7-4668-BE54-9A1E7A4F7556"; + /// + /// A representing the C# project type based on the Common Project System (CPS). + /// + public const string CSharp = "9A19103F-16F7-4668-BE54-9A1E7A4F7556"; - /// - /// A representing the C# project type based on the Common Project System (CPS). - /// - public static readonly Guid CSharpGuid = new(CSharp); + /// + /// A representing the C# project type based on the Common Project System (CPS). + /// + public static readonly Guid CSharpGuid = new(CSharp); - /// - /// A representing the legacy C# project type based on the native project system in csproj.dll. - /// - public const string LegacyCSharp = "FAE04EC0-301F-11d3-BF4B-00C04F79EFBC"; + /// + /// A representing the legacy C# project type based on the native project system in csproj.dll. + /// + public const string LegacyCSharp = "FAE04EC0-301F-11d3-BF4B-00C04F79EFBC"; - /// - /// A representing the legacy C# project type based on the native project system in csproj.dll. - /// - public static readonly Guid LegacyCSharpGuid = new(LegacyCSharp); + /// + /// A representing the legacy C# project type based on the native project system in csproj.dll. + /// + public static readonly Guid LegacyCSharpGuid = new(LegacyCSharp); - /// - /// A representing the F# project type based on the Common Project System (CPS). - /// - public const string FSharp = "6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705"; + /// + /// A representing the F# project type based on the Common Project System (CPS). + /// + public const string FSharp = "6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705"; - /// - /// A representing the F# project type based on the Common Project System (CPS). - /// - public static readonly Guid FSharpGuid = new(FSharp); + /// + /// A representing the F# project type based on the Common Project System (CPS). + /// + public static readonly Guid FSharpGuid = new(FSharp); - /// - /// A representing the legacy F# project type based on the Managed Package Framework (MPF). - /// - public const string LegacyFSharp = "F2A71F9B-5D33-465A-A702-920D77279786"; + /// + /// A representing the legacy F# project type based on the Managed Package Framework (MPF). + /// + public const string LegacyFSharp = "F2A71F9B-5D33-465A-A702-920D77279786"; - /// - /// A representing the legacy F# project type based on the Managed Package Framework (MPF). - /// - public static readonly Guid LegacyFSharpGuid = new(LegacyFSharp); + /// + /// A representing the legacy F# project type based on the Managed Package Framework (MPF). + /// + public static readonly Guid LegacyFSharpGuid = new(LegacyFSharp); - /// - /// A representing the deprecated C# (xproj) project type based on the Common Project System (CPS). - /// - public const string LegacyXProj = "8BB2217D-0F2D-49D1-97BC-3654ED321F3B"; - } + /// + /// A representing the deprecated C# (xproj) project type based on the Common Project System (CPS). + /// + public const string LegacyXProj = "8BB2217D-0F2D-49D1-97BC-3654ED321F3B"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/AbstractExtenderCATIDProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/AbstractExtenderCATIDProvider.cs index f97427166d..6cc579959f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/AbstractExtenderCATIDProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/AbstractExtenderCATIDProvider.cs @@ -3,109 +3,108 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Properties; using BCLDebug = System.Diagnostics.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +/// +/// An abstract class that maps to a more correct model. +/// +internal abstract class AbstractExtenderCATIDProvider : IExtenderCATIDProvider { - /// - /// An abstract class that maps to a more correct model. - /// - internal abstract class AbstractExtenderCATIDProvider : IExtenderCATIDProvider + public string? GetExtenderCATID(ExtenderCATIDType extenderCATIDType, IProjectTree? treeNode) { - public string? GetExtenderCATID(ExtenderCATIDType extenderCATIDType, IProjectTree? treeNode) + // CPS's implementation of ExtenderCATIDType incorrectly treats the same "instances" as distinct items based + // where they are accessed in CPS. It also incorrectly maps "HierarchyExtensionObject" and "HierarchyBrowseObject" + // as only applying to the hierarchy, when they take ITEMIDs indicating the node they apply to. + // + // See https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/extending-the-object-model-of-the-base-project. + // + // The latter issue we can do nothing about, however, to address the former, map these types to a truer form it makes + // it easier on implementors and maintainers of this to understand the objects we're talking about. + + return extenderCATIDType switch { - // CPS's implementation of ExtenderCATIDType incorrectly treats the same "instances" as distinct items based - // where they are accessed in CPS. It also incorrectly maps "HierarchyExtensionObject" and "HierarchyBrowseObject" - // as only applying to the hierarchy, when they take ITEMIDs indicating the node they apply to. - // - // See https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/extending-the-object-model-of-the-base-project. - // - // The latter issue we can do nothing about, however, to address the former, map these types to a truer form it makes - // it easier on implementors and maintainers of this to understand the objects we're talking about. - - return extenderCATIDType switch - { - ExtenderCATIDType.HierarchyExtensionObject or // IVsHierarchy.GetProperty(VSITEMID.Root, VSHPROPID_ExtObjectCATID) - ExtenderCATIDType.AutomationProject => // DTE.Project - GetExtenderCATID(ExtendeeObject.Project), - - ExtenderCATIDType.HierarchyBrowseObject or // IVsHierarchy.GetProperty(VSHPROPID_BrowseObjectCATID) - ExtenderCATIDType.ProjectBrowseObject => // EnvDTE.Project.Properties - GetExtenderCATID(ExtendeeObject.ProjectBrowseObject), - - ExtenderCATIDType.ConfigurationBrowseObject => // IVsCfgProvider2.GetCfgProviderProperty(VSCFGPROPID_IntrinsicExtenderCATID)/DTE.Configuration - GetExtenderCATID(ExtendeeObject.Configuration), - - ExtenderCATIDType.HierarchyConfigurationBrowseObject or // IVsHierarchy.GetProperty(VSHPROPID_CfgBrowseObjectCATID) - ExtenderCATIDType.ProjectConfigurationBrowseObject => // EnvDTE.Configuration.Properties - GetExtenderCATID(ExtendeeObject.ConfigurationBrowseObject), - - ExtenderCATIDType.AutomationProjectItem => // EnvDTE.ProjectItem - GetExtenderCATID(ExtendeeObject.ProjectItem), - - ExtenderCATIDType.AutomationReference or // VSLangProject.Reference - ExtenderCATIDType.ReferenceBrowseObject => // EnvDTE.ProjectItem.Properties (when reference) - GetExtenderCATID(ExtendeeObject.ReferenceBrowseObject), - - ExtenderCATIDType.FileBrowseObject => // EnvDTE.ProjectItem.Properties (when file) - GetExtenderCATID(ExtendeeObject.FileBrowseObject), - - ExtenderCATIDType.AutomationFolderProperties or // FolderProperties - ExtenderCATIDType.FolderBrowseObject => // EnvDTE.ProjectItem.Properties (when folder) - GetExtenderCATID(ExtendeeObject.FolderBrowseObject), - - ExtenderCATIDType.Unknown or _ => // EnvDTE.ProjectItem.Properties (when not file, folder, reference) - UnknownOrDefault() - }; - - string? UnknownOrDefault() - { - BCLDebug.Assert(extenderCATIDType == ExtenderCATIDType.Unknown, $"Unrecognized CATID type {extenderCATIDType}"); - return null; - } - } + ExtenderCATIDType.HierarchyExtensionObject or // IVsHierarchy.GetProperty(VSITEMID.Root, VSHPROPID_ExtObjectCATID) + ExtenderCATIDType.AutomationProject => // DTE.Project + GetExtenderCATID(ExtendeeObject.Project), + + ExtenderCATIDType.HierarchyBrowseObject or // IVsHierarchy.GetProperty(VSHPROPID_BrowseObjectCATID) + ExtenderCATIDType.ProjectBrowseObject => // EnvDTE.Project.Properties + GetExtenderCATID(ExtendeeObject.ProjectBrowseObject), + + ExtenderCATIDType.ConfigurationBrowseObject => // IVsCfgProvider2.GetCfgProviderProperty(VSCFGPROPID_IntrinsicExtenderCATID)/DTE.Configuration + GetExtenderCATID(ExtendeeObject.Configuration), + + ExtenderCATIDType.HierarchyConfigurationBrowseObject or // IVsHierarchy.GetProperty(VSHPROPID_CfgBrowseObjectCATID) + ExtenderCATIDType.ProjectConfigurationBrowseObject => // EnvDTE.Configuration.Properties + GetExtenderCATID(ExtendeeObject.ConfigurationBrowseObject), + + ExtenderCATIDType.AutomationProjectItem => // EnvDTE.ProjectItem + GetExtenderCATID(ExtendeeObject.ProjectItem), + + ExtenderCATIDType.AutomationReference or // VSLangProject.Reference + ExtenderCATIDType.ReferenceBrowseObject => // EnvDTE.ProjectItem.Properties (when reference) + GetExtenderCATID(ExtendeeObject.ReferenceBrowseObject), - protected abstract string GetExtenderCATID(ExtendeeObject extendee); + ExtenderCATIDType.FileBrowseObject => // EnvDTE.ProjectItem.Properties (when file) + GetExtenderCATID(ExtendeeObject.FileBrowseObject), - protected enum ExtendeeObject + ExtenderCATIDType.AutomationFolderProperties or // FolderProperties + ExtenderCATIDType.FolderBrowseObject => // EnvDTE.ProjectItem.Properties (when folder) + GetExtenderCATID(ExtendeeObject.FolderBrowseObject), + + ExtenderCATIDType.Unknown or _ => // EnvDTE.ProjectItem.Properties (when not file, folder, reference) + UnknownOrDefault() + }; + + string? UnknownOrDefault() { - /// - /// Represents . - /// - Project, - - /// - /// Represents . - /// - ProjectBrowseObject, - - /// - /// Represents . - /// - Configuration, - - /// - /// Represents . - /// - ConfigurationBrowseObject, - - /// - /// Represents . - /// - ProjectItem, - - /// - /// Represents when a file. - /// - FileBrowseObject, - - /// - /// Represents when a folder. - /// - FolderBrowseObject, - - /// - /// Represents or when a reference. - /// - ReferenceBrowseObject, + BCLDebug.Assert(extenderCATIDType == ExtenderCATIDType.Unknown, $"Unrecognized CATID type {extenderCATIDType}"); + return null; } } + + protected abstract string GetExtenderCATID(ExtendeeObject extendee); + + protected enum ExtendeeObject + { + /// + /// Represents . + /// + Project, + + /// + /// Represents . + /// + ProjectBrowseObject, + + /// + /// Represents . + /// + Configuration, + + /// + /// Represents . + /// + ConfigurationBrowseObject, + + /// + /// Represents . + /// + ProjectItem, + + /// + /// Represents when a file. + /// + FileBrowseObject, + + /// + /// Represents when a folder. + /// + FolderBrowseObject, + + /// + /// Represents or when a reference. + /// + ReferenceBrowseObject, + } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProvider.cs index 17b817e8af..fa90f16b96 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProvider.cs @@ -4,36 +4,35 @@ using VSLangProj; using BCLDebug = System.Diagnostics.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.CSharp; + +[Export(typeof(IExtenderCATIDProvider))] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpExtenderCATIDProvider : AbstractExtenderCATIDProvider { - [Export(typeof(IExtenderCATIDProvider))] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpExtenderCATIDProvider : AbstractExtenderCATIDProvider + [ImportingConstructor] + public CSharpExtenderCATIDProvider() { - [ImportingConstructor] - public CSharpExtenderCATIDProvider() - { - } + } - protected override string GetExtenderCATID(ExtendeeObject extendee) + protected override string GetExtenderCATID(ExtendeeObject extendee) + { + return extendee switch { - return extendee switch - { - ExtendeeObject.Project => PrjCATID.prjCATIDProject, - ExtendeeObject.ProjectBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject, - ExtendeeObject.Configuration => PrjBrowseObjectCATID.prjCATIDCSharpConfig, - ExtendeeObject.ConfigurationBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpProjectConfigBrowseObject, - ExtendeeObject.ProjectItem => PrjCATID.prjCATIDProjectItem, - ExtendeeObject.FolderBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpFolderBrowseObject, - ExtendeeObject.ReferenceBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpReferenceBrowseObject, - ExtendeeObject.FileBrowseObject or _ => FileBrowseObjectOrDefault() - }; + ExtendeeObject.Project => PrjCATID.prjCATIDProject, + ExtendeeObject.ProjectBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject, + ExtendeeObject.Configuration => PrjBrowseObjectCATID.prjCATIDCSharpConfig, + ExtendeeObject.ConfigurationBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpProjectConfigBrowseObject, + ExtendeeObject.ProjectItem => PrjCATID.prjCATIDProjectItem, + ExtendeeObject.FolderBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpFolderBrowseObject, + ExtendeeObject.ReferenceBrowseObject => PrjBrowseObjectCATID.prjCATIDCSharpReferenceBrowseObject, + ExtendeeObject.FileBrowseObject or _ => FileBrowseObjectOrDefault() + }; - string FileBrowseObjectOrDefault() - { - BCLDebug.Assert(extendee == ExtendeeObject.FileBrowseObject); - return PrjBrowseObjectCATID.prjCATIDCSharpFileBrowseObject; - } + string FileBrowseObjectOrDefault() + { + BCLDebug.Assert(extendee == ExtendeeObject.FileBrowseObject); + return PrjBrowseObjectCATID.prjCATIDCSharpFileBrowseObject; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetNamespaceImportsList.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetNamespaceImportsList.cs index 9a90797747..939ebbae33 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetNamespaceImportsList.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetNamespaceImportsList.cs @@ -4,214 +4,213 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +[Export(typeof(DotNetNamespaceImportsList))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] +internal class DotNetNamespaceImportsList : UnconfiguredProjectHostBridge, IProjectVersionedValue>, IProjectVersionedValue>>, IEnumerable { - [Export(typeof(DotNetNamespaceImportsList))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] - internal class DotNetNamespaceImportsList : UnconfiguredProjectHostBridge, IProjectVersionedValue>, IProjectVersionedValue>>, IEnumerable + private static readonly ImmutableHashSet s_namespaceImportRule = Empty.OrdinalIgnoreCaseStringSet + .Add(NamespaceImport.SchemaName); + + private readonly IActiveConfiguredProjectSubscriptionService _activeConfiguredProjectSubscriptionService; + + /// + /// For unit testing purposes, to avoid having to mock all of CPS + /// + internal bool SkipInitialization { get; set; } + + [ImportingConstructor] + public DotNetNamespaceImportsList( + UnconfiguredProject project, + IProjectThreadingService threadingService, + IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscriptionService) + : base(threadingService.JoinableTaskContext) { - private static readonly ImmutableHashSet s_namespaceImportRule = Empty.OrdinalIgnoreCaseStringSet - .Add(NamespaceImport.SchemaName); - - private readonly IActiveConfiguredProjectSubscriptionService _activeConfiguredProjectSubscriptionService; - - /// - /// For unit testing purposes, to avoid having to mock all of CPS - /// - internal bool SkipInitialization { get; set; } - - [ImportingConstructor] - public DotNetNamespaceImportsList( - UnconfiguredProject project, - IProjectThreadingService threadingService, - IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscriptionService) - : base(threadingService.JoinableTaskContext) - { - _activeConfiguredProjectSubscriptionService = activeConfiguredProjectSubscriptionService; - } + _activeConfiguredProjectSubscriptionService = activeConfiguredProjectSubscriptionService; + } - [Import(typeof(DotNetVSImports))] - internal Lazy VSImports { get; set; } = null!; + [Import(typeof(DotNetVSImports))] + internal Lazy VSImports { get; set; } = null!; - /// - /// Get the global project collection version number, so we can make sure we are waiting for the latest build after a dependent project is updated. - /// - [Import(ContractNames.MSBuild.GlobalProjectCollectionGlobalProperties, typeof(IProjectGlobalPropertiesProvider))] - private IProjectGlobalPropertiesProvider GlobalProjectCollectionWatcher { get; set; } = null!; + /// + /// Get the global project collection version number, so we can make sure we are waiting for the latest build after a dependent project is updated. + /// + [Import(ContractNames.MSBuild.GlobalProjectCollectionGlobalProperties, typeof(IProjectGlobalPropertiesProvider))] + private IProjectGlobalPropertiesProvider GlobalProjectCollectionWatcher { get; set; } = null!; - private void TryInitialize() + private void TryInitialize() + { + if (!SkipInitialization) { - if (!SkipInitialization) - { - Initialize(); - } + Initialize(); } + } - internal int Count + internal int Count + { + get { - get - { - TryInitialize(); + TryInitialize(); - Assumes.NotNull(AppliedValue); + Assumes.NotNull(AppliedValue); - return AppliedValue.Value.Count; - } + return AppliedValue.Value.Count; } + } - /// - /// Returns an enumerator for the list of imports. - /// - public IEnumerator GetEnumerator() - { - TryInitialize(); + /// + /// Returns an enumerator for the list of imports. + /// + public IEnumerator GetEnumerator() + { + TryInitialize(); - Assumes.NotNull(AppliedValue); + Assumes.NotNull(AppliedValue); - return AppliedValue.Value.GetEnumerator(); - } + return AppliedValue.Value.GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - protected override async Task ApplyAsync(IProjectVersionedValue> value) - { - await JoinableFactory.SwitchToMainThreadAsync(); + protected override async Task ApplyAsync(IProjectVersionedValue> value) + { + await JoinableFactory.SwitchToMainThreadAsync(); - IProjectVersionedValue>? previous = AppliedValue; + IProjectVersionedValue>? previous = AppliedValue; - AppliedValue = value; + AppliedValue = value; + + // To avoid callers seeing an inconsistent state where there are no Imports, + // we use BlockInitializeOnFirstAppliedValue to block on the first value + // being applied. + // + // Due to that, and to avoid a deadlock when event handlers call back into us + // while we're still initializing, we avoid firing the events the first time + // a value is applied. + if (previous is not null) + { + DotNetVSImports imports = VSImports.Value; + ImmutableList currentValue = value.Value; + ImmutableList previousValue = previous.Value; - // To avoid callers seeing an inconsistent state where there are no Imports, - // we use BlockInitializeOnFirstAppliedValue to block on the first value - // being applied. - // - // Due to that, and to avoid a deadlock when event handlers call back into us - // while we're still initializing, we avoid firing the events the first time - // a value is applied. - if (previous is not null) + foreach (string import in previousValue.Except(currentValue)) { - DotNetVSImports imports = VSImports.Value; - ImmutableList currentValue = value.Value; - ImmutableList previousValue = previous.Value; - - foreach (string import in previousValue.Except(currentValue)) - { - imports.OnImportRemoved(import); - } - - foreach (string import in currentValue.Except(previousValue)) - { - imports.OnImportAdded(import); - } + imports.OnImportRemoved(import); + } + + foreach (string import in currentValue.Except(previousValue)) + { + imports.OnImportAdded(import); } } + } - protected override Task>?> PreprocessAsync(IProjectVersionedValue input, IProjectVersionedValue>? previousOutput) - { - IProjectChangeDescription projectChange = input.Value.ProjectChanges[NamespaceImport.SchemaName]; + protected override Task>?> PreprocessAsync(IProjectVersionedValue input, IProjectVersionedValue>? previousOutput) + { + IProjectChangeDescription projectChange = input.Value.ProjectChanges[NamespaceImport.SchemaName]; - return Task.FromResult>?>( - new ProjectVersionedValue>( - projectChange.After.Items.Keys.ToImmutableList(), - input.DataSourceVersions)); - } + return Task.FromResult>?>( + new ProjectVersionedValue>( + projectChange.After.Items.Keys.ToImmutableList(), + input.DataSourceVersions)); + } - protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) - { - JoinUpstreamDataSources(_activeConfiguredProjectSubscriptionService.ProjectRuleSource); + protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) + { + JoinUpstreamDataSources(_activeConfiguredProjectSubscriptionService.ProjectRuleSource); - //set up a subscription to listen for namespace import changes - return _activeConfiguredProjectSubscriptionService.ProjectRuleSource.SourceBlock.LinkTo(targetBlock, - linkOptions: DataflowOption.PropagateCompletion, - ruleNames: s_namespaceImportRule); - } + //set up a subscription to listen for namespace import changes + return _activeConfiguredProjectSubscriptionService.ProjectRuleSource.SourceBlock.LinkTo(targetBlock, + linkOptions: DataflowOption.PropagateCompletion, + ruleNames: s_namespaceImportRule); + } - protected override Task InitializeInnerCoreAsync(CancellationToken cancellationToken) => Task.CompletedTask; + protected override Task InitializeInnerCoreAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// - /// Receive latest snapshot from project subscription service. - /// - /// returns a task, which the caller could await for - internal async Task>> ReceiveLatestSnapshotAsync() + /// + /// Receive latest snapshot from project subscription service. + /// + /// returns a task, which the caller could await for + internal async Task>> ReceiveLatestSnapshotAsync() + { + await InitializeAsync(); + while (true) { - await InitializeAsync(); - while (true) + try { - try - { - var minimumRequiredDataSourceVersions = ActiveConfiguredProjectProvider.ActiveConfiguredProject?.CreateVersionRequirement(allowMissingData: false).ToBuilder(); - - Assumes.NotNull(minimumRequiredDataSourceVersions); - - IComparable? latestProjectCollectionVersion = GlobalProjectCollectionWatcher.DataSourceVersion; - minimumRequiredDataSourceVersions.Add( - ProjectDataSources.GlobalProjectCollectionGlobalProperties, - new ProjectVersionRequirement(latestProjectCollectionVersion!, allowMissingData: true)); - - return await ApplyAsync(minimumRequiredDataSourceVersions.ToImmutable(), default); - } - catch (ActiveProjectConfigurationChangedException) - { - // The active project config has changed since we started. Recalculate the data source versions - // we need and start waiting over again. - } - } - } + var minimumRequiredDataSourceVersions = ActiveConfiguredProjectProvider.ActiveConfiguredProject?.CreateVersionRequirement(allowMissingData: false).ToBuilder(); - // lIndex is One-based index - internal string Item(int lIndex) - { - if (lIndex < 1) + Assumes.NotNull(minimumRequiredDataSourceVersions); + + IComparable? latestProjectCollectionVersion = GlobalProjectCollectionWatcher.DataSourceVersion; + minimumRequiredDataSourceVersions.Add( + ProjectDataSources.GlobalProjectCollectionGlobalProperties, + new ProjectVersionRequirement(latestProjectCollectionVersion!, allowMissingData: true)); + + return await ApplyAsync(minimumRequiredDataSourceVersions.ToImmutable(), default); + } + catch (ActiveProjectConfigurationChangedException) { - throw new ArgumentException(string.Format("{0} - Index value is less than One.", lIndex), nameof(lIndex)); + // The active project config has changed since we started. Recalculate the data source versions + // we need and start waiting over again. } + } + } - TryInitialize(); + // lIndex is One-based index + internal string Item(int lIndex) + { + if (lIndex < 1) + { + throw new ArgumentException(string.Format("{0} - Index value is less than One.", lIndex), nameof(lIndex)); + } - Assumes.NotNull(AppliedValue); + TryInitialize(); - ImmutableList list = AppliedValue.Value; + Assumes.NotNull(AppliedValue); - if (lIndex > list.Count) - { - throw new ArgumentException(string.Format("{0} - Index value is greater than the length of the namespace import list.", lIndex), nameof(lIndex)); - } + ImmutableList list = AppliedValue.Value; - return list[lIndex - 1]; + if (lIndex > list.Count) + { + throw new ArgumentException(string.Format("{0} - Index value is greater than the length of the namespace import list.", lIndex), nameof(lIndex)); } - internal bool IsPresent(int indexInt) - { - if (indexInt < 1) - { - throw new ArgumentException(string.Format("{0} - Index value is less than One.", indexInt), nameof(indexInt)); - } + return list[lIndex - 1]; + } - TryInitialize(); + internal bool IsPresent(int indexInt) + { + if (indexInt < 1) + { + throw new ArgumentException(string.Format("{0} - Index value is less than One.", indexInt), nameof(indexInt)); + } - Assumes.NotNull(AppliedValue); + TryInitialize(); - if (indexInt > AppliedValue.Value.Count) - { - throw new ArgumentException(string.Format("{0} - Index value is greater than the length of the namespace import list.", indexInt), nameof(indexInt)); - } + Assumes.NotNull(AppliedValue); - return true; + if (indexInt > AppliedValue.Value.Count) + { + throw new ArgumentException(string.Format("{0} - Index value is greater than the length of the namespace import list.", indexInt), nameof(indexInt)); } - internal bool IsPresent(string bstrImport) + return true; + } + + internal bool IsPresent(string bstrImport) + { + if (string.IsNullOrEmpty(bstrImport)) { - if (string.IsNullOrEmpty(bstrImport)) - { - throw new ArgumentException("The string cannot be null or empty", nameof(bstrImport)); - } + throw new ArgumentException("The string cannot be null or empty", nameof(bstrImport)); + } - TryInitialize(); + TryInitialize(); - Assumes.NotNull(AppliedValue); + Assumes.NotNull(AppliedValue); - return AppliedValue.Value.Any(l => string.Equals(bstrImport, l, StringComparisons.ItemNames)); - } + return AppliedValue.Value.Any(l => string.Equals(bstrImport, l, StringComparisons.ItemNames)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetVSImports.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetVSImports.cs index 5fa20f0bd7..b42be330b5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetVSImports.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/DotNetVSImports.cs @@ -5,147 +5,146 @@ using Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +[Export(typeof(Imports))] +[Export(typeof(ImportsEvents))] +[Export(typeof(DotNetVSImports))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +[Order(Order.Default)] +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] +internal class DotNetVSImports : ConnectionPointContainer, + IEventSource<_dispImportsEvents>, + Imports, + ImportsEvents { - [Export(typeof(Imports))] - [Export(typeof(ImportsEvents))] - [Export(typeof(DotNetVSImports))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - [Order(Order.Default)] - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] - internal class DotNetVSImports : ConnectionPointContainer, - IEventSource<_dispImportsEvents>, - Imports, - ImportsEvents + private const string ImportItemTypeName = "Import"; + + private readonly IActiveConfiguredValue _activeConfiguredProject; + private readonly IProjectThreadingService _threadingService; + private readonly IProjectAccessor _projectAccessor; + private readonly VSLangProj.VSProject _vsProject; + private readonly IUnconfiguredProjectVsServices _unconfiguredProjectVSServices; + private readonly DotNetNamespaceImportsList _importsList; + + public event _dispImportsEvents_ImportAddedEventHandler? ImportAdded; + public event _dispImportsEvents_ImportRemovedEventHandler? ImportRemoved; + + [ImportingConstructor] + public DotNetVSImports( + [Import(ExportContractNames.VsTypes.CpsVSProject)] VSLangProj.VSProject vsProject, + IProjectThreadingService threadingService, + IActiveConfiguredValue activeConfiguredProject, + IProjectAccessor projectAccessor, + IUnconfiguredProjectVsServices unconfiguredProjectVSServices, + DotNetNamespaceImportsList importsList) { - private const string ImportItemTypeName = "Import"; - - private readonly IActiveConfiguredValue _activeConfiguredProject; - private readonly IProjectThreadingService _threadingService; - private readonly IProjectAccessor _projectAccessor; - private readonly VSLangProj.VSProject _vsProject; - private readonly IUnconfiguredProjectVsServices _unconfiguredProjectVSServices; - private readonly DotNetNamespaceImportsList _importsList; - - public event _dispImportsEvents_ImportAddedEventHandler? ImportAdded; - public event _dispImportsEvents_ImportRemovedEventHandler? ImportRemoved; - - [ImportingConstructor] - public DotNetVSImports( - [Import(ExportContractNames.VsTypes.CpsVSProject)] VSLangProj.VSProject vsProject, - IProjectThreadingService threadingService, - IActiveConfiguredValue activeConfiguredProject, - IProjectAccessor projectAccessor, - IUnconfiguredProjectVsServices unconfiguredProjectVSServices, - DotNetNamespaceImportsList importsList) - { - _vsProject = vsProject; - _activeConfiguredProject = activeConfiguredProject; - _projectAccessor = projectAccessor; - _threadingService = threadingService; - _unconfiguredProjectVSServices = unconfiguredProjectVSServices; - _importsList = importsList; - - AddEventSource(this); - } + _vsProject = vsProject; + _activeConfiguredProject = activeConfiguredProject; + _projectAccessor = projectAccessor; + _threadingService = threadingService; + _unconfiguredProjectVSServices = unconfiguredProjectVSServices; + _importsList = importsList; + + AddEventSource(this); + } - private ConfiguredProject ConfiguredProject => _activeConfiguredProject.Value; + private ConfiguredProject ConfiguredProject => _activeConfiguredProject.Value; - public string Item(int lIndex) - { - return _importsList.Item(lIndex); - } + public string Item(int lIndex) + { + return _importsList.Item(lIndex); + } - public void Add(string bstrImport) + public void Add(string bstrImport) + { + if (!_importsList.IsPresent(bstrImport)) { - if (!_importsList.IsPresent(bstrImport)) + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => + await _projectAccessor.OpenProjectXmlForWriteAsync(_unconfiguredProjectVSServices.Project, project => { - await _projectAccessor.OpenProjectXmlForWriteAsync(_unconfiguredProjectVSServices.Project, project => - { - project.AddItem(ImportItemTypeName, bstrImport); - }); - - await _importsList.ReceiveLatestSnapshotAsync(); + project.AddItem(ImportItemTypeName, bstrImport); }); - } - else - { - throw new ArgumentException(string.Format("{0} - Namespace is already imported", bstrImport), nameof(bstrImport)); - } + + await _importsList.ReceiveLatestSnapshotAsync(); + }); + } + else + { + throw new ArgumentException(string.Format("{0} - Namespace is already imported", bstrImport), nameof(bstrImport)); } + } - public void Remove(object index) + public void Remove(object index) + { + string? importToRemove = index switch { - string? importToRemove = index switch - { - int indexInt when _importsList.IsPresent(indexInt) => _importsList.Item(indexInt), - string removeImport when _importsList.IsPresent(removeImport) => removeImport, - _ => null - }; + int indexInt when _importsList.IsPresent(indexInt) => _importsList.Item(indexInt), + string removeImport when _importsList.IsPresent(removeImport) => removeImport, + _ => null + }; - if (importToRemove is not null) + if (importToRemove is not null) + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => + await _projectAccessor.OpenProjectForWriteAsync(ConfiguredProject, project => { - await _projectAccessor.OpenProjectForWriteAsync(ConfiguredProject, project => - { - Microsoft.Build.Evaluation.ProjectItem importProjectItem = project.GetItems(ImportItemTypeName) - .First(i => string.Equals(importToRemove, i.EvaluatedInclude, StringComparisons.ItemNames)); - - if (importProjectItem.IsImported) - { - throw new ArgumentException(string.Format(VSResources.ImportsFromTargetCannotBeDeleted, importToRemove), nameof(index)); - } + Microsoft.Build.Evaluation.ProjectItem importProjectItem = project.GetItems(ImportItemTypeName) + .First(i => string.Equals(importToRemove, i.EvaluatedInclude, StringComparisons.ItemNames)); - project.RemoveItem(importProjectItem); - }); + if (importProjectItem.IsImported) + { + throw new ArgumentException(string.Format(VSResources.ImportsFromTargetCannotBeDeleted, importToRemove), nameof(index)); + } - await _importsList.ReceiveLatestSnapshotAsync(); + project.RemoveItem(importProjectItem); }); - } - else - { - throw new ArgumentException(string.Format("{0} - index is neither an Int nor a String, or the Namepsace was not found", index), nameof(index)); - } - } - public IEnumerator GetEnumerator() + await _importsList.ReceiveLatestSnapshotAsync(); + }); + } + else { - return _importsList.GetEnumerator(); + throw new ArgumentException(string.Format("{0} - index is neither an Int nor a String, or the Namepsace was not found", index), nameof(index)); } + } - public DTE DTE => _vsProject.DTE; + public IEnumerator GetEnumerator() + { + return _importsList.GetEnumerator(); + } - public Project ContainingProject => _vsProject.Project; + public DTE DTE => _vsProject.DTE; - public int Count => _importsList.Count; + public Project ContainingProject => _vsProject.Project; - public object Parent => _unconfiguredProjectVSServices.VsHierarchy; + public int Count => _importsList.Count; - internal virtual void OnImportAdded(string importNamespace) - { - ImportAdded?.Invoke(importNamespace); - } + public object Parent => _unconfiguredProjectVSServices.VsHierarchy; - internal virtual void OnImportRemoved(string importNamespace) - { - ImportRemoved?.Invoke(importNamespace); - } + internal virtual void OnImportAdded(string importNamespace) + { + ImportAdded?.Invoke(importNamespace); + } - public void OnSinkAdded(_dispImportsEvents sink) - { - Requires.NotNull(sink); + internal virtual void OnImportRemoved(string importNamespace) + { + ImportRemoved?.Invoke(importNamespace); + } - ImportAdded += new _dispImportsEvents_ImportAddedEventHandler(sink.ImportAdded); - ImportRemoved += new _dispImportsEvents_ImportRemovedEventHandler(sink.ImportRemoved); - } + public void OnSinkAdded(_dispImportsEvents sink) + { + Requires.NotNull(sink); - public void OnSinkRemoved(_dispImportsEvents sink) - { - ImportAdded -= new _dispImportsEvents_ImportAddedEventHandler(sink.ImportAdded); - ImportRemoved -= new _dispImportsEvents_ImportRemovedEventHandler(sink.ImportRemoved); - } + ImportAdded += new _dispImportsEvents_ImportAddedEventHandler(sink.ImportAdded); + ImportRemoved += new _dispImportsEvents_ImportRemovedEventHandler(sink.ImportRemoved); + } + + public void OnSinkRemoved(_dispImportsEvents sink) + { + ImportAdded -= new _dispImportsEvents_ImportAddedEventHandler(sink.ImportAdded); + ImportRemoved -= new _dispImportsEvents_ImportRemovedEventHandler(sink.ImportRemoved); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProvider.cs index 6a7a3db502..22fa19354a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProvider.cs @@ -4,36 +4,35 @@ using VSLangProj; using BCLDebug = System.Diagnostics.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic; + +[Export(typeof(IExtenderCATIDProvider))] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicExtenderCATIDProvider : AbstractExtenderCATIDProvider { - [Export(typeof(IExtenderCATIDProvider))] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicExtenderCATIDProvider : AbstractExtenderCATIDProvider + [ImportingConstructor] + public VisualBasicExtenderCATIDProvider() { - [ImportingConstructor] - public VisualBasicExtenderCATIDProvider() - { - } + } - protected override string GetExtenderCATID(ExtendeeObject extendee) + protected override string GetExtenderCATID(ExtendeeObject extendee) + { + return extendee switch { - return extendee switch - { - ExtendeeObject.Project => PrjCATID.prjCATIDProject, - ExtendeeObject.ProjectBrowseObject => PrjBrowseObjectCATID.prjCATIDVBProjectBrowseObject, - ExtendeeObject.Configuration => PrjBrowseObjectCATID.prjCATIDVBConfig, - ExtendeeObject.ConfigurationBrowseObject => PrjBrowseObjectCATID.prjCATIDVBProjectConfigBrowseObject, - ExtendeeObject.ProjectItem => PrjCATID.prjCATIDProjectItem, - ExtendeeObject.FolderBrowseObject => PrjBrowseObjectCATID.prjCATIDVBFolderBrowseObject, - ExtendeeObject.ReferenceBrowseObject => PrjBrowseObjectCATID.prjCATIDVBReferenceBrowseObject, - ExtendeeObject.FileBrowseObject or _ => FileBrowseObjectOrDefault() - }; + ExtendeeObject.Project => PrjCATID.prjCATIDProject, + ExtendeeObject.ProjectBrowseObject => PrjBrowseObjectCATID.prjCATIDVBProjectBrowseObject, + ExtendeeObject.Configuration => PrjBrowseObjectCATID.prjCATIDVBConfig, + ExtendeeObject.ConfigurationBrowseObject => PrjBrowseObjectCATID.prjCATIDVBProjectConfigBrowseObject, + ExtendeeObject.ProjectItem => PrjCATID.prjCATIDProjectItem, + ExtendeeObject.FolderBrowseObject => PrjBrowseObjectCATID.prjCATIDVBFolderBrowseObject, + ExtendeeObject.ReferenceBrowseObject => PrjBrowseObjectCATID.prjCATIDVBReferenceBrowseObject, + ExtendeeObject.FileBrowseObject or _ => FileBrowseObjectOrDefault() + }; - string FileBrowseObjectOrDefault() - { - BCLDebug.Assert(extendee == ExtendeeObject.FileBrowseObject); - return PrjBrowseObjectCATID.prjCATIDVBFileBrowseObject; - } + string FileBrowseObjectOrDefault() + { + BCLDebug.Assert(extendee == ExtendeeObject.FileBrowseObject); + return PrjBrowseObjectCATID.prjCATIDVBFileBrowseObject; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicVSProjectEvents.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicVSProjectEvents.cs index 0dde74e0d2..15c979b0c9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicVSProjectEvents.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VisualBasic/VisualBasicVSProjectEvents.cs @@ -2,31 +2,30 @@ using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic; + +[Export(typeof(VSProjectEvents))] +[AppliesTo(ProjectCapability.VisualBasic)] +[Order(Order.Default)] +internal class VisualBasicVSProjectEvents : VSProjectEvents { - [Export(typeof(VSProjectEvents))] - [AppliesTo(ProjectCapability.VisualBasic)] - [Order(Order.Default)] - internal class VisualBasicVSProjectEvents : VSProjectEvents - { - private readonly VSLangProj.VSProject _vsProject; + private readonly VSLangProj.VSProject _vsProject; - [ImportingConstructor] - public VisualBasicVSProjectEvents( - [Import(ExportContractNames.VsTypes.CpsVSProject)] VSLangProj.VSProject vsProject, - UnconfiguredProject project) - { - _vsProject = vsProject; - ImportsEventsImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + [ImportingConstructor] + public VisualBasicVSProjectEvents( + [Import(ExportContractNames.VsTypes.CpsVSProject)] VSLangProj.VSProject vsProject, + UnconfiguredProject project) + { + _vsProject = vsProject; + ImportsEventsImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - protected OrderPrecedenceImportCollection ImportsEventsImpl { get; set; } + [ImportMany] + protected OrderPrecedenceImportCollection ImportsEventsImpl { get; set; } - public ReferencesEvents ReferencesEvents => _vsProject.Events.ReferencesEvents; + public ReferencesEvents ReferencesEvents => _vsProject.Events.ReferencesEvents; - public BuildManagerEvents BuildManagerEvents => _vsProject.Events.BuildManagerEvents; + public BuildManagerEvents BuildManagerEvents => _vsProject.Events.BuildManagerEvents; - public ImportsEvents ImportsEvents => ImportsEventsImpl.First().Value; - } + public ImportsEvents ImportsEvents => ImportsEventsImpl.First().Value; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsBuildManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsBuildManager.cs index 54d3b7a547..6b492b0a80 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsBuildManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsBuildManager.cs @@ -4,134 +4,133 @@ using Microsoft.VisualStudio.ProjectSystem.VS.TempPE; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +/// +/// Manages the portable executable (PE) files produced by running custom tools. +/// +[Export(typeof(BuildManager))] +[Export(typeof(VSBuildManager))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +[Order(Order.Default)] +internal class VSBuildManager : ConnectionPointContainer, + IEventSource<_dispBuildManagerEvents>, + BuildManager, + BuildManagerEvents { + private readonly IProjectThreadingService _threadingService; + private readonly IUnconfiguredProjectCommonServices _unconfiguredProjectServices; + /// - /// Manages the portable executable (PE) files produced by running custom tools. + /// Initializes a new instance of the class. /// - [Export(typeof(BuildManager))] - [Export(typeof(VSBuildManager))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - [Order(Order.Default)] - internal class VSBuildManager : ConnectionPointContainer, - IEventSource<_dispBuildManagerEvents>, - BuildManager, - BuildManagerEvents + [ImportingConstructor] + internal VSBuildManager(IProjectThreadingService threadingService, IUnconfiguredProjectCommonServices unconfiguredProjectServices) { - private readonly IProjectThreadingService _threadingService; - private readonly IUnconfiguredProjectCommonServices _unconfiguredProjectServices; - - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - internal VSBuildManager(IProjectThreadingService threadingService, IUnconfiguredProjectCommonServices unconfiguredProjectServices) - { - AddEventSource(this); - _threadingService = threadingService; - _unconfiguredProjectServices = unconfiguredProjectServices; - Project = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: unconfiguredProjectServices.Project); - } + AddEventSource(this); + _threadingService = threadingService; + _unconfiguredProjectServices = unconfiguredProjectServices; + Project = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: unconfiguredProjectServices.Project); + } - [ImportMany(ExportContractNames.VsTypes.VSProject)] - internal OrderPrecedenceImportCollection Project { get; } - - // This has to be a property import to prevent a circular dependency as the bridge imports this class in order to fire events - [Import] - internal Lazy? DesignTimeInputsBuildManagerBridge { get; private set; } - - /// - /// Occurs when a design time output moniker is deleted. - /// - public event _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler? DesignTimeOutputDeleted; - - /// - /// Occurs when a design time output moniker is dirty - /// - public event _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler? DesignTimeOutputDirty; - - /// - /// Gets the project of which the selected item is a part. - /// - public EnvDTE.Project? ContainingProject => Project.FirstOrDefault()?.Value.Project; - - /// - /// Gets the top-level extensibility object. - /// - public EnvDTE.DTE? DTE => Project.FirstOrDefault()?.Value.DTE; - - /// - /// Gets the immediate parent object of a given object. - /// - public object? Parent => Project.FirstOrDefault()?.Value; - - /// - /// Gets the temporary portable executable (PE) monikers for a project. - /// - public object DesignTimeOutputMonikers - { - get - { - if (DesignTimeInputsBuildManagerBridge?.AppliesTo(_unconfiguredProjectServices.Project.Capabilities) == true) - { - IDesignTimeInputsBuildManagerBridge bridge = DesignTimeInputsBuildManagerBridge.Value; + [ImportMany(ExportContractNames.VsTypes.VSProject)] + internal OrderPrecedenceImportCollection Project { get; } - // We don't need to thread switch here because if the caller is on the UI thread then everything is fine - // and if the caller is on a background thread, switching us to the UI thread doesn't provide any guarantees to it. - // It would mean the bridges state can't change, but it only reads the state once, and thats not our responsibility anyway. - return _threadingService.ExecuteSynchronously(bridge.GetDesignTimeOutputMonikersAsync); - } + // This has to be a property import to prevent a circular dependency as the bridge imports this class in order to fire events + [Import] + internal Lazy? DesignTimeInputsBuildManagerBridge { get; private set; } - throw new NotImplementedException(); - } - } + /// + /// Occurs when a design time output moniker is deleted. + /// + public event _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler? DesignTimeOutputDeleted; + + /// + /// Occurs when a design time output moniker is dirty + /// + public event _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler? DesignTimeOutputDirty; - /// - /// Builds a temporary portable executable (PE) and returns its description in an XML string. - /// - public string BuildDesignTimeOutput(string bstrOutputMoniker) + /// + /// Gets the project of which the selected item is a part. + /// + public EnvDTE.Project? ContainingProject => Project.FirstOrDefault()?.Value.Project; + + /// + /// Gets the top-level extensibility object. + /// + public EnvDTE.DTE? DTE => Project.FirstOrDefault()?.Value.DTE; + + /// + /// Gets the immediate parent object of a given object. + /// + public object? Parent => Project.FirstOrDefault()?.Value; + + /// + /// Gets the temporary portable executable (PE) monikers for a project. + /// + public object DesignTimeOutputMonikers + { + get { if (DesignTimeInputsBuildManagerBridge?.AppliesTo(_unconfiguredProjectServices.Project.Capabilities) == true) { IDesignTimeInputsBuildManagerBridge bridge = DesignTimeInputsBuildManagerBridge.Value; - // See comment above about why we don't need any thread switching here. - return _threadingService.ExecuteSynchronously(() => bridge.BuildDesignTimeOutputAsync(bstrOutputMoniker)); + // We don't need to thread switch here because if the caller is on the UI thread then everything is fine + // and if the caller is on a background thread, switching us to the UI thread doesn't provide any guarantees to it. + // It would mean the bridges state can't change, but it only reads the state once, and thats not our responsibility anyway. + return _threadingService.ExecuteSynchronously(bridge.GetDesignTimeOutputMonikersAsync); } throw new NotImplementedException(); } + } - void IEventSource<_dispBuildManagerEvents>.OnSinkAdded(_dispBuildManagerEvents sink) + /// + /// Builds a temporary portable executable (PE) and returns its description in an XML string. + /// + public string BuildDesignTimeOutput(string bstrOutputMoniker) + { + if (DesignTimeInputsBuildManagerBridge?.AppliesTo(_unconfiguredProjectServices.Project.Capabilities) == true) { - DesignTimeOutputDeleted += new _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler(sink.DesignTimeOutputDeleted); - DesignTimeOutputDirty += new _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler(sink.DesignTimeOutputDirty); - } + IDesignTimeInputsBuildManagerBridge bridge = DesignTimeInputsBuildManagerBridge.Value; - void IEventSource<_dispBuildManagerEvents>.OnSinkRemoved(_dispBuildManagerEvents sink) - { - DesignTimeOutputDeleted -= new _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler(sink.DesignTimeOutputDeleted); - DesignTimeOutputDirty -= new _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler(sink.DesignTimeOutputDirty); + // See comment above about why we don't need any thread switching here. + return _threadingService.ExecuteSynchronously(() => bridge.BuildDesignTimeOutputAsync(bstrOutputMoniker)); } - /// - /// Occurs when a design time output moniker is deleted. - /// - internal virtual void OnDesignTimeOutputDeleted(string outputMoniker) - { - _threadingService.VerifyOnUIThread(); + throw new NotImplementedException(); + } - DesignTimeOutputDeleted?.Invoke(outputMoniker); - } + void IEventSource<_dispBuildManagerEvents>.OnSinkAdded(_dispBuildManagerEvents sink) + { + DesignTimeOutputDeleted += new _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler(sink.DesignTimeOutputDeleted); + DesignTimeOutputDirty += new _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler(sink.DesignTimeOutputDirty); + } - /// - /// Occurs when a design time output moniker is dirty. - /// - internal virtual void OnDesignTimeOutputDirty(string outputMoniker) - { - _threadingService.VerifyOnUIThread(); + void IEventSource<_dispBuildManagerEvents>.OnSinkRemoved(_dispBuildManagerEvents sink) + { + DesignTimeOutputDeleted -= new _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler(sink.DesignTimeOutputDeleted); + DesignTimeOutputDirty -= new _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler(sink.DesignTimeOutputDirty); + } - DesignTimeOutputDirty?.Invoke(outputMoniker); - } + /// + /// Occurs when a design time output moniker is deleted. + /// + internal virtual void OnDesignTimeOutputDeleted(string outputMoniker) + { + _threadingService.VerifyOnUIThread(); + + DesignTimeOutputDeleted?.Invoke(outputMoniker); + } + + /// + /// Occurs when a design time output moniker is dirty. + /// + internal virtual void OnDesignTimeOutputDirty(string outputMoniker) + { + _threadingService.VerifyOnUIThread(); + + DesignTimeOutputDirty?.Invoke(outputMoniker); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject.cs index 3451616346..a0b6498a70 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject.cs @@ -5,132 +5,131 @@ using Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +/// +/// imports provided by CPS +/// and wraps it into an object that implements both and +/// . This enables us to provide +/// ProjectProperties to the Project Property Pages and maintain Backward Compatibility. +/// +/// +/// This implementation of VSProject just redirects the VSProject call to the contained +/// VSProject object imported from CPS +/// +[Export(ExportContractNames.VsTypes.VSProject, typeof(VSLangProj.VSProject))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +[Order(Order.Default)] +public partial class VSProject : VSLangProj.VSProject, IConnectionPointContainer, IEventSource { - /// - /// imports provided by CPS - /// and wraps it into an object that implements both and - /// . This enables us to provide - /// ProjectProperties to the Project Property Pages and maintain Backward Compatibility. - /// - /// - /// This implementation of VSProject just redirects the VSProject call to the contained - /// VSProject object imported from CPS - /// - [Export(ExportContractNames.VsTypes.VSProject, typeof(VSLangProj.VSProject))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - [Order(Order.Default)] - public partial class VSProject : VSLangProj.VSProject, IConnectionPointContainer, IEventSource + private readonly VSLangProj.VSProject _vsProject; + private readonly IProjectThreadingService _threadingService; + private readonly IActiveConfiguredValue _projectProperties; + private readonly BuildManager _buildManager; + + [ImportingConstructor] + internal VSProject( + [Import(ExportContractNames.VsTypes.CpsVSProject)] VSLangProj.VSProject vsProject, + IProjectThreadingService threadingService, + IActiveConfiguredValue projectProperties, + UnconfiguredProject project, + BuildManager buildManager) { - private readonly VSLangProj.VSProject _vsProject; - private readonly IProjectThreadingService _threadingService; - private readonly IActiveConfiguredValue _projectProperties; - private readonly BuildManager _buildManager; + _vsProject = vsProject; + _threadingService = threadingService; + _projectProperties = projectProperties; + _buildManager = buildManager; - [ImportingConstructor] - internal VSProject( - [Import(ExportContractNames.VsTypes.CpsVSProject)] VSLangProj.VSProject vsProject, - IProjectThreadingService threadingService, - IActiveConfiguredValue projectProperties, - UnconfiguredProject project, - BuildManager buildManager) - { - _vsProject = vsProject; - _threadingService = threadingService; - _projectProperties = projectProperties; - _buildManager = buildManager; - - ImportsImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - VSProjectEventsImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } - - [ImportMany] - internal OrderPrecedenceImportCollection ImportsImpl { get; set; } + ImportsImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + VSProjectEventsImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - internal OrderPrecedenceImportCollection VSProjectEventsImpl { get; set; } + [ImportMany] + internal OrderPrecedenceImportCollection ImportsImpl { get; set; } - public VSLangProj.References References => _vsProject.References; + [ImportMany] + internal OrderPrecedenceImportCollection VSProjectEventsImpl { get; set; } - public BuildManager BuildManager => _buildManager; + public VSLangProj.References References => _vsProject.References; - public DTE DTE => _vsProject.DTE; + public BuildManager BuildManager => _buildManager; - public Project Project => _vsProject.Project; + public DTE DTE => _vsProject.DTE; - public ProjectItem WebReferencesFolder => _vsProject.WebReferencesFolder; + public Project Project => _vsProject.Project; - public string TemplatePath => _vsProject.TemplatePath; + public ProjectItem WebReferencesFolder => _vsProject.WebReferencesFolder; - public bool WorkOffline { get => _vsProject.WorkOffline; set => _vsProject.WorkOffline = value; } + public string TemplatePath => _vsProject.TemplatePath; - public Imports Imports => ImportsImpl.FirstOrDefault()?.Value ?? _vsProject.Imports; + public bool WorkOffline { get => _vsProject.WorkOffline; set => _vsProject.WorkOffline = value; } - public VSProjectEvents Events => VSProjectEventsImpl.FirstOrDefault()?.Value ?? _vsProject.Events; + public Imports Imports => ImportsImpl.FirstOrDefault()?.Value ?? _vsProject.Imports; - public ProjectItem CreateWebReferencesFolder() - { - return _vsProject.CreateWebReferencesFolder(); - } + public VSProjectEvents Events => VSProjectEventsImpl.FirstOrDefault()?.Value ?? _vsProject.Events; - public ProjectItem AddWebReference(string bstrUrl) - { - return _vsProject.AddWebReference(bstrUrl); - } + public ProjectItem CreateWebReferencesFolder() + { + return _vsProject.CreateWebReferencesFolder(); + } - public void Refresh() - { - _vsProject.Refresh(); - } + public ProjectItem AddWebReference(string bstrUrl) + { + return _vsProject.AddWebReference(bstrUrl); + } - public void CopyProject(string bstrDestFolder, string bstrDestUNCPath, prjCopyProjectOption copyProjectOption, string bstrUsername, string bstrPassword) - { - _vsProject.CopyProject(bstrDestFolder, bstrDestUNCPath, copyProjectOption, bstrUsername, bstrPassword); - } + public void Refresh() + { + _vsProject.Refresh(); + } - public void Exec(prjExecCommand command, int bSuppressUI, object varIn, out object pVarOut) - { - _vsProject.Exec(command, bSuppressUI, varIn, out pVarOut); - } + public void CopyProject(string bstrDestFolder, string bstrDestUNCPath, prjCopyProjectOption copyProjectOption, string bstrUsername, string bstrPassword) + { + _vsProject.CopyProject(bstrDestFolder, bstrDestUNCPath, copyProjectOption, bstrUsername, bstrPassword); + } - public void GenerateKeyPairFiles(string strPublicPrivateFile, string strPublicOnlyFile = "0") - { - _vsProject.GenerateKeyPairFiles(strPublicPrivateFile, strPublicOnlyFile); - } + public void Exec(prjExecCommand command, int bSuppressUI, object varIn, out object pVarOut) + { + _vsProject.Exec(command, bSuppressUI, varIn, out pVarOut); + } - public string GetUniqueFilename(object pDispatch, string bstrRoot, string bstrDesiredExt) - { - return _vsProject.GetUniqueFilename(pDispatch, bstrRoot, bstrDesiredExt); - } + public void GenerateKeyPairFiles(string strPublicPrivateFile, string strPublicOnlyFile = "0") + { + _vsProject.GenerateKeyPairFiles(strPublicPrivateFile, strPublicOnlyFile); + } - #region IConnectionPointContainer + public string GetUniqueFilename(object pDispatch, string bstrRoot, string bstrDesiredExt) + { + return _vsProject.GetUniqueFilename(pDispatch, bstrRoot, bstrDesiredExt); + } - public void EnumConnectionPoints(out IEnumConnectionPoints? ppEnum) - { - ppEnum = null; - (_vsProject as IConnectionPointContainer)?.EnumConnectionPoints(out ppEnum); - } + #region IConnectionPointContainer - public void FindConnectionPoint(ref Guid riid, out IConnectionPoint? ppCP) - { - ppCP = null; - (_vsProject as IConnectionPointContainer)?.FindConnectionPoint(ref riid, out ppCP); - } + public void EnumConnectionPoints(out IEnumConnectionPoints? ppEnum) + { + ppEnum = null; + (_vsProject as IConnectionPointContainer)?.EnumConnectionPoints(out ppEnum); + } - #endregion IConnectionPointContainer + public void FindConnectionPoint(ref Guid riid, out IConnectionPoint? ppCP) + { + ppCP = null; + (_vsProject as IConnectionPointContainer)?.FindConnectionPoint(ref riid, out ppCP); + } - #region IEventSource + #endregion IConnectionPointContainer - public void OnSinkAdded(IPropertyNotifySink sink) - { - (_vsProject as IEventSource)?.OnSinkAdded(sink); - } + #region IEventSource - public void OnSinkRemoved(IPropertyNotifySink sink) - { - (_vsProject as IEventSource)?.OnSinkRemoved(sink); - } + public void OnSinkAdded(IPropertyNotifySink sink) + { + (_vsProject as IEventSource)?.OnSinkAdded(sink); + } - #endregion IEventSource + public void OnSinkRemoved(IPropertyNotifySink sink) + { + (_vsProject as IEventSource)?.OnSinkRemoved(sink); } + + #endregion IEventSource } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject_VsLangProjectProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject_VsLangProjectProperties.cs index b17993df83..017e88f5d8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject_VsLangProjectProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Automation/VsProject_VsLangProjectProperties.cs @@ -5,178 +5,177 @@ using VSLangProj110; using VSLangProj165; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +public partial class VSProject : VSLangProj.ProjectProperties { - public partial class VSProject : VSLangProj.ProjectProperties + private T GetBrowseObjectValue(Func func) { - private T GetBrowseObjectValue(Func func) - { - return _threadingService.ExecuteSynchronously(async () => - { - ConfigurationGeneralBrowseObject browseObject = await _projectProperties.Value.GetConfigurationGeneralBrowseObjectPropertiesAsync(); - IEvaluatedProperty evaluatedProperty = func(browseObject); - object? value = await evaluatedProperty.GetValueAsync(); - - // IEvaluatedProperty.GetValueAsync always returns a non-null value, though - // it inherits this method from IProperty which can return null. - return (T)value!; - }); - } - - private void SetBrowseObjectValue(Func func, object value) - { - _threadingService.ExecuteSynchronously(async () => - { - ConfigurationGeneralBrowseObject browseObject = await _projectProperties.Value.GetConfigurationGeneralBrowseObjectPropertiesAsync(); - IEvaluatedProperty evaluatedProperty = func(browseObject); - await evaluatedProperty.SetValueAsync(value); - }); - } - - private string GetBrowseObjectValueAtEnd(Func func) - { - return _threadingService.ExecuteSynchronously(async () => - { - ConfigurationGeneralBrowseObject browseObject = await _projectProperties.Value.GetConfigurationGeneralBrowseObjectPropertiesAsync(); - IEvaluatedProperty evaluatedProperty = func(browseObject); - return await evaluatedProperty.GetEvaluatedValueAtEndAsync(); - }); - } - - private string GetValueAtEnd(Func func) - { - return _threadingService.ExecuteSynchronously(async () => - { - ConfigurationGeneral configurationGeneral = await _projectProperties.Value.GetConfigurationGeneralPropertiesAsync(); - IEvaluatedProperty evaluatedProperty = func(configurationGeneral); - return await evaluatedProperty.GetEvaluatedValueAtEndAsync(); - }); - } - - private void SetValue(Func func, object value) + return _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfigurationGeneral configurationGeneral = await _projectProperties.Value.GetConfigurationGeneralPropertiesAsync(); - IEvaluatedProperty evaluatedProperty = func(configurationGeneral); - await evaluatedProperty.SetValueAsync(value); - }); - } - - public prjOutputTypeEx OutputTypeEx - { - get => GetBrowseObjectValue(browseObject => browseObject.OutputType); - set => SetBrowseObjectValue(browseObject => browseObject.OutputType, value); - } + ConfigurationGeneralBrowseObject browseObject = await _projectProperties.Value.GetConfigurationGeneralBrowseObjectPropertiesAsync(); + IEvaluatedProperty evaluatedProperty = func(browseObject); + object? value = await evaluatedProperty.GetValueAsync(); + + // IEvaluatedProperty.GetValueAsync always returns a non-null value, though + // it inherits this method from IProperty which can return null. + return (T)value!; + }); + } - // Implementation of VsLangProj.ProjectProperties - public prjOutputType OutputType + private void SetBrowseObjectValue(Func func, object value) + { + _threadingService.ExecuteSynchronously(async () => { - get => GetBrowseObjectValue(browseObject => browseObject.OutputType); - set => SetBrowseObjectValue(browseObject => browseObject.OutputType, value); - } + ConfigurationGeneralBrowseObject browseObject = await _projectProperties.Value.GetConfigurationGeneralBrowseObjectPropertiesAsync(); + IEvaluatedProperty evaluatedProperty = func(browseObject); + await evaluatedProperty.SetValueAsync(value); + }); + } - public string AssemblyName + private string GetBrowseObjectValueAtEnd(Func func) + { + return _threadingService.ExecuteSynchronously(async () => { - get => GetValueAtEnd(configurationGeneral => configurationGeneral.AssemblyName); - set => SetValue(configurationGeneral => configurationGeneral.AssemblyName, value); - } + ConfigurationGeneralBrowseObject browseObject = await _projectProperties.Value.GetConfigurationGeneralBrowseObjectPropertiesAsync(); + IEvaluatedProperty evaluatedProperty = func(browseObject); + return await evaluatedProperty.GetEvaluatedValueAtEndAsync(); + }); + } - public string FullPath + private string GetValueAtEnd(Func func) + { + return _threadingService.ExecuteSynchronously(async () => { - get => GetValueAtEnd(configurationGeneral => configurationGeneral.ProjectDir); - } + ConfigurationGeneral configurationGeneral = await _projectProperties.Value.GetConfigurationGeneralPropertiesAsync(); + IEvaluatedProperty evaluatedProperty = func(configurationGeneral); + return await evaluatedProperty.GetEvaluatedValueAtEndAsync(); + }); + } - public string OutputFileName + private void SetValue(Func func, object value) + { + _threadingService.ExecuteSynchronously(async () => { - get => GetBrowseObjectValueAtEnd(browseObject => browseObject.OutputFileName); - } + ConfigurationGeneral configurationGeneral = await _projectProperties.Value.GetConfigurationGeneralPropertiesAsync(); + IEvaluatedProperty evaluatedProperty = func(configurationGeneral); + await evaluatedProperty.SetValueAsync(value); + }); + } - public string? ExtenderCATID => null; + public prjOutputTypeEx OutputTypeEx + { + get => GetBrowseObjectValue(browseObject => browseObject.OutputType); + set => SetBrowseObjectValue(browseObject => browseObject.OutputType, value); + } - public string AbsoluteProjectDirectory - { - get => GetBrowseObjectValueAtEnd(browseObject => browseObject.FullPath); - } + // Implementation of VsLangProj.ProjectProperties + public prjOutputType OutputType + { + get => GetBrowseObjectValue(browseObject => browseObject.OutputType); + set => SetBrowseObjectValue(browseObject => browseObject.OutputType, value); + } - public bool AutoGenerateBindingRedirects - { - get => GetBrowseObjectValue(browseObject => browseObject.AutoGenerateBindingRedirects).GetValueOrDefault(); - set => SetBrowseObjectValue(browseObject => browseObject.AutoGenerateBindingRedirects, value); - } + public string AssemblyName + { + get => GetValueAtEnd(configurationGeneral => configurationGeneral.AssemblyName); + set => SetValue(configurationGeneral => configurationGeneral.AssemblyName, value); + } - public AuthenticationMode AuthenticationMode - { - get => GetBrowseObjectValue(browseObject => browseObject.AuthenticationMode).GetValueOrDefault(); - set => SetBrowseObjectValue(browseObject => browseObject.AuthenticationMode, value); - } + public string FullPath + { + get => GetValueAtEnd(configurationGeneral => configurationGeneral.ProjectDir); + } + + public string OutputFileName + { + get => GetBrowseObjectValueAtEnd(browseObject => browseObject.OutputFileName); + } + + public string? ExtenderCATID => null; - public string __id => throw new NotImplementedException(); + public string AbsoluteProjectDirectory + { + get => GetBrowseObjectValueAtEnd(browseObject => browseObject.FullPath); + } + + public bool AutoGenerateBindingRedirects + { + get => GetBrowseObjectValue(browseObject => browseObject.AutoGenerateBindingRedirects).GetValueOrDefault(); + set => SetBrowseObjectValue(browseObject => browseObject.AutoGenerateBindingRedirects, value); + } - public object __project => throw new NotImplementedException(); + public AuthenticationMode AuthenticationMode + { + get => GetBrowseObjectValue(browseObject => browseObject.AuthenticationMode).GetValueOrDefault(); + set => SetBrowseObjectValue(browseObject => browseObject.AuthenticationMode, value); + } - public string StartupObject { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string __id => throw new NotImplementedException(); - public string RootNamespace { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public object __project => throw new NotImplementedException(); - public string AssemblyOriginatorKeyFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string StartupObject { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string AssemblyKeyContainerName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string RootNamespace { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjOriginatorKeyMode AssemblyOriginatorKeyMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string AssemblyOriginatorKeyFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string AssemblyKeyContainerName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string WebServer => throw new NotImplementedException(); + public prjOriginatorKeyMode AssemblyOriginatorKeyMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string WebServerVersion => throw new NotImplementedException(); + public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string ServerExtensionsVersion => throw new NotImplementedException(); + public string WebServer => throw new NotImplementedException(); - public bool LinkRepair { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string WebServerVersion => throw new NotImplementedException(); - public string OfflineURL => throw new NotImplementedException(); + public string ServerExtensionsVersion => throw new NotImplementedException(); - public string FileSharePath { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool LinkRepair { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string ActiveFileSharePath => throw new NotImplementedException(); + public string OfflineURL => throw new NotImplementedException(); - public prjWebAccessMethod WebAccessMethod { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string FileSharePath { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjWebAccessMethod ActiveWebAccessMethod => throw new NotImplementedException(); + public string ActiveFileSharePath => throw new NotImplementedException(); - public prjScriptLanguage DefaultClientScript { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjWebAccessMethod WebAccessMethod { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjTargetSchema DefaultTargetSchema { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjWebAccessMethod ActiveWebAccessMethod => throw new NotImplementedException(); - public prjHTMLPageLayout DefaultHTMLPageLayout { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjScriptLanguage DefaultClientScript { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string FileName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjTargetSchema DefaultTargetSchema { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string LocalPath => throw new NotImplementedException(); + public prjHTMLPageLayout DefaultHTMLPageLayout { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string URL => throw new NotImplementedException(); + public string FileName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public ProjectConfigurationProperties ActiveConfigurationSettings => throw new NotImplementedException(); + public string LocalPath => throw new NotImplementedException(); - public object ExtenderNames => throw new NotImplementedException(); + public string URL => throw new NotImplementedException(); - public string ApplicationIcon { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public ProjectConfigurationProperties ActiveConfigurationSettings => throw new NotImplementedException(); - public prjOptionStrict OptionStrict { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public object ExtenderNames => throw new NotImplementedException(); - public string ReferencePath { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string ApplicationIcon { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjOptionExplicit OptionExplicit { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjOptionStrict OptionStrict { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjCompare OptionCompare { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string ReferencePath { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjProjectType ProjectType => throw new NotImplementedException(); + public prjOptionExplicit OptionExplicit { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string DefaultNamespace { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjCompare OptionCompare { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public object get_Extender(string ExtenderName) => throw new NotImplementedException(); + public prjProjectType ProjectType => throw new NotImplementedException(); - object VSLangProj.ProjectProperties.Extender => throw new NotImplementedException(); - } + public string DefaultNamespace { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public object get_Extender(string ExtenderName) => throw new NotImplementedException(); + + object VSLangProj.ProjectProperties.Extender => throw new NotImplementedException(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IIncrementalBuildFailureReporter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IIncrementalBuildFailureReporter.cs index c0ea679bf4..2afb76d362 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IIncrementalBuildFailureReporter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IIncrementalBuildFailureReporter.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics; + +/// +/// Extension point for components that report on incremental build failures. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] +internal interface IIncrementalBuildFailureReporter { /// - /// Extension point for components that report on incremental build failures. + /// Gets whether this reporter is enabled or not. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] - internal interface IIncrementalBuildFailureReporter - { - /// - /// Gets whether this reporter is enabled or not. - /// - /// - /// When this method returns , - /// will not be called. If all exports of return false, the incremental - /// build failure check is not performed at all. - /// - /// A token whose cancellation marks lost interest in the result of this task. - /// A task that resolves to if this reporter is currently enabled, otherwise . - ValueTask IsEnabledAsync(CancellationToken cancellationToken); + /// + /// When this method returns , + /// will not be called. If all exports of return false, the incremental + /// build failure check is not performed at all. + /// + /// A token whose cancellation marks lost interest in the result of this task. + /// A task that resolves to if this reporter is currently enabled, otherwise . + ValueTask IsEnabledAsync(CancellationToken cancellationToken); - /// - /// Reports an incremental build failure for the project in the current scope. - /// - /// A string that identifies the reason for the failure. - /// A detailed description of the failure. May include file paths and timestamps. - /// The duration spent performing the incremental build failure check. - /// A token whose cancellation marks lost interest in the result of this task. - /// A task that completes when reporting is done. - Task ReportFailureAsync(string failureReason, string failureDescription, TimeSpan checkDuration, CancellationToken cancellationToken); - } + /// + /// Reports an incremental build failure for the project in the current scope. + /// + /// A string that identifies the reason for the failure. + /// A detailed description of the failure. May include file paths and timestamps. + /// The duration spent performing the incremental build failure check. + /// A token whose cancellation marks lost interest in the result of this task. + /// A task that completes when reporting is done. + Task ReportFailureAsync(string failureReason, string failureDescription, TimeSpan checkDuration, CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.IProjectChecker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.IProjectChecker.cs index 42d2e7af1c..709ec8debf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.IProjectChecker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.IProjectChecker.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics; + +internal sealed partial class IncrementalBuildFailureDetector { - internal sealed partial class IncrementalBuildFailureDetector + /// + /// Tracks per-project state. + /// + /// + /// The parent class is in the global MEF scope. Projects are built within the context of a specific + /// configuration. We use exports of this component to perform logic at the configuration scope. + /// + [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] + internal interface IProjectChecker { /// - /// Tracks per-project state. + /// Checks for incremental build failure. /// - /// - /// The parent class is in the global MEF scope. Projects are built within the context of a specific - /// configuration. We use exports of this component to perform logic at the configuration scope. - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectChecker - { - /// - /// Checks for incremental build failure. - /// - void OnProjectBuildCompleted(); - } + void OnProjectBuildCompleted(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.ProjectChecker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.ProjectChecker.cs index 5a342a114e..0ed5ba7064 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.ProjectChecker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.ProjectChecker.cs @@ -5,103 +5,102 @@ using Microsoft.VisualStudio.ProjectSystem.Build; using Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics; + +internal sealed partial class IncrementalBuildFailureDetector { - internal sealed partial class IncrementalBuildFailureDetector + /// + /// Configured project scoped data related to incremental build failure detection. + /// + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Export(typeof(IProjectChecker))] + private sealed class ProjectChecker : IProjectChecker { - /// - /// Configured project scoped data related to incremental build failure detection. - /// - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Export(typeof(IProjectChecker))] - private sealed class ProjectChecker : IProjectChecker + private readonly ConfiguredProject _configuredProject; + private readonly IBuildUpToDateCheckValidator _upToDateCheckValidator; + private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; + + [ImportMany] + private OrderPrecedenceImportCollection Reporters { get; } + + [ImportingConstructor] + public ProjectChecker( + ConfiguredProject configuredProject, + IBuildUpToDateCheckValidator upToDateCheckValidator, + [Import(ExportContractNames.Scopes.ConfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService) { - private readonly ConfiguredProject _configuredProject; - private readonly IBuildUpToDateCheckValidator _upToDateCheckValidator; - private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; - - [ImportMany] - private OrderPrecedenceImportCollection Reporters { get; } - - [ImportingConstructor] - public ProjectChecker( - ConfiguredProject configuredProject, - IBuildUpToDateCheckValidator upToDateCheckValidator, - [Import(ExportContractNames.Scopes.ConfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService) - { - _configuredProject = configuredProject; - _upToDateCheckValidator = upToDateCheckValidator; - _projectAsynchronousTasksService = projectAsynchronousTasksService; + _configuredProject = configuredProject; + _upToDateCheckValidator = upToDateCheckValidator; + _projectAsynchronousTasksService = projectAsynchronousTasksService; - Reporters = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: configuredProject); - } + Reporters = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: configuredProject); + } - public void OnProjectBuildCompleted() - { - _configuredProject.Services.ThreadingPolicy.RunAndForget( - () => CheckAsync(_projectAsynchronousTasksService.UnloadCancellationToken), - configuredProject: _configuredProject, - options: ForkOptions.StartOnThreadPool | ForkOptions.CancelOnUnload | ForkOptions.NoAssistanceMask); + public void OnProjectBuildCompleted() + { + _configuredProject.Services.ThreadingPolicy.RunAndForget( + () => CheckAsync(_projectAsynchronousTasksService.UnloadCancellationToken), + configuredProject: _configuredProject, + options: ForkOptions.StartOnThreadPool | ForkOptions.CancelOnUnload | ForkOptions.NoAssistanceMask); - return; + return; - async Task CheckAsync(CancellationToken cancellationToken) - { - var sw = Stopwatch.StartNew(); + async Task CheckAsync(CancellationToken cancellationToken) + { + var sw = Stopwatch.StartNew(); - if (_upToDateCheckValidator is IBuildUpToDateCheckProvider provider) + if (_upToDateCheckValidator is IBuildUpToDateCheckProvider provider) + { + if (!await provider.IsUpToDateCheckEnabledAsync(cancellationToken)) { - if (!await provider.IsUpToDateCheckEnabledAsync(cancellationToken)) - { - // The fast up-to-date check has been disabled. We can't know the reason why. - // We currently do not flag errors in this case, so stop processing immediately. - return; - } + // The fast up-to-date check has been disabled. We can't know the reason why. + // We currently do not flag errors in this case, so stop processing immediately. + return; } + } - List? reporters = await GetEnabledReportersAsync(); + List? reporters = await GetEnabledReportersAsync(); - if (reporters is null) - { - // No reporter is enabled, so return immediately without checking anything - return; - } + if (reporters is null) + { + // No reporter is enabled, so return immediately without checking anything + return; + } - (bool isUpToDate, string? failureReason, string? failureDescription) = await _upToDateCheckValidator.ValidateUpToDateAsync(cancellationToken); + (bool isUpToDate, string? failureReason, string? failureDescription) = await _upToDateCheckValidator.ValidateUpToDateAsync(cancellationToken); - if (isUpToDate) - { - // The project is up-to-date, as expected. Nothing more to do. - return; - } + if (isUpToDate) + { + // The project is up-to-date, as expected. Nothing more to do. + return; + } - Assumes.NotNull(failureReason); - Assumes.NotNull(failureDescription); + Assumes.NotNull(failureReason); + Assumes.NotNull(failureDescription); - TimeSpan checkDuration = sw.Elapsed; + TimeSpan checkDuration = sw.Elapsed; - foreach (IIncrementalBuildFailureReporter reporter in reporters) - { - await reporter.ReportFailureAsync(failureReason, failureDescription, checkDuration, cancellationToken); - } + foreach (IIncrementalBuildFailureReporter reporter in reporters) + { + await reporter.ReportFailureAsync(failureReason, failureDescription, checkDuration, cancellationToken); + } - return; + return; - async Task?> GetEnabledReportersAsync() - { - List? reporters = null; + async Task?> GetEnabledReportersAsync() + { + List? reporters = null; - foreach (IIncrementalBuildFailureReporter reporter in Reporters.ExtensionValues()) + foreach (IIncrementalBuildFailureReporter reporter in Reporters.ExtensionValues()) + { + if (await reporter.IsEnabledAsync(cancellationToken)) { - if (await reporter.IsEnabledAsync(cancellationToken)) - { - reporters ??= new(); - reporters.Add(reporter); - } + reporters ??= new(); + reporters.Add(reporter); } - - return reporters; } + + return reporters; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs index 0ddf19ac15..7d0ed66f19 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureDetector.cs @@ -7,175 +7,174 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics; + +/// +/// Detects when a project's incremental build is not working, and reports a message to the user and publishes telemetry. +/// +/// +/// +/// When a project's incremental build is working correctly, it will be up-to-date immediately after build. +/// +/// +/// This component validates that the fast up-to-date check sees the project as up-to-date after build. +/// +/// +/// If not, we: +/// +/// Notify the user and provide a URL for more information. +/// Publish telemetry about the failure in order to improve incremental build in future. +/// +/// +/// +/// This component lives in the global scope. Its single instance listens to solution build events. +/// It then delegates to in the relevant +/// configured project scope to determine whether incremental build worked. +/// +/// +[Export(typeof(IPackageService))] +internal sealed partial class IncrementalBuildFailureDetector + : OnceInitializedOnceDisposedAsync, + IVsUpdateSolutionEvents2, + IVsRunningDocTableEvents, + IPackageService { - /// - /// Detects when a project's incremental build is not working, and reports a message to the user and publishes telemetry. - /// - /// - /// - /// When a project's incremental build is working correctly, it will be up-to-date immediately after build. - /// - /// - /// This component validates that the fast up-to-date check sees the project as up-to-date after build. - /// - /// - /// If not, we: - /// - /// Notify the user and provide a URL for more information. - /// Publish telemetry about the failure in order to improve incremental build in future. - /// - /// - /// - /// This component lives in the global scope. Its single instance listens to solution build events. - /// It then delegates to in the relevant - /// configured project scope to determine whether incremental build worked. - /// - /// - [Export(typeof(IPackageService))] - internal sealed partial class IncrementalBuildFailureDetector - : OnceInitializedOnceDisposedAsync, - IVsUpdateSolutionEvents2, - IVsRunningDocTableEvents, - IPackageService - { - private readonly ISolutionBuildManager _solutionBuildEvents; - private readonly IRunningDocumentTable _rdtEvents; + private readonly ISolutionBuildManager _solutionBuildEvents; + private readonly IRunningDocumentTable _rdtEvents; - private IAsyncDisposable? _solutionBuildEventsSubscription; - private IAsyncDisposable? _rdtEventsSubscription; + private IAsyncDisposable? _solutionBuildEventsSubscription; + private IAsyncDisposable? _rdtEventsSubscription; - private DateTime _lastSavedAtUtc = DateTime.MinValue; - private DateTime _lastBuildStartedAtUtc = DateTime.MinValue; + private DateTime _lastSavedAtUtc = DateTime.MinValue; + private DateTime _lastBuildStartedAtUtc = DateTime.MinValue; - [ImportingConstructor] - public IncrementalBuildFailureDetector( - ISolutionBuildManager solutionBuildEvents, - IRunningDocumentTable rdtEvents, - JoinableTaskContext joinableTaskContext) - : base(new(joinableTaskContext)) - { - _solutionBuildEvents = solutionBuildEvents; - _rdtEvents = rdtEvents; - } + [ImportingConstructor] + public IncrementalBuildFailureDetector( + ISolutionBuildManager solutionBuildEvents, + IRunningDocumentTable rdtEvents, + JoinableTaskContext joinableTaskContext) + : base(new(joinableTaskContext)) + { + _solutionBuildEvents = solutionBuildEvents; + _rdtEvents = rdtEvents; + } - async Task IPackageService.InitializeAsync(IAsyncServiceProvider _) - { - // These will both internally switch to the UI thread, so better to do the - // switch once so most of this will run synchronously without yielding. - await JoinableFactory.SwitchToMainThreadAsync(); + async Task IPackageService.InitializeAsync(IAsyncServiceProvider _) + { + // These will both internally switch to the UI thread, so better to do the + // switch once so most of this will run synchronously without yielding. + await JoinableFactory.SwitchToMainThreadAsync(); - // We want to hook these early, so do this during package initialisation - _solutionBuildEventsSubscription = await _solutionBuildEvents.SubscribeSolutionEventsAsync(this); - _rdtEventsSubscription = await _rdtEvents.SubscribeEventsAsync(this); + // We want to hook these early, so do this during package initialisation + _solutionBuildEventsSubscription = await _solutionBuildEvents.SubscribeSolutionEventsAsync(this); + _rdtEventsSubscription = await _rdtEvents.SubscribeEventsAsync(this); - await InitializeAsync(); - } + await InitializeAsync(); + } - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + Assumes.NotNull(_solutionBuildEventsSubscription); + Assumes.NotNull(_rdtEventsSubscription); + + return Task.CompletedTask; + } + + protected override async Task DisposeCoreAsync(bool initialized) + { + if (initialized) { Assumes.NotNull(_solutionBuildEventsSubscription); Assumes.NotNull(_rdtEventsSubscription); - return Task.CompletedTask; + // Switch the UI thread once here, rather than once per dispose below + await JoinableFactory.SwitchToMainThreadAsync(); + + await _solutionBuildEventsSubscription.DisposeAsync(); + await _rdtEventsSubscription.DisposeAsync(); } + } - protected override async Task DisposeCoreAsync(bool initialized) + /// + /// Called right after a project configuration is finished building. + /// + int IVsUpdateSolutionEvents2.UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) + { + if (fSuccess != 0 && fCancel == 0) { - if (initialized) + if (_lastSavedAtUtc > _lastBuildStartedAtUtc) { - Assumes.NotNull(_solutionBuildEventsSubscription); - Assumes.NotNull(_rdtEventsSubscription); + // Something was saved since the last build. + // This can cause the project to appear out-of-date in a way that does not indicate + // broken incrementality. In such cases, avoid the check here altogether for + // this project build. + return HResult.OK; + } - // Switch the UI thread once here, rather than once per dispose below - await JoinableFactory.SwitchToMainThreadAsync(); + if (IsRelevantBuild(dwAction) && pCfgProj is IVsBrowseObjectContext { ConfiguredProject: { } configuredProject }) + { + IProjectChecker? checker = configuredProject.Services.ExportProvider.GetExportedValueOrDefault(); - await _solutionBuildEventsSubscription.DisposeAsync(); - await _rdtEventsSubscription.DisposeAsync(); + checker?.OnProjectBuildCompleted(); } } - /// - /// Called right after a project configuration is finished building. - /// - int IVsUpdateSolutionEvents2.UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) - { - if (fSuccess != 0 && fCancel == 0) - { - if (_lastSavedAtUtc > _lastBuildStartedAtUtc) - { - // Something was saved since the last build. - // This can cause the project to appear out-of-date in a way that does not indicate - // broken incrementality. In such cases, avoid the check here altogether for - // this project build. - return HResult.OK; - } - - if (IsRelevantBuild(dwAction) && pCfgProj is IVsBrowseObjectContext { ConfiguredProject: { } configuredProject }) - { - IProjectChecker? checker = configuredProject.Services.ExportProvider.GetExportedValueOrDefault(); - - checker?.OnProjectBuildCompleted(); - } - } + return HResult.OK; - return HResult.OK; + static bool IsRelevantBuild(uint options) + { + var operation = (VSSOLNBUILDUPDATEFLAGS)options; - static bool IsRelevantBuild(uint options) + if ((operation & VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD) == VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD) { - var operation = (VSSOLNBUILDUPDATEFLAGS)options; - - if ((operation & VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD) == VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD) - { - return true; - } - - return false; + return true; } - } - /// - /// Called before the first project configuration is about to be built. - /// - int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate) - { - _lastBuildStartedAtUtc = DateTime.UtcNow; - return HResult.OK; + return false; } + } - /// - /// Called after saving a document in the Running Document Table. - /// - int IVsRunningDocTableEvents.OnAfterSave(uint docCookie) - { - _lastSavedAtUtc = DateTime.UtcNow; - return HResult.OK; - } + /// + /// Called before the first project configuration is about to be built. + /// + int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate) + { + _lastBuildStartedAtUtc = DateTime.UtcNow; + return HResult.OK; + } - #region IVsUpdateSolutionEvents stubs + /// + /// Called after saving a document in the Running Document Table. + /// + int IVsRunningDocTableEvents.OnAfterSave(uint docCookie) + { + _lastSavedAtUtc = DateTime.UtcNow; + return HResult.OK; + } - int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; - int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => HResult.OK; - int IVsUpdateSolutionEvents.UpdateSolution_Cancel() => HResult.OK; - int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; + #region IVsUpdateSolutionEvents stubs - int IVsUpdateSolutionEvents2.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_StartUpdate(ref int pfCancelUpdate) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_Cancel() => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) => HResult.OK; + int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; + int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => HResult.OK; + int IVsUpdateSolutionEvents.UpdateSolution_Cancel() => HResult.OK; + int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; - #endregion + int IVsUpdateSolutionEvents2.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_StartUpdate(ref int pfCancelUpdate) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_Cancel() => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) => HResult.OK; - #region IVsRunningDocTableEvents stubs + #endregion - int IVsRunningDocTableEvents.OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => HResult.OK; - int IVsRunningDocTableEvents.OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => HResult.OK; - int IVsRunningDocTableEvents.OnAfterAttributeChange(uint docCookie, uint grfAttribs) => HResult.OK; - int IVsRunningDocTableEvents.OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) => HResult.OK; - int IVsRunningDocTableEvents.OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) => HResult.OK; + #region IVsRunningDocTableEvents stubs - #endregion - } + int IVsRunningDocTableEvents.OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => HResult.OK; + int IVsRunningDocTableEvents.OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => HResult.OK; + int IVsRunningDocTableEvents.OnAfterAttributeChange(uint docCookie, uint grfAttribs) => HResult.OK; + int IVsRunningDocTableEvents.OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) => HResult.OK; + int IVsRunningDocTableEvents.OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) => HResult.OK; + + #endregion } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureOutputWindowReporter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureOutputWindowReporter.cs index d1913682b3..53f2569c19 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureOutputWindowReporter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureOutputWindowReporter.cs @@ -4,66 +4,65 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Telemetry; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics; + +/// +/// Reports incremental build failures via the output window. +/// +[Export(typeof(IIncrementalBuildFailureReporter))] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +internal sealed class IncrementalBuildFailureOutputWindowReporter : IIncrementalBuildFailureReporter { - /// - /// Reports incremental build failures via the output window. - /// - [Export(typeof(IIncrementalBuildFailureReporter))] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - internal sealed class IncrementalBuildFailureOutputWindowReporter : IIncrementalBuildFailureReporter + private readonly UnconfiguredProject _project; + private readonly IVsUIService _outputWindow; + private readonly IProjectSystemOptions _projectSystemOptions; + + [ImportingConstructor] + public IncrementalBuildFailureOutputWindowReporter( + UnconfiguredProject project, + IVsUIService outputWindow, + IProjectSystemOptions projectSystemOptions) { - private readonly UnconfiguredProject _project; - private readonly IVsUIService _outputWindow; - private readonly IProjectSystemOptions _projectSystemOptions; + _project = project; + _outputWindow = outputWindow; + _projectSystemOptions = projectSystemOptions; + } - [ImportingConstructor] - public IncrementalBuildFailureOutputWindowReporter( - UnconfiguredProject project, - IVsUIService outputWindow, - IProjectSystemOptions projectSystemOptions) - { - _project = project; - _outputWindow = outputWindow; - _projectSystemOptions = projectSystemOptions; - } + public async ValueTask IsEnabledAsync(CancellationToken cancellationToken) + { + LogLevel logLevel = await _projectSystemOptions.GetFastUpToDateLoggingLevelAsync(cancellationToken); - public async ValueTask IsEnabledAsync(CancellationToken cancellationToken) + if (logLevel != LogLevel.None) { - LogLevel logLevel = await _projectSystemOptions.GetFastUpToDateLoggingLevelAsync(cancellationToken); - - if (logLevel != LogLevel.None) - { - // Always display if the user has enabled any kind of up-to-date check logging - return true; - } - - return await _projectSystemOptions.IsIncrementalBuildFailureOutputLoggingEnabledAsync(cancellationToken); + // Always display if the user has enabled any kind of up-to-date check logging + return true; } - public async Task ReportFailureAsync(string failureReason, string failureDescription, TimeSpan checkDuration, CancellationToken cancellationToken) - { - // Report telemetry indicating that we showed this message. This will allow us to compare the number of times this is shown - // with the click-through rate for more information. - var telemetryEvent = new TelemetryEvent(TelemetryEventName.IncrementalBuildValidationFailureDisplayed); + return await _projectSystemOptions.IsIncrementalBuildFailureOutputLoggingEnabledAsync(cancellationToken); + } - telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.FailureReason, failureReason); - telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.DurationMillis, checkDuration); + public async Task ReportFailureAsync(string failureReason, string failureDescription, TimeSpan checkDuration, CancellationToken cancellationToken) + { + // Report telemetry indicating that we showed this message. This will allow us to compare the number of times this is shown + // with the click-through rate for more information. + var telemetryEvent = new TelemetryEvent(TelemetryEventName.IncrementalBuildValidationFailureDisplayed); + + telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.FailureReason, failureReason); + telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.DurationMillis, checkDuration); - TelemetryService.DefaultSession.PostEvent(telemetryEvent); + TelemetryService.DefaultSession.PostEvent(telemetryEvent); - await _project.Services.ThreadingPolicy.SwitchToUIThread(cancellationToken); + await _project.Services.ThreadingPolicy.SwitchToUIThread(cancellationToken); - // Log a message to the output window. This message will appear after the build completes, which is fine. - // We don't want to hold up reporting of a completed build on this. - Guid outputPaneGuid = VSConstants.GUID_BuildOutputWindowPane; + // Log a message to the output window. This message will appear after the build completes, which is fine. + // We don't want to hold up reporting of a completed build on this. + Guid outputPaneGuid = VSConstants.GUID_BuildOutputWindowPane; - if (_outputWindow.Value.GetPane(ref outputPaneGuid, out IVsOutputWindowPane? outputPane) == HResult.OK && outputPane is not null) - { - string message = string.Format(VSResources.IncrementalBuildFailureWarningMessage_2, Path.GetFileName(_project.FullPath), failureDescription.TrimEnd(Delimiter.Period)); + if (_outputWindow.Value.GetPane(ref outputPaneGuid, out IVsOutputWindowPane? outputPane) == HResult.OK && outputPane is not null) + { + string message = string.Format(VSResources.IncrementalBuildFailureWarningMessage_2, Path.GetFileName(_project.FullPath), failureDescription.TrimEnd(Delimiter.Period)); - outputPane.OutputStringNoPump(message); - } + outputPane.OutputStringNoPump(message); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureTelemetryReporter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureTelemetryReporter.cs index 3f32062845..7527bf72a1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureTelemetryReporter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/Diagnostics/IncrementalBuildFailureTelemetryReporter.cs @@ -3,52 +3,51 @@ using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Telemetry; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build.Diagnostics; + +/// +/// Reports incremental build failures via telemetry. +/// +[Export(typeof(IIncrementalBuildFailureReporter))] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +internal sealed class IncrementalBuildFailureTelemetryReporter : IIncrementalBuildFailureReporter { - /// - /// Reports incremental build failures via telemetry. - /// - [Export(typeof(IIncrementalBuildFailureReporter))] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - internal sealed class IncrementalBuildFailureTelemetryReporter : IIncrementalBuildFailureReporter + private readonly IProjectSystemOptions _projectSystemOptions; + private bool _hasBeenReported; + + [ImportingConstructor] + public IncrementalBuildFailureTelemetryReporter( + UnconfiguredProject _, // scoping + IProjectSystemOptions projectSystemOptions) { - private readonly IProjectSystemOptions _projectSystemOptions; - private bool _hasBeenReported; + _projectSystemOptions = projectSystemOptions; + } - [ImportingConstructor] - public IncrementalBuildFailureTelemetryReporter( - UnconfiguredProject _, // scoping - IProjectSystemOptions projectSystemOptions) + public ValueTask IsEnabledAsync(CancellationToken cancellationToken) + { + if (_hasBeenReported) { - _projectSystemOptions = projectSystemOptions; + // Only report once per project. If we have previously reported this, + // return false. + return new ValueTask(false); } - public ValueTask IsEnabledAsync(CancellationToken cancellationToken) - { - if (_hasBeenReported) - { - // Only report once per project. If we have previously reported this, - // return false. - return new ValueTask(false); - } - - return _projectSystemOptions.IsIncrementalBuildFailureTelemetryEnabledAsync(cancellationToken); - } + return _projectSystemOptions.IsIncrementalBuildFailureTelemetryEnabledAsync(cancellationToken); + } - public Task ReportFailureAsync(string failureReason, string failureDescription, TimeSpan checkDuration, CancellationToken cancellationToken) - { - Assumes.False(_hasBeenReported); + public Task ReportFailureAsync(string failureReason, string failureDescription, TimeSpan checkDuration, CancellationToken cancellationToken) + { + Assumes.False(_hasBeenReported); - var telemetryEvent = new TelemetryEvent(TelemetryEventName.IncrementalBuildValidationFailure); + var telemetryEvent = new TelemetryEvent(TelemetryEventName.IncrementalBuildValidationFailure); - telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.FailureReason, failureReason); - telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.DurationMillis, checkDuration); + telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.FailureReason, failureReason); + telemetryEvent.Properties.Add(TelemetryPropertyName.IncrementalBuildValidation.DurationMillis, checkDuration); - TelemetryService.DefaultSession.PostEvent(telemetryEvent); + TelemetryService.DefaultSession.PostEvent(telemetryEvent); - _hasBeenReported = true; + _hasBeenReported = true; - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ISolutionBuildManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ISolutionBuildManager.cs index 41f5b90a5a..5b9ede0163 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ISolutionBuildManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ISolutionBuildManager.cs @@ -2,32 +2,31 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +/// +/// Allows subscribing to solution build manager events via the +/// family of event handler interfaces. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.System)] +internal interface ISolutionBuildManager { /// - /// Allows subscribing to solution build manager events via the - /// family of event handler interfaces. + /// Creates a new subscription for build events that will call back via . /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.System)] - internal interface ISolutionBuildManager - { - /// - /// Creates a new subscription for build events that will call back via . - /// - /// The callback for events. Note that it may also implement additional version(s) of this interface. - /// An object that unsubscribes when disposed. - Task SubscribeSolutionEventsAsync(IVsUpdateSolutionEvents eventListener); + /// The callback for events. Note that it may also implement additional version(s) of this interface. + /// An object that unsubscribes when disposed. + Task SubscribeSolutionEventsAsync(IVsUpdateSolutionEvents eventListener); - int QueryBuildManagerBusy(); + int QueryBuildManagerBusy(); - uint QueryBuildManagerBusyEx(); + uint QueryBuildManagerBusyEx(); - void SaveDocumentsBeforeBuild(IVsHierarchy hierarchy, uint itemId = unchecked((uint)VSConstants.VSITEMID.Root), uint docCookie = 0); + void SaveDocumentsBeforeBuild(IVsHierarchy hierarchy, uint itemId = unchecked((uint)VSConstants.VSITEMID.Root), uint docCookie = 0); - void CalculateProjectDependencies(); + void CalculateProjectDependencies(); - IVsHierarchy[] GetProjectDependencies(IVsHierarchy hierarchy); + IVsHierarchy[] GetProjectDependencies(IVsHierarchy hierarchy); - void StartUpdateSpecificProjectConfigurations(IVsHierarchy[] hierarchy, uint[] buildFlags, uint dwFlags); - } + void StartUpdateSpecificProjectConfigurations(IVsHierarchy[] hierarchy, uint[] buildFlags, uint dwFlags); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManager.cs index 9479b9b00b..e965175dfb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManager.cs @@ -5,136 +5,135 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +/// +/// Tracks implicitly builds triggered as part of F5/Ctrl+F5 debug/launch commands and +/// updates the appropriately. +/// +[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] +[AppliesTo(ProjectCapability.DotNet)] +internal class ImplicitlyTriggeredDebugBuildManager : OnceInitializedOnceDisposedAsync, IProjectDynamicLoadComponent, IVsUpdateSolutionEvents2, IVsUpdateSolutionEvents3 { - /// - /// Tracks implicitly builds triggered as part of F5/Ctrl+F5 debug/launch commands and - /// updates the appropriately. - /// - [Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] - [AppliesTo(ProjectCapability.DotNet)] - internal class ImplicitlyTriggeredDebugBuildManager : OnceInitializedOnceDisposedAsync, IProjectDynamicLoadComponent, IVsUpdateSolutionEvents2, IVsUpdateSolutionEvents3 - { - private readonly IStartupProjectHelper _startupProjectHelper; - private readonly ISolutionBuildManager _solutionBuildManager; + private readonly IStartupProjectHelper _startupProjectHelper; + private readonly ISolutionBuildManager _solutionBuildManager; #pragma warning disable CS0618 // Type or member is obsolete - IImplicitlyTriggeredBuildManager is marked obsolete as it may eventually be replaced with a different API. - private readonly IImplicitlyTriggeredBuildManager _implicitlyTriggeredBuildManager; + private readonly IImplicitlyTriggeredBuildManager _implicitlyTriggeredBuildManager; #pragma warning restore CS0618 // Type or member is obsolete - private IAsyncDisposable? _solutionBuildEventsSubscription; + private IAsyncDisposable? _solutionBuildEventsSubscription; - [ImportingConstructor] - public ImplicitlyTriggeredDebugBuildManager( - IProjectThreadingService threadingService, - ISolutionBuildManager solutionBuildManager, + [ImportingConstructor] + public ImplicitlyTriggeredDebugBuildManager( + IProjectThreadingService threadingService, + ISolutionBuildManager solutionBuildManager, #pragma warning disable CS0618 // Type or member is obsolete - IImplicitlyTriggeredBuildManager is marked obsolete as it may eventually be replaced with a different API. - IImplicitlyTriggeredBuildManager implicitlyTriggeredBuildManager, + IImplicitlyTriggeredBuildManager implicitlyTriggeredBuildManager, #pragma warning restore CS0618 // Type or member is obsolete - IStartupProjectHelper startupProjectHelper) - : base(threadingService.JoinableTaskContext) - { - _solutionBuildManager = solutionBuildManager; - _implicitlyTriggeredBuildManager = implicitlyTriggeredBuildManager; - _startupProjectHelper = startupProjectHelper; - } + IStartupProjectHelper startupProjectHelper) + : base(threadingService.JoinableTaskContext) + { + _solutionBuildManager = solutionBuildManager; + _implicitlyTriggeredBuildManager = implicitlyTriggeredBuildManager; + _startupProjectHelper = startupProjectHelper; + } - public Task LoadAsync() => InitializeAsync(); - public Task UnloadAsync() => DisposeAsync(); + public Task LoadAsync() => InitializeAsync(); + public Task UnloadAsync() => DisposeAsync(); - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _solutionBuildEventsSubscription = await _solutionBuildManager.SubscribeSolutionEventsAsync(this); - } + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _solutionBuildEventsSubscription = await _solutionBuildManager.SubscribeSolutionEventsAsync(this); + } - protected override Task DisposeCoreAsync(bool initialized) + protected override Task DisposeCoreAsync(bool initialized) + { + if (initialized) { - if (initialized) + if (_solutionBuildEventsSubscription is not null) { - if (_solutionBuildEventsSubscription is not null) - { - return _solutionBuildEventsSubscription.DisposeAsync().AsTask(); - } + return _solutionBuildEventsSubscription.DisposeAsync().AsTask(); } - - return Task.CompletedTask; } - private bool IsImplicitlyTriggeredBuild() - { - Assumes.NotNull(_solutionBuildManager); - - var buildFlags = (VSSOLNBUILDUPDATEFLAGS)_solutionBuildManager.QueryBuildManagerBusyEx(); + return Task.CompletedTask; + } - return (buildFlags & (VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH | VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG)) != 0; - } + private bool IsImplicitlyTriggeredBuild() + { + Assumes.NotNull(_solutionBuildManager); - public int UpdateSolution_Begin(ref int pfCancelUpdate) - { - if (IsImplicitlyTriggeredBuild()) - { - if (_implicitlyTriggeredBuildManager is IImplicitlyTriggeredBuildManager2 implicitlyTriggeredBuildManager2) - { - ImmutableArray startupProjectFullPaths = _startupProjectHelper.GetFullPathsOfStartupProjects(); - implicitlyTriggeredBuildManager2.OnBuildStart(startupProjectFullPaths); - } - else - { - _implicitlyTriggeredBuildManager.OnBuildStart(); - } - } + var buildFlags = (VSSOLNBUILDUPDATEFLAGS)_solutionBuildManager.QueryBuildManagerBusyEx(); - return HResult.OK; - } + return (buildFlags & (VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH | VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG)) != 0; + } - public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) + public int UpdateSolution_Begin(ref int pfCancelUpdate) + { + if (IsImplicitlyTriggeredBuild()) { - if (IsImplicitlyTriggeredBuild()) + if (_implicitlyTriggeredBuildManager is IImplicitlyTriggeredBuildManager2 implicitlyTriggeredBuildManager2) { - _implicitlyTriggeredBuildManager.OnBuildEndOrCancel(); + ImmutableArray startupProjectFullPaths = _startupProjectHelper.GetFullPathsOfStartupProjects(); + implicitlyTriggeredBuildManager2.OnBuildStart(startupProjectFullPaths); } - - return HResult.OK; - } - - public int UpdateSolution_Cancel() - { - if (IsImplicitlyTriggeredBuild()) + else { - _implicitlyTriggeredBuildManager.OnBuildEndOrCancel(); + _implicitlyTriggeredBuildManager.OnBuildStart(); } - - return HResult.OK; } - public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) - { - return HResult.OK; - } + return HResult.OK; + } - public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) + public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) + { + if (IsImplicitlyTriggeredBuild()) { - return HResult.OK; + _implicitlyTriggeredBuildManager.OnBuildEndOrCancel(); } - public int UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) - { - return HResult.OK; - } + return HResult.OK; + } - public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) + public int UpdateSolution_Cancel() + { + if (IsImplicitlyTriggeredBuild()) { - return HResult.OK; + _implicitlyTriggeredBuildManager.OnBuildEndOrCancel(); } - public int OnBeforeActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) - { - return HResult.OK; - } + return HResult.OK; + } - public int OnAfterActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) - { - return HResult.OK; - } + public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) + { + return HResult.OK; + } + + public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) + { + return HResult.OK; + } + + public int UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) + { + return HResult.OK; + } + + public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) + { + return HResult.OK; + } + + public int OnBeforeActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) + { + return HResult.OK; + } + + public int OnAfterActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) + { + return HResult.OK; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.ErrorListDetails.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.ErrorListDetails.cs index 50ac3a7f44..68cceb05d6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.ErrorListDetails.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.ErrorListDetails.cs @@ -2,116 +2,115 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +internal partial class LanguageServiceErrorListProvider { - internal partial class LanguageServiceErrorListProvider + /// + /// Captures details from various types of MSBuild events. + /// + internal struct ErrorListDetails { - /// - /// Captures details from various types of MSBuild events. - /// - internal struct ErrorListDetails - { - public string? Message { get; set; } + public string? Message { get; set; } - public string ProjectFile { get; set; } + public string ProjectFile { get; set; } - public string File { get; set; } + public string File { get; set; } - public string GetFileFullPath(string? projectFile) + public string GetFileFullPath(string? projectFile) + { + string? baseFilePath = ProjectFile; + if (Strings.IsNullOrEmpty(baseFilePath)) { - string? baseFilePath = ProjectFile; - if (Strings.IsNullOrEmpty(baseFilePath)) - { - baseFilePath = projectFile; - } - - if (!Strings.IsNullOrEmpty(baseFilePath) && !Strings.IsNullOrEmpty(File)) - { - return TryMakeRooted(baseFilePath, File); - } + baseFilePath = projectFile; + } - return string.Empty; + if (!Strings.IsNullOrEmpty(baseFilePath) && !Strings.IsNullOrEmpty(File)) + { + return TryMakeRooted(baseFilePath, File); } - public int LineNumber { get; set; } + return string.Empty; + } - public int EndLineNumber { get; set; } + public int LineNumber { get; set; } - /// - /// Gets the line number that should be reported to the VS error list. - /// ( - 1) to account for +1 that the error list applies. - /// - public int LineNumberForErrorList + public int EndLineNumber { get; set; } + + /// + /// Gets the line number that should be reported to the VS error list. + /// ( - 1) to account for +1 that the error list applies. + /// + public int LineNumberForErrorList + { + get { - get - { - // The VS error list uses 0-based line numbers so a -1 adjustment needs to be made. - // It's weird. We report "12" and they'll display "13". - return LineNumber > 0 ? LineNumber - 1 : 0; - } + // The VS error list uses 0-based line numbers so a -1 adjustment needs to be made. + // It's weird. We report "12" and they'll display "13". + return LineNumber > 0 ? LineNumber - 1 : 0; } + } - /// - /// Gets the line number that should be reported to the VS error list. - /// ( - 1) to account for +1 that the error list applies. - /// - public int EndLineNumberForErrorList + /// + /// Gets the line number that should be reported to the VS error list. + /// ( - 1) to account for +1 that the error list applies. + /// + public int EndLineNumberForErrorList + { + get { - get - { - // The VS error list uses 0-based line numbers so a -1 adjustment needs to be made. - // It's weird. We report "12" and they'll display "13". - return (EndLineNumber > LineNumber && EndLineNumber > 0) ? EndLineNumber - 1 : LineNumberForErrorList; - } + // The VS error list uses 0-based line numbers so a -1 adjustment needs to be made. + // It's weird. We report "12" and they'll display "13". + return (EndLineNumber > LineNumber && EndLineNumber > 0) ? EndLineNumber - 1 : LineNumberForErrorList; } + } - public int ColumnNumber { get; set; } + public int ColumnNumber { get; set; } - public int EndColumnNumber { get; set; } + public int EndColumnNumber { get; set; } - /// - /// Gets the column number that should be reported to the VS error list. - /// ( - 1) to account for +1 that the error list applies. - /// See , too. - /// - public int ColumnNumberForErrorList - { - get { return ColumnNumber > 0 ? ColumnNumber - 1 : 0; } - } + /// + /// Gets the column number that should be reported to the VS error list. + /// ( - 1) to account for +1 that the error list applies. + /// See , too. + /// + public int ColumnNumberForErrorList + { + get { return ColumnNumber > 0 ? ColumnNumber - 1 : 0; } + } - /// - /// Gets the column number that should be reported to the VS error list. - /// ( - 1) to account for +1 that the error list applies. - /// See , too. - /// - public int EndColumnNumberForErrorList - { - get { return (EndColumnNumber > ColumnNumber && EndColumnNumber > 0) ? EndColumnNumber - 1 : ColumnNumberForErrorList; } - } + /// + /// Gets the column number that should be reported to the VS error list. + /// ( - 1) to account for +1 that the error list applies. + /// See , too. + /// + public int EndColumnNumberForErrorList + { + get { return (EndColumnNumber > ColumnNumber && EndColumnNumber > 0) ? EndColumnNumber - 1 : ColumnNumberForErrorList; } + } - public string Code { get; set; } + public string Code { get; set; } - public VSTASKPRIORITY Priority { get; set; } + public VSTASKPRIORITY Priority { get; set; } - /// - /// Makes the specified path absolute if possible, otherwise return an empty string. - /// - /// The path used as the root if is relative. - /// An absolute or relative path. - /// An absolute path, or the empty string if invalid. - private static string TryMakeRooted(string basePath, string path) + /// + /// Makes the specified path absolute if possible, otherwise return an empty string. + /// + /// The path used as the root if is relative. + /// An absolute or relative path. + /// An absolute path, or the empty string if invalid. + private static string TryMakeRooted(string basePath, string path) + { + Requires.NotNullOrEmpty(basePath); + Requires.NotNullOrEmpty(path); + + try + { + return PathHelper.MakeRooted(basePath, path); + } + catch (ArgumentException) { - Requires.NotNullOrEmpty(basePath); - Requires.NotNullOrEmpty(path); - - try - { - return PathHelper.MakeRooted(basePath, path); - } - catch (ArgumentException) - { - return string.Empty; - } + return string.Empty; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.cs index 3a4db2b620..09e911bc85 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/LanguageServiceErrorListProvider.cs @@ -5,177 +5,176 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +/// +/// An implementation of that delegates onto the language +/// service so that it de-dupes warnings and errors between IntelliSense and build. +/// +[AppliesTo(ProjectCapability.DotNetLanguageService)] +[Export(typeof(IVsErrorListProvider))] +[Order(Order.Default)] +internal partial class LanguageServiceErrorListProvider : IVsErrorListProvider { - /// - /// An implementation of that delegates onto the language - /// service so that it de-dupes warnings and errors between IntelliSense and build. - /// - [AppliesTo(ProjectCapability.DotNetLanguageService)] - [Export(typeof(IVsErrorListProvider))] - [Order(Order.Default)] - internal partial class LanguageServiceErrorListProvider : IVsErrorListProvider + private readonly UnconfiguredProject _project; + private readonly IWorkspaceWriter _workspaceWriter; + + private readonly AsyncLazy _isLspPullDiagnosticsEnabled; + + /// + /// must be imported in the constructor in order for scope of this class's export to be correct. + /// + [ImportingConstructor] + public LanguageServiceErrorListProvider( + UnconfiguredProject project, + IWorkspaceWriter workspaceWriter, + IProjectSystemOptions projectSystemOptions, + JoinableTaskContext joinableTaskContext) { - private readonly UnconfiguredProject _project; - private readonly IWorkspaceWriter _workspaceWriter; - - private readonly AsyncLazy _isLspPullDiagnosticsEnabled; - - /// - /// must be imported in the constructor in order for scope of this class's export to be correct. - /// - [ImportingConstructor] - public LanguageServiceErrorListProvider( - UnconfiguredProject project, - IWorkspaceWriter workspaceWriter, - IProjectSystemOptions projectSystemOptions, - JoinableTaskContext joinableTaskContext) - { - _project = project; - _workspaceWriter = workspaceWriter; + _project = project; + _workspaceWriter = workspaceWriter; - _isLspPullDiagnosticsEnabled = new AsyncLazy( - async () => await projectSystemOptions.IsLspPullDiagnosticsEnabledAsync(CancellationToken.None), - joinableTaskContext.Factory); - } + _isLspPullDiagnosticsEnabled = new AsyncLazy( + async () => await projectSystemOptions.IsLspPullDiagnosticsEnabledAsync(CancellationToken.None), + joinableTaskContext.Factory); + } + + public void SuspendRefresh() + { + } + + public void ResumeRefresh() + { + } + + public async Task AddMessageAsync(TargetGeneratedError error) + { + Requires.NotNull(error); - public void SuspendRefresh() + // We only want to pass compiler, analyzers, etc to the language + // service, so we skip tasks that do not have a code + if (!TryExtractErrorListDetails(error.BuildEventArgs, out ErrorListDetails details) || string.IsNullOrEmpty(details.Code)) + return AddMessageResult.NotHandled; + + // When this feature flag is enabled, build diagnostics will be published by CPS and should not be passed to roslyn. + bool isLspPullDiagnosticsEnabled = await _isLspPullDiagnosticsEnabled.GetValueAsync(); + if (isLspPullDiagnosticsEnabled) { + return AddMessageResult.NotHandled; } - public void ResumeRefresh() + bool handled = false; + + if (await _workspaceWriter.IsEnabledAsync()) { + handled = await _workspaceWriter.WriteAsync(workspace => + { + try + { + workspace.ErrorReporter.ReportError2( + details.Message, + details.Code, + details.Priority, + details.LineNumberForErrorList, + details.ColumnNumberForErrorList, + details.EndLineNumberForErrorList, + details.EndColumnNumberForErrorList, + details.GetFileFullPath(_project.FullPath)); + + return TaskResult.True; + } + catch (NotImplementedException) + { // Language Service doesn't handle it, typically because file + // isn't in the project or because it doesn't have line/column + } + + return TaskResult.False; + }); } - public async Task AddMessageAsync(TargetGeneratedError error) - { - Requires.NotNull(error); + return handled ? AddMessageResult.HandledAndStopProcessing : AddMessageResult.NotHandled; + } - // We only want to pass compiler, analyzers, etc to the language - // service, so we skip tasks that do not have a code - if (!TryExtractErrorListDetails(error.BuildEventArgs, out ErrorListDetails details) || string.IsNullOrEmpty(details.Code)) - return AddMessageResult.NotHandled; + public Task ClearMessageFromTargetAsync(string targetName) + { + return Task.CompletedTask; + } - // When this feature flag is enabled, build diagnostics will be published by CPS and should not be passed to roslyn. - bool isLspPullDiagnosticsEnabled = await _isLspPullDiagnosticsEnabled.GetValueAsync(); - if (isLspPullDiagnosticsEnabled) + public async Task ClearAllAsync() + { + if (await _workspaceWriter.IsEnabledAsync()) + { + await _workspaceWriter.WriteAsync(workspace => { - return AddMessageResult.NotHandled; - } + workspace.ErrorReporter.ClearErrors(); - bool handled = false; - - if (await _workspaceWriter.IsEnabledAsync()) - { - handled = await _workspaceWriter.WriteAsync(workspace => - { - try - { - workspace.ErrorReporter.ReportError2( - details.Message, - details.Code, - details.Priority, - details.LineNumberForErrorList, - details.ColumnNumberForErrorList, - details.EndLineNumberForErrorList, - details.EndColumnNumberForErrorList, - details.GetFileFullPath(_project.FullPath)); - - return TaskResult.True; - } - catch (NotImplementedException) - { // Language Service doesn't handle it, typically because file - // isn't in the project or because it doesn't have line/column - } - - return TaskResult.False; - }); - } - - return handled ? AddMessageResult.HandledAndStopProcessing : AddMessageResult.NotHandled; + return Task.CompletedTask; + }); } + } - public Task ClearMessageFromTargetAsync(string targetName) + /// + /// Attempts to extract the details required by the VS Error List from an MSBuild build event. + /// + /// The build event. + /// The extracted details, or if was or of an unrecognized type. + internal static bool TryExtractErrorListDetails(BuildEventArgs? eventArgs, out ErrorListDetails result) + { + if (eventArgs is BuildErrorEventArgs errorMessage) { - return Task.CompletedTask; + result = new ErrorListDetails() + { + ProjectFile = errorMessage.ProjectFile, + File = errorMessage.File, + LineNumber = errorMessage.LineNumber, + EndLineNumber = errorMessage.EndLineNumber, + ColumnNumber = errorMessage.ColumnNumber, + EndColumnNumber = errorMessage.EndColumnNumber, + Code = errorMessage.Code, + Message = errorMessage.Message, + Priority = VSTASKPRIORITY.TP_HIGH, + }; + + return true; } - public async Task ClearAllAsync() + if (eventArgs is BuildWarningEventArgs warningMessage) { - if (await _workspaceWriter.IsEnabledAsync()) + result = new ErrorListDetails() { - await _workspaceWriter.WriteAsync(workspace => - { - workspace.ErrorReporter.ClearErrors(); - - return Task.CompletedTask; - }); - } + ProjectFile = warningMessage.ProjectFile, + File = warningMessage.File, + LineNumber = warningMessage.LineNumber, + EndLineNumber = warningMessage.EndLineNumber, + ColumnNumber = warningMessage.ColumnNumber, + EndColumnNumber = warningMessage.EndColumnNumber, + Code = warningMessage.Code, + Message = warningMessage.Message, + Priority = VSTASKPRIORITY.TP_NORMAL, + }; + + return true; } - /// - /// Attempts to extract the details required by the VS Error List from an MSBuild build event. - /// - /// The build event. - /// The extracted details, or if was or of an unrecognized type. - internal static bool TryExtractErrorListDetails(BuildEventArgs? eventArgs, out ErrorListDetails result) + if (eventArgs is CriticalBuildMessageEventArgs criticalMessage) { - if (eventArgs is BuildErrorEventArgs errorMessage) - { - result = new ErrorListDetails() - { - ProjectFile = errorMessage.ProjectFile, - File = errorMessage.File, - LineNumber = errorMessage.LineNumber, - EndLineNumber = errorMessage.EndLineNumber, - ColumnNumber = errorMessage.ColumnNumber, - EndColumnNumber = errorMessage.EndColumnNumber, - Code = errorMessage.Code, - Message = errorMessage.Message, - Priority = VSTASKPRIORITY.TP_HIGH, - }; - - return true; - } - - if (eventArgs is BuildWarningEventArgs warningMessage) + result = new ErrorListDetails() { - result = new ErrorListDetails() - { - ProjectFile = warningMessage.ProjectFile, - File = warningMessage.File, - LineNumber = warningMessage.LineNumber, - EndLineNumber = warningMessage.EndLineNumber, - ColumnNumber = warningMessage.ColumnNumber, - EndColumnNumber = warningMessage.EndColumnNumber, - Code = warningMessage.Code, - Message = warningMessage.Message, - Priority = VSTASKPRIORITY.TP_NORMAL, - }; - - return true; - } - - if (eventArgs is CriticalBuildMessageEventArgs criticalMessage) - { - result = new ErrorListDetails() - { - ProjectFile = criticalMessage.ProjectFile, - File = criticalMessage.File, - LineNumber = criticalMessage.LineNumber, - EndLineNumber = criticalMessage.EndLineNumber, - ColumnNumber = criticalMessage.ColumnNumber, - EndColumnNumber = criticalMessage.EndColumnNumber, - Code = criticalMessage.Code, - Message = criticalMessage.Message, - Priority = VSTASKPRIORITY.TP_LOW, - }; - - return true; - } - - result = default; - return false; + ProjectFile = criticalMessage.ProjectFile, + File = criticalMessage.File, + LineNumber = criticalMessage.LineNumber, + EndLineNumber = criticalMessage.EndLineNumber, + ColumnNumber = criticalMessage.ColumnNumber, + EndColumnNumber = criticalMessage.EndColumnNumber, + Code = criticalMessage.Code, + Message = criticalMessage.Message, + Priority = VSTASKPRIORITY.TP_LOW, + }; + + return true; } + + result = default; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs index 9b8f366df1..889bd07b1e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Build/SolutionBuildManager.cs @@ -4,169 +4,168 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +[Export(typeof(ISolutionBuildManager))] +internal sealed class SolutionBuildManager : OnceInitializedOnceDisposedAsync, ISolutionBuildManager { - [Export(typeof(ISolutionBuildManager))] - internal sealed class SolutionBuildManager : OnceInitializedOnceDisposedAsync, ISolutionBuildManager - { - private readonly IVsService _vsSolutionBuildManagerService; + private readonly IVsService _vsSolutionBuildManagerService; - private IVsSolutionBuildManager2? _vsSolutionBuildManager2; - private IVsSolutionBuildManager3? _vsSolutionBuildManager3; + private IVsSolutionBuildManager2? _vsSolutionBuildManager2; + private IVsSolutionBuildManager3? _vsSolutionBuildManager3; - [ImportingConstructor] - public SolutionBuildManager( - IVsService solutionBuildManagerService, - JoinableTaskContext joinableTaskContext) - : base(new(joinableTaskContext)) - { - _vsSolutionBuildManagerService = solutionBuildManagerService; - } + [ImportingConstructor] + public SolutionBuildManager( + IVsService solutionBuildManagerService, + JoinableTaskContext joinableTaskContext) + : base(new(joinableTaskContext)) + { + _vsSolutionBuildManagerService = solutionBuildManagerService; + } - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); - _vsSolutionBuildManager2 = await _vsSolutionBuildManagerService.GetValueAsync(cancellationToken); - _vsSolutionBuildManager3 = (IVsSolutionBuildManager3)_vsSolutionBuildManager2; - } + _vsSolutionBuildManager2 = await _vsSolutionBuildManagerService.GetValueAsync(cancellationToken); + _vsSolutionBuildManager3 = (IVsSolutionBuildManager3)_vsSolutionBuildManager2; + } - protected override Task DisposeCoreAsync(bool initialized) - { - return Task.CompletedTask; - } + protected override Task DisposeCoreAsync(bool initialized) + { + return Task.CompletedTask; + } - public async Task SubscribeSolutionEventsAsync(IVsUpdateSolutionEvents eventListener) - { - await InitializeAsync(); + public async Task SubscribeSolutionEventsAsync(IVsUpdateSolutionEvents eventListener) + { + await InitializeAsync(); - Assumes.NotNull(_vsSolutionBuildManager2); - Assumes.NotNull(_vsSolutionBuildManager3); + Assumes.NotNull(_vsSolutionBuildManager2); + Assumes.NotNull(_vsSolutionBuildManager3); - await JoinableFactory.SwitchToMainThreadAsync(); + await JoinableFactory.SwitchToMainThreadAsync(); - uint cookie3 = VSConstants.VSCOOKIE_NIL; + uint cookie3 = VSConstants.VSCOOKIE_NIL; - HResult.Verify( - _vsSolutionBuildManager2.AdviseUpdateSolutionEvents(eventListener, out uint cookie2), - $"Error advising solution events in {typeof(SolutionBuildManager)}."); + HResult.Verify( + _vsSolutionBuildManager2.AdviseUpdateSolutionEvents(eventListener, out uint cookie2), + $"Error advising solution events in {typeof(SolutionBuildManager)}."); - if (eventListener is IVsUpdateSolutionEvents3 events3) - { - int hr = _vsSolutionBuildManager3.AdviseUpdateSolutionEvents3(events3, out cookie3); + if (eventListener is IVsUpdateSolutionEvents3 events3) + { + int hr = _vsSolutionBuildManager3.AdviseUpdateSolutionEvents3(events3, out cookie3); - if (hr != HResult.OK) - { - // The first subscription succeeded while the second failed. - // We need to clean up the first subscription before throwing an exception. - _vsSolutionBuildManager2.UnadviseUpdateSolutionEvents(cookie2); + if (hr != HResult.OK) + { + // The first subscription succeeded while the second failed. + // We need to clean up the first subscription before throwing an exception. + _vsSolutionBuildManager2.UnadviseUpdateSolutionEvents(cookie2); - // Throw. - HResult.Verify(hr, $"Error advising solution events 3 in {typeof(SolutionBuildManager)}."); - } + // Throw. + HResult.Verify(hr, $"Error advising solution events 3 in {typeof(SolutionBuildManager)}."); } - - return new AsyncDisposable(async () => - { - await JoinableFactory.SwitchToMainThreadAsync(); - - int result2 = HResult.OK; - int result3 = HResult.OK; - - if (cookie2 != VSConstants.VSCOOKIE_NIL) - { - result2 = _vsSolutionBuildManager2.UnadviseUpdateSolutionEvents(cookie2); - } - - if (cookie3 != VSConstants.VSCOOKIE_NIL) - { - result3 = _vsSolutionBuildManager3.UnadviseUpdateSolutionEvents3(cookie3); - } - - // Defer any exception until this point, so that we ensure both subscriptions - // have a chance to be cleaned up. - HResult.Verify(result2, $"Error unadvising solution events 2 in {typeof(SolutionBuildManager)}."); - HResult.Verify(result3, $"Error unadvising solution events 3 in {typeof(SolutionBuildManager)}."); - }); } - public int QueryBuildManagerBusy() + return new AsyncDisposable(async () => { - Assumes.NotNull(_vsSolutionBuildManager2); + await JoinableFactory.SwitchToMainThreadAsync(); - JoinableFactory.Context.VerifyIsOnMainThread(); + int result2 = HResult.OK; + int result3 = HResult.OK; - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.QueryBuildManagerBusy(out int flags)); + if (cookie2 != VSConstants.VSCOOKIE_NIL) + { + result2 = _vsSolutionBuildManager2.UnadviseUpdateSolutionEvents(cookie2); + } - return flags; - } + if (cookie3 != VSConstants.VSCOOKIE_NIL) + { + result3 = _vsSolutionBuildManager3.UnadviseUpdateSolutionEvents3(cookie3); + } - public uint QueryBuildManagerBusyEx() - { - Assumes.NotNull(_vsSolutionBuildManager3); + // Defer any exception until this point, so that we ensure both subscriptions + // have a chance to be cleaned up. + HResult.Verify(result2, $"Error unadvising solution events 2 in {typeof(SolutionBuildManager)}."); + HResult.Verify(result3, $"Error unadvising solution events 3 in {typeof(SolutionBuildManager)}."); + }); + } - JoinableFactory.Context.VerifyIsOnMainThread(); + public int QueryBuildManagerBusy() + { + Assumes.NotNull(_vsSolutionBuildManager2); - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager3.QueryBuildManagerBusyEx(out uint flags)); + JoinableFactory.Context.VerifyIsOnMainThread(); - return flags; - } + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.QueryBuildManagerBusy(out int flags)); - public void SaveDocumentsBeforeBuild(IVsHierarchy hierarchy, uint itemId, uint docCookie) - { - Assumes.NotNull(_vsSolutionBuildManager2); + return flags; + } - JoinableFactory.Context.VerifyIsOnMainThread(); + public uint QueryBuildManagerBusyEx() + { + Assumes.NotNull(_vsSolutionBuildManager3); - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.SaveDocumentsBeforeBuild(hierarchy, itemId, docCookie)); - } + JoinableFactory.Context.VerifyIsOnMainThread(); - public void CalculateProjectDependencies() - { - Assumes.NotNull(_vsSolutionBuildManager2); + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager3.QueryBuildManagerBusyEx(out uint flags)); - JoinableFactory.Context.VerifyIsOnMainThread(); + return flags; + } - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.CalculateProjectDependencies()); - } + public void SaveDocumentsBeforeBuild(IVsHierarchy hierarchy, uint itemId, uint docCookie) + { + Assumes.NotNull(_vsSolutionBuildManager2); - public IVsHierarchy[] GetProjectDependencies(IVsHierarchy hierarchy) - { - Assumes.NotNull(_vsSolutionBuildManager2); + JoinableFactory.Context.VerifyIsOnMainThread(); - JoinableFactory.Context.VerifyIsOnMainThread(); + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.SaveDocumentsBeforeBuild(hierarchy, itemId, docCookie)); + } - // Find out how many dependent projects there are - uint[] dependencyCounts = new uint[1]; - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.GetProjectDependencies(hierarchy, 0, null, dependencyCounts)); - uint count = dependencyCounts[0]; + public void CalculateProjectDependencies() + { + Assumes.NotNull(_vsSolutionBuildManager2); - if (count == 0) - { - return Array.Empty(); - } + JoinableFactory.Context.VerifyIsOnMainThread(); - // Get all of the dependent projects, and add them to our list - var projectsArray = new IVsHierarchy[count]; - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.GetProjectDependencies(hierarchy, count, projectsArray, dependencyCounts)); - return projectsArray; - } + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.CalculateProjectDependencies()); + } + + public IVsHierarchy[] GetProjectDependencies(IVsHierarchy hierarchy) + { + Assumes.NotNull(_vsSolutionBuildManager2); - public void StartUpdateSpecificProjectConfigurations(IVsHierarchy[] hierarchy, uint[] buildFlags, uint dwFlags) + JoinableFactory.Context.VerifyIsOnMainThread(); + + // Find out how many dependent projects there are + uint[] dependencyCounts = new uint[1]; + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.GetProjectDependencies(hierarchy, 0, null, dependencyCounts)); + uint count = dependencyCounts[0]; + + if (count == 0) { - Assumes.NotNull(_vsSolutionBuildManager2); - - JoinableFactory.Context.VerifyIsOnMainThread(); - - ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.StartUpdateSpecificProjectConfigurations( - cProjs: (uint)hierarchy.Length, - rgpHier: hierarchy, - rgpcfg: null, - rgdwCleanFlags: null, - rgdwBuildFlags: buildFlags, - rgdwDeployFlags: null, - dwFlags: dwFlags, - fSuppressUI: 0)); + return Array.Empty(); } + + // Get all of the dependent projects, and add them to our list + var projectsArray = new IVsHierarchy[count]; + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.GetProjectDependencies(hierarchy, count, projectsArray, dependencyCounts)); + return projectsArray; + } + + public void StartUpdateSpecificProjectConfigurations(IVsHierarchy[] hierarchy, uint[] buildFlags, uint dwFlags) + { + Assumes.NotNull(_vsSolutionBuildManager2); + + JoinableFactory.Context.VerifyIsOnMainThread(); + + ErrorHandler.ThrowOnFailure(_vsSolutionBuildManager2.StartUpdateSpecificProjectConfigurations( + cProjs: (uint)hierarchy.Length, + rgpHier: hierarchy, + rgpcfg: null, + rgdwCleanFlags: null, + rgdwBuildFlags: buildFlags, + rgdwDeployFlags: null, + dwFlags: dwFlags, + fSuppressUI: 0)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProvider.cs index cffe50459c..70ad8def34 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProvider.cs @@ -3,32 +3,31 @@ using Microsoft.Build.Construction; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp; + +/// +/// Checks a legacy VB project for compatibility with the new project system. +/// +[SupportedProjectTypeGuid(ProjectType.LegacyCSharp)] +[Export(ExportContractNames.Extensions.SupportedProjectTypeGuid)] +[Export(typeof(IFlavoredProjectCompatibilityProvider))] +[ProjectTypeGuidFilter(ProjectType.LegacyCSharp)] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +internal class CSharpProjectCompatibilityProvider : IFlavoredProjectCompatibilityProvider { - /// - /// Checks a legacy VB project for compatibility with the new project system. - /// - [SupportedProjectTypeGuid(ProjectType.LegacyCSharp)] - [Export(ExportContractNames.Extensions.SupportedProjectTypeGuid)] - [Export(typeof(IFlavoredProjectCompatibilityProvider))] - [ProjectTypeGuidFilter(ProjectType.LegacyCSharp)] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - internal class CSharpProjectCompatibilityProvider : IFlavoredProjectCompatibilityProvider + [ImportingConstructor] + public CSharpProjectCompatibilityProvider() { - [ImportingConstructor] - public CSharpProjectCompatibilityProvider() - { - } + } - public Task IsProjectCompatibleAsync(ProjectRootElement project) - { - return TaskResult.True; - } + public Task IsProjectCompatibleAsync(ProjectRootElement project) + { + return TaskResult.True; + } - public Task IsProjectNeedBeUpgradedAsync(ProjectRootElement project) - { - // We need to fill this out: https://github.com/dotnet/roslyn/issues/11285 - return TaskResult.False; - } + public Task IsProjectNeedBeUpgradedAsync(ProjectRootElement project) + { + // We need to fill this out: https://github.com/dotnet/roslyn/issues/11285 + return TaskResult.False; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProvider.cs index d5bdf79b33..4a390164e4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProvider.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp; + +/// +/// Provides the C# implementation of . +/// +[Export(typeof(IItemTypeGuidProvider))] +[AppliesTo(ProjectCapabilities.CSharp)] +internal class CSharpProjectTypeGuidProvider : IItemTypeGuidProvider { - /// - /// Provides the C# implementation of . - /// - [Export(typeof(IItemTypeGuidProvider))] - [AppliesTo(ProjectCapabilities.CSharp)] - internal class CSharpProjectTypeGuidProvider : IItemTypeGuidProvider + [ImportingConstructor] + public CSharpProjectTypeGuidProvider() { - [ImportingConstructor] - public CSharpProjectTypeGuidProvider() - { - } + } - public Guid ProjectTypeGuid - { - get { return ProjectType.LegacyCSharpGuid; } - } + public Guid ProjectTypeGuid + { + get { return ProjectType.LegacyCSharpGuid; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPoint.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPoint.cs index f9e1e1230a..4bf2aca0cd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPoint.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPoint.cs @@ -3,66 +3,65 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.OLE.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint +namespace Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint; + +/// +/// This implementation is a copy from CPS +/// +internal class ConnectionPoint : IConnectionPoint + where TSinkType : class { - /// - /// This implementation is a copy from CPS - /// - internal class ConnectionPoint : IConnectionPoint - where TSinkType : class - { - private readonly Dictionary _sinks = new(); - private readonly ConnectionPointContainer _container; - private readonly IEventSource _source; + private readonly Dictionary _sinks = new(); + private readonly ConnectionPointContainer _container; + private readonly IEventSource _source; - private uint _nextCookie; + private uint _nextCookie; - internal ConnectionPoint(ConnectionPointContainer container, IEventSource source) - { - Requires.NotNull(container); - Requires.NotNull(source); + internal ConnectionPoint(ConnectionPointContainer container, IEventSource source) + { + Requires.NotNull(container); + Requires.NotNull(source); - _container = container; - _source = source; - _nextCookie = 1; - } + _container = container; + _source = source; + _nextCookie = 1; + } - public void Advise(object pUnkSink, out uint pdwCookie) + public void Advise(object pUnkSink, out uint pdwCookie) + { + if (pUnkSink is TSinkType sink) { - if (pUnkSink is TSinkType sink) - { - _sinks.Add(_nextCookie, sink); - pdwCookie = _nextCookie; - _source.OnSinkAdded(sink); - _nextCookie++; - return; - } - - Marshal.ThrowExceptionForHR(HResult.NoInterface); - pdwCookie = default; + _sinks.Add(_nextCookie, sink); + pdwCookie = _nextCookie; + _source.OnSinkAdded(sink); + _nextCookie++; + return; } - public void EnumConnections(out IEnumConnections ppEnum) - { - throw new NotImplementedException(); - } + Marshal.ThrowExceptionForHR(HResult.NoInterface); + pdwCookie = default; + } - public void GetConnectionInterface(out Guid pIID) - { - pIID = typeof(TSinkType).GUID; - } + public void EnumConnections(out IEnumConnections ppEnum) + { + throw new NotImplementedException(); + } - public void GetConnectionPointContainer(out IConnectionPointContainer ppCPC) - { - ppCPC = _container; - } + public void GetConnectionInterface(out Guid pIID) + { + pIID = typeof(TSinkType).GUID; + } - public void Unadvise(uint dwCookie) - { - // This will throw if the cookie is not in the list. - TSinkType sink = _sinks[dwCookie]; - _sinks.Remove(dwCookie); - _source.OnSinkRemoved(sink); - } + public void GetConnectionPointContainer(out IConnectionPointContainer ppCPC) + { + ppCPC = _container; + } + + public void Unadvise(uint dwCookie) + { + // This will throw if the cookie is not in the list. + TSinkType sink = _sinks[dwCookie]; + _sinks.Remove(dwCookie); + _source.OnSinkRemoved(sink); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPointContainer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPointContainer.cs index 72ad2f594f..20470bfb48 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPointContainer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/ConnectionPointContainer.cs @@ -3,37 +3,36 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.OLE.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint +namespace Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint; + +/// +/// This implementation is a copy from CPS +/// +[ComVisible(true)] +public class ConnectionPointContainer : IConnectionPointContainer { - /// - /// This implementation is a copy from CPS - /// - [ComVisible(true)] - public class ConnectionPointContainer : IConnectionPointContainer + private readonly Dictionary _connectionPoints = new(); + + internal ConnectionPointContainer() + { + } + + internal void AddEventSource(IEventSource source) + where SinkType : class + { + Requires.NotNull(source); + Verify.Operation(!_connectionPoints.ContainsKey(typeof(SinkType).GUID), "EventSource guid already added to the list of connection points"); + + _connectionPoints.Add(typeof(SinkType).GUID, new ConnectionPoint(this, source)); + } + + void IConnectionPointContainer.EnumConnectionPoints(out IEnumConnectionPoints ppEnum) + { + throw new NotImplementedException(); + } + + void IConnectionPointContainer.FindConnectionPoint(ref Guid riid, out IConnectionPoint ppCP) { - private readonly Dictionary _connectionPoints = new(); - - internal ConnectionPointContainer() - { - } - - internal void AddEventSource(IEventSource source) - where SinkType : class - { - Requires.NotNull(source); - Verify.Operation(!_connectionPoints.ContainsKey(typeof(SinkType).GUID), "EventSource guid already added to the list of connection points"); - - _connectionPoints.Add(typeof(SinkType).GUID, new ConnectionPoint(this, source)); - } - - void IConnectionPointContainer.EnumConnectionPoints(out IEnumConnectionPoints ppEnum) - { - throw new NotImplementedException(); - } - - void IConnectionPointContainer.FindConnectionPoint(ref Guid riid, out IConnectionPoint ppCP) - { - ppCP = _connectionPoints[riid]; - } + ppCP = _connectionPoints[riid]; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/IEventSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/IEventSource.cs index c3d2dd19c0..62f3ef4e3c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/IEventSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ConnectionPoint/IEventSource.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint +namespace Microsoft.VisualStudio.ProjectSystem.VS.ConnectionPoint; + +/// +/// This implementation is a copy from CPS +/// +internal interface IEventSource + where TSinkType : class { - /// - /// This implementation is a copy from CPS - /// - internal interface IEventSource - where TSinkType : class - { - void OnSinkAdded(TSinkType sink); + void OnSinkAdded(TSinkType sink); - void OnSinkRemoved(TSinkType sink); - } + void OnSinkRemoved(TSinkType sink); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CreateFileFromTemplateService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CreateFileFromTemplateService.cs index 48c3822c57..198a263563 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CreateFileFromTemplateService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/CreateFileFromTemplateService.cs @@ -4,69 +4,68 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// This service creates a file from a given file template. +/// +[Export(typeof(ICreateFileFromTemplateService))] +internal class CreateFileFromTemplateService : ICreateFileFromTemplateService { + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IVsUIService _dte; + private readonly ProjectProperties _properties; + + [ImportingConstructor] + public CreateFileFromTemplateService(IUnconfiguredProjectVsServices projectVsServices, IVsUIService dte, ProjectProperties properties) + { + _projectVsServices = projectVsServices; + _dte = dte; + _properties = properties; + } + /// - /// This service creates a file from a given file template. + /// Create a file with the given template file and add it to the parent node. /// - [Export(typeof(ICreateFileFromTemplateService))] - internal class CreateFileFromTemplateService : ICreateFileFromTemplateService + /// The name of the template zip file. + /// The path to the file to be created. + /// true if file is added successfully. + public async Task CreateFileAsync(string templateFile, string path) { - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IVsUIService _dte; - private readonly ProjectProperties _properties; + Requires.NotNull(templateFile); + Requires.NotNullOrEmpty(path); - [ImportingConstructor] - public CreateFileFromTemplateService(IUnconfiguredProjectVsServices projectVsServices, IVsUIService dte, ProjectProperties properties) - { - _projectVsServices = projectVsServices; - _dte = dte; - _properties = properties; - } + string directoryName = Path.GetDirectoryName(path); + string fileName = Path.GetFileName(path); - /// - /// Create a file with the given template file and add it to the parent node. - /// - /// The name of the template zip file. - /// The path to the file to be created. - /// true if file is added successfully. - public async Task CreateFileAsync(string templateFile, string path) - { - Requires.NotNull(templateFile); - Requires.NotNullOrEmpty(path); - - string directoryName = Path.GetDirectoryName(path); - string fileName = Path.GetFileName(path); + string? templateLanguage = await GetTemplateLanguageAsync(); + if (string.IsNullOrEmpty(templateLanguage)) + return false; - string? templateLanguage = await GetTemplateLanguageAsync(); - if (string.IsNullOrEmpty(templateLanguage)) - return false; + await _projectVsServices.ThreadingService.SwitchToUIThread(); - await _projectVsServices.ThreadingService.SwitchToUIThread(); + string templateFilePath = ((Solution2)_dte.Value.Solution).GetProjectItemTemplate(templateFile, templateLanguage); - string templateFilePath = ((Solution2)_dte.Value.Solution).GetProjectItemTemplate(templateFile, templateLanguage); + if (templateFilePath is not null) + { + HierarchyId parentId = _projectVsServices.VsProject.GetHierarchyId(directoryName); + var result = new VSADDRESULT[1]; + string[] files = new string[] { templateFilePath }; + _projectVsServices.VsProject.AddItemWithSpecific(parentId, VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD, fileName, (uint)files.Length, files, IntPtr.Zero, 0, Guid.Empty, null, Guid.Empty, result); - if (templateFilePath is not null) + if (result[0] == VSADDRESULT.ADDRESULT_Success) { - HierarchyId parentId = _projectVsServices.VsProject.GetHierarchyId(directoryName); - var result = new VSADDRESULT[1]; - string[] files = new string[] { templateFilePath }; - _projectVsServices.VsProject.AddItemWithSpecific(parentId, VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD, fileName, (uint)files.Length, files, IntPtr.Zero, 0, Guid.Empty, null, Guid.Empty, result); - - if (result[0] == VSADDRESULT.ADDRESULT_Success) - { - return true; - } + return true; } - - return false; } - private async Task GetTemplateLanguageAsync() - { - ConfigurationGeneral general = await _properties.GetConfigurationGeneralPropertiesAsync(); + return false; + } - return (string?)await general.TemplateLanguage.GetValueAsync(); - } + private async Task GetTemplateLanguageAsync() + { + ConfigurationGeneral general = await _properties.GetConfigurationGeneralPropertiesAsync(); + + return (string?)await general.TemplateLanguage.GetValueAsync(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ErrorProfileDebugTargetsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ErrorProfileDebugTargetsProvider.cs index 7f0576decb..65bfcf089f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ErrorProfileDebugTargetsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ErrorProfileDebugTargetsProvider.cs @@ -3,64 +3,63 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using ExportOrder = Microsoft.VisualStudio.ProjectSystem.OrderAttribute; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Handles the NoAction profile so that Ctrl-f5\F5 throws an error to the user +/// +[Export(typeof(IDebugProfileLaunchTargetsProvider))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +[ExportOrder(1000)] // High number so it called first +internal class ErrorProfileDebugTargetsProvider : IDebugProfileLaunchTargetsProvider { + [ImportingConstructor] + public ErrorProfileDebugTargetsProvider(ConfiguredProject configuredProject) + { + _configuredProject = configuredProject; + } + + private readonly ConfiguredProject _configuredProject; + /// - /// Handles the NoAction profile so that Ctrl-f5\F5 throws an error to the user + /// This provider handles the NoAction profile /// - [Export(typeof(IDebugProfileLaunchTargetsProvider))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - [ExportOrder(1000)] // High number so it called first - internal class ErrorProfileDebugTargetsProvider : IDebugProfileLaunchTargetsProvider + public bool SupportsProfile(ILaunchProfile profile) { - [ImportingConstructor] - public ErrorProfileDebugTargetsProvider(ConfiguredProject configuredProject) - { - _configuredProject = configuredProject; - } - - private readonly ConfiguredProject _configuredProject; + return string.Equals(profile.CommandName, LaunchSettingsProvider.ErrorProfileCommandName, StringComparisons.LaunchProfileCommandNames); + } - /// - /// This provider handles the NoAction profile - /// - public bool SupportsProfile(ILaunchProfile profile) - { - return string.Equals(profile.CommandName, LaunchSettingsProvider.ErrorProfileCommandName, StringComparisons.LaunchProfileCommandNames); - } + /// + /// Called just prior to launch to do additional work (put up ui, do special configuration etc). + /// + public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) + { + // Nothing to do here + return Task.CompletedTask; + } - /// - /// Called just prior to launch to do additional work (put up ui, do special configuration etc). - /// - public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) - { - // Nothing to do here - return Task.CompletedTask; - } + /// + /// Called just prior to launch to do additional work (put up ui, do special configuration etc). + /// + public Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) + { + // Nothing to do here + return Task.CompletedTask; + } - /// - /// Called just prior to launch to do additional work (put up ui, do special configuration etc). - /// - public Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) + /// + /// When F5\Ctrl-F5 is invoked on a NoAction profile and error is thrown to the user. Typical case is trying to run a + /// class library project + /// + public Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile) + { + if (activeProfile.OtherSettings is not null && + activeProfile.OtherSettings.TryGetValue("ErrorString", out object? objErrorString) && + objErrorString is string errorString) { - // Nothing to do here - return Task.CompletedTask; + throw new Exception(string.Format(VSResources.ErrorInProfilesFile2, Path.GetFileNameWithoutExtension(_configuredProject.UnconfiguredProject.FullPath), errorString)); } - /// - /// When F5\Ctrl-F5 is invoked on a NoAction profile and error is thrown to the user. Typical case is trying to run a - /// class library project - /// - public Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile) - { - if (activeProfile.OtherSettings is not null && - activeProfile.OtherSettings.TryGetValue("ErrorString", out object? objErrorString) && - objErrorString is string errorString) - { - throw new Exception(string.Format(VSResources.ErrorInProfilesFile2, Path.GetFileNameWithoutExtension(_configuredProject.UnconfiguredProject.FullPath), errorString)); - } - - throw new Exception(string.Format(VSResources.ErrorInProfilesFile, Path.GetFileNameWithoutExtension(_configuredProject.UnconfiguredProject.FullPath))); - } + throw new Exception(string.Format(VSResources.ErrorInProfilesFile, Path.GetFileNameWithoutExtension(_configuredProject.UnconfiguredProject.FullPath))); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider.cs index 576f9e28e3..0e2392135f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider.cs @@ -2,44 +2,43 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Interface definition used by the ProjectDebugger to decide how to launch a profile. The order +/// of the imports is important in that this determines the order which profiles will be tested +/// for support +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IDebugProfileLaunchTargetsProvider { /// - /// Interface definition used by the ProjectDebugger to decide how to launch a profile. The order - /// of the imports is important in that this determines the order which profiles will be tested - /// for support + /// Return true if this provider supports this profile type. /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IDebugProfileLaunchTargetsProvider - { - /// - /// Return true if this provider supports this profile type. - /// - bool SupportsProfile(ILaunchProfile profile); + bool SupportsProfile(ILaunchProfile profile); - /// - /// Called in response to an F5/Ctrl+F5 operation to get the debug launch settings to pass to the - /// debugger for the active profile - /// - Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); + /// + /// Called in response to an F5/Ctrl+F5 operation to get the debug launch settings to pass to the + /// debugger for the active profile + /// + Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); - /// - /// Called just prior to launch to allow the provider to do additional work. - /// - /// - /// Note that if is also implemented, - /// will be called instead of this. - /// - Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); + /// + /// Called just prior to launch to allow the provider to do additional work. + /// + /// + /// Note that if is also implemented, + /// will be called instead of this. + /// + Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); - /// - /// Called right after launch to allow the provider to do additional work. - /// - /// - /// Note that if is also implemented, - /// will be called instead of this. - /// - Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); - } + /// + /// Called right after launch to allow the provider to do additional work. + /// + /// + /// Note that if is also implemented, + /// will be called instead of this. + /// + Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider2.cs index 394ae34e7a..012674a60c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider2.cs @@ -2,20 +2,19 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Optional interface that can be cast from IDebugProfileLaunchTargetsProvider for those implementations which need to distinguish +/// calls to QueryDebugTargetsAsync that originate from IVsDebuggableProjectCfg:QueryDebugTargets, from calls that originate from +/// IVsDebuggableProjectCfg:DebugLaunch. If this interface is implemented, calls that originate from a debugLaunch will call +/// QueryDebugTargetsForDebugLaunchAsync(). Calls from QueryDebugTargets will call IDebugProfileLaunchTargetsProvider:QueryDebugTargetsAsync +/// +public interface IDebugProfileLaunchTargetsProvider2 { /// - /// Optional interface that can be cast from IDebugProfileLaunchTargetsProvider for those implementations which need to distinguish - /// calls to QueryDebugTargetsAsync that originate from IVsDebuggableProjectCfg:QueryDebugTargets, from calls that originate from - /// IVsDebuggableProjectCfg:DebugLaunch. If this interface is implemented, calls that originate from a debugLaunch will call - /// QueryDebugTargetsForDebugLaunchAsync(). Calls from QueryDebugTargets will call IDebugProfileLaunchTargetsProvider:QueryDebugTargetsAsync + /// Called in response to an F5/Ctrl+F5 operation to get the debug launch settings to pass to the + /// debugger for the active profile. /// - public interface IDebugProfileLaunchTargetsProvider2 - { - /// - /// Called in response to an F5/Ctrl+F5 operation to get the debug launch settings to pass to the - /// debugger for the active profile. - /// - Task> QueryDebugTargetsForDebugLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); - } + Task> QueryDebugTargetsForDebugLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider3.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider3.cs index e21d87e01b..ab7058ab0e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider3.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider3.cs @@ -2,14 +2,13 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Optional interface that is used to tell whether a project should appear in +/// the Startup drop down list. +/// +public interface IDebugProfileLaunchTargetsProvider3 { - /// - /// Optional interface that is used to tell whether a project should appear in - /// the Startup drop down list. - /// - public interface IDebugProfileLaunchTargetsProvider3 - { - Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); - } + Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider4.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider4.cs index b054063c50..2c1d3199ef 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider4.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IDebugProfileLaunchTargetsProvider4.cs @@ -3,28 +3,27 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Optional interface that allows the provider to receive more information about +/// launch settings and started processes. +/// +public interface IDebugProfileLaunchTargetsProvider4 { /// - /// Optional interface that allows the provider to receive more information about - /// launch settings and started processes. + /// Called just prior to launch to allow the provider to do additional work. /// - public interface IDebugProfileLaunchTargetsProvider4 - { - /// - /// Called just prior to launch to allow the provider to do additional work. - /// - /// - /// Note this will be called instead of . - /// - Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList debugLaunchSettings); + /// + /// Note this will be called instead of . + /// + Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList debugLaunchSettings); - /// - /// Called right after launch to allow the provider to do additional work. - /// - /// - /// Note this will be called instead of . - /// - Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList processInfos); - } + /// + /// Called right after launch to allow the provider to do additional work. + /// + /// + /// Note this will be called instead of . + /// + Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList processInfos); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IStartupProjectProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IStartupProjectProvider.cs index 9c2f9dac54..401ccfa829 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IStartupProjectProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/IStartupProjectProvider.cs @@ -2,17 +2,16 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Interface definition used by StartupProjectRegistrar to display only debuggable projects +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Extension)] +internal interface IStartupProjectProvider { /// - /// Interface definition used by StartupProjectRegistrar to display only debuggable projects + /// Returns true if this project can be selected as a startup project. /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Extension)] - internal interface IStartupProjectProvider - { - /// - /// Returns true if this project can be selected as a startup project. - /// - Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions); - } + Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProvider.cs index 5a51e71245..2763823e37 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProvider.cs @@ -6,361 +6,360 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// The exported CPS debugger for all types of K projects (web, consoles, class libraries). Defers to +/// other types to get the DebugTarget information to launch. +/// +[ExportDebugger(ProjectDebugger.SchemaName)] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal class LaunchProfilesDebugLaunchProvider : DebugLaunchProviderBase, IDeployedProjectItemMappingProvider, IStartupProjectProvider { + private readonly IVsService _vsDebuggerService; + // Launch providers to enforce requirements for debuggable projects + private readonly ILaunchSettingsProvider _launchSettingsProvider; + private IDebugProfileLaunchTargetsProvider? _lastLaunchProvider; + + [ImportingConstructor] + public LaunchProfilesDebugLaunchProvider( + ConfiguredProject configuredProject, + ILaunchSettingsProvider launchSettingsProvider, + IVsService vsDebuggerService) + : base(configuredProject) + { + _launchSettingsProvider = launchSettingsProvider; + _vsDebuggerService = vsDebuggerService; + + LaunchTargetsProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: configuredProject.UnconfiguredProject); + } + + /// + /// Import the LaunchTargetProviders which know how to run profiles + /// + [ImportMany] + public OrderPrecedenceImportCollection LaunchTargetsProviders { get; } + + /// + /// Called by CPS to determine whether we can launch + /// + public override Task CanLaunchAsync(DebugLaunchOptions launchOptions) => TaskResult.True; + /// - /// The exported CPS debugger for all types of K projects (web, consoles, class libraries). Defers to - /// other types to get the DebugTarget information to launch. + /// Called by StartupProjectRegistrar to determine whether this project should appear in the Startup list. /// - [ExportDebugger(ProjectDebugger.SchemaName)] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal class LaunchProfilesDebugLaunchProvider : DebugLaunchProviderBase, IDeployedProjectItemMappingProvider, IStartupProjectProvider + public async Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions) { - private readonly IVsService _vsDebuggerService; - // Launch providers to enforce requirements for debuggable projects - private readonly ILaunchSettingsProvider _launchSettingsProvider; - private IDebugProfileLaunchTargetsProvider? _lastLaunchProvider; - - [ImportingConstructor] - public LaunchProfilesDebugLaunchProvider( - ConfiguredProject configuredProject, - ILaunchSettingsProvider launchSettingsProvider, - IVsService vsDebuggerService) - : base(configuredProject) + if (await GetActiveProfileAsync() is ILaunchProfile activeProfile) { - _launchSettingsProvider = launchSettingsProvider; - _vsDebuggerService = vsDebuggerService; + // Now find the DebugTargets provider for this profile + IDebugProfileLaunchTargetsProvider? launchProvider = GetLaunchTargetsProvider(activeProfile); + + if (launchProvider is IDebugProfileLaunchTargetsProvider3 provider3) + { + return await provider3.CanBeStartupProjectAsync(launchOptions, activeProfile); + } - LaunchTargetsProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: configuredProject.UnconfiguredProject); + // Maintain backwards compat + return true; } - /// - /// Import the LaunchTargetProviders which know how to run profiles - /// - [ImportMany] - public OrderPrecedenceImportCollection LaunchTargetsProviders { get; } - - /// - /// Called by CPS to determine whether we can launch - /// - public override Task CanLaunchAsync(DebugLaunchOptions launchOptions) => TaskResult.True; - - /// - /// Called by StartupProjectRegistrar to determine whether this project should appear in the Startup list. - /// - public async Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions) - { - if (await GetActiveProfileAsync() is ILaunchProfile activeProfile) - { - // Now find the DebugTargets provider for this profile - IDebugProfileLaunchTargetsProvider? launchProvider = GetLaunchTargetsProvider(activeProfile); + // If we can't identify the active launch profile, we can't start the project. + return false; + } + + private async Task GetActiveProfileAsync() + { + ILaunchSettings currentProfiles = await _launchSettingsProvider.WaitForFirstSnapshot(); + ILaunchProfile? activeProfile = currentProfiles.ActiveProfile; - if (launchProvider is IDebugProfileLaunchTargetsProvider3 provider3) - { - return await provider3.CanBeStartupProjectAsync(launchOptions, activeProfile); - } + return activeProfile; + } - // Maintain backwards compat - return true; - } + /// + /// This is called to query the list of debug targets + /// + public override Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions) + { + return QueryDebugTargetsInternalAsync(launchOptions, fromDebugLaunch: false); + } - // If we can't identify the active launch profile, we can't start the project. - return false; - } + /// + /// This is called on F5 to return the list of debug targets. What is returned depends on the debug provider extensions + /// which understands how to launch the currently active profile type. + /// + private async Task> QueryDebugTargetsInternalAsync(DebugLaunchOptions launchOptions, bool fromDebugLaunch) + { + ILaunchProfile? activeProfile = await GetActiveProfileAsync(); - private async Task GetActiveProfileAsync() + if (activeProfile is null) { - ILaunchSettings currentProfiles = await _launchSettingsProvider.WaitForFirstSnapshot(); - ILaunchProfile? activeProfile = currentProfiles.ActiveProfile; - - return activeProfile; + throw new Exception(VSResources.ActiveLaunchProfileNotFound); } - /// - /// This is called to query the list of debug targets - /// - public override Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions) + // Now find the DebugTargets provider for this profile + IDebugProfileLaunchTargetsProvider launchProvider = GetLaunchTargetsProvider(activeProfile) ?? + throw new Exception(string.Format(VSResources.DontKnowHowToRunProfile_2, activeProfile.Name, activeProfile.CommandName)); + + IReadOnlyList launchSettings; + if (fromDebugLaunch && launchProvider is IDebugProfileLaunchTargetsProvider2 launchProvider2) { - return QueryDebugTargetsInternalAsync(launchOptions, fromDebugLaunch: false); + launchSettings = await launchProvider2.QueryDebugTargetsForDebugLaunchAsync(launchOptions, activeProfile); } - - /// - /// This is called on F5 to return the list of debug targets. What is returned depends on the debug provider extensions - /// which understands how to launch the currently active profile type. - /// - private async Task> QueryDebugTargetsInternalAsync(DebugLaunchOptions launchOptions, bool fromDebugLaunch) + else { - ILaunchProfile? activeProfile = await GetActiveProfileAsync(); - - if (activeProfile is null) - { - throw new Exception(VSResources.ActiveLaunchProfileNotFound); - } + launchSettings = await launchProvider.QueryDebugTargetsAsync(launchOptions, activeProfile); + } - // Now find the DebugTargets provider for this profile - IDebugProfileLaunchTargetsProvider launchProvider = GetLaunchTargetsProvider(activeProfile) ?? - throw new Exception(string.Format(VSResources.DontKnowHowToRunProfile_2, activeProfile.Name, activeProfile.CommandName)); + _lastLaunchProvider = launchProvider; + return launchSettings; + } - IReadOnlyList launchSettings; - if (fromDebugLaunch && launchProvider is IDebugProfileLaunchTargetsProvider2 launchProvider2) - { - launchSettings = await launchProvider2.QueryDebugTargetsForDebugLaunchAsync(launchOptions, activeProfile); - } - else + /// + /// Returns the provider which knows how to launch the profile type. + /// + public IDebugProfileLaunchTargetsProvider? GetLaunchTargetsProvider(ILaunchProfile profile) + { + // We search through the imports in order to find the one which supports the profile + foreach (Lazy provider in LaunchTargetsProviders) + { + if (provider.Value.SupportsProfile(profile)) { - launchSettings = await launchProvider.QueryDebugTargetsAsync(launchOptions, activeProfile); + return provider.Value; } - - _lastLaunchProvider = launchProvider; - return launchSettings; } - /// - /// Returns the provider which knows how to launch the profile type. - /// - public IDebugProfileLaunchTargetsProvider? GetLaunchTargetsProvider(ILaunchProfile profile) - { - // We search through the imports in order to find the one which supports the profile - foreach (Lazy provider in LaunchTargetsProviders) - { - if (provider.Value.SupportsProfile(profile)) - { - return provider.Value; - } - } + return null; + } - return null; - } + private class LaunchCompleteCallback : IVsDebuggerLaunchCompletionCallback + { + private readonly DebugLaunchOptions _launchOptions; + private readonly IDebugProfileLaunchTargetsProvider? _targetsProvider; + private readonly ILaunchProfile _activeProfile; + private readonly IProjectThreadingService _threadingService; - private class LaunchCompleteCallback : IVsDebuggerLaunchCompletionCallback + public LaunchCompleteCallback(IProjectThreadingService threadingService, DebugLaunchOptions launchOptions, IDebugProfileLaunchTargetsProvider? targetsProvider, ILaunchProfile activeProfile) { - private readonly DebugLaunchOptions _launchOptions; - private readonly IDebugProfileLaunchTargetsProvider? _targetsProvider; - private readonly ILaunchProfile _activeProfile; - private readonly IProjectThreadingService _threadingService; + _threadingService = threadingService; + _launchOptions = launchOptions; + _targetsProvider = targetsProvider; + _activeProfile = activeProfile; + } - public LaunchCompleteCallback(IProjectThreadingService threadingService, DebugLaunchOptions launchOptions, IDebugProfileLaunchTargetsProvider? targetsProvider, ILaunchProfile activeProfile) + public void OnComplete(int hr, uint debugTargetCount, VsDebugTargetProcessInfo[] processInfoArray) + { + if (_targetsProvider is IDebugProfileLaunchTargetsProvider4 targetsProvider4) { - _threadingService = threadingService; - _launchOptions = launchOptions; - _targetsProvider = targetsProvider; - _activeProfile = activeProfile; + _threadingService.ExecuteSynchronously(() => targetsProvider4.OnAfterLaunchAsync(_launchOptions, _activeProfile, processInfoArray)); } - - public void OnComplete(int hr, uint debugTargetCount, VsDebugTargetProcessInfo[] processInfoArray) + else if (_targetsProvider is not null) { - if (_targetsProvider is IDebugProfileLaunchTargetsProvider4 targetsProvider4) - { - _threadingService.ExecuteSynchronously(() => targetsProvider4.OnAfterLaunchAsync(_launchOptions, _activeProfile, processInfoArray)); - } - else if (_targetsProvider is not null) - { - _threadingService.ExecuteSynchronously(() => _targetsProvider.OnAfterLaunchAsync(_launchOptions, _activeProfile)); - } + _threadingService.ExecuteSynchronously(() => _targetsProvider.OnAfterLaunchAsync(_launchOptions, _activeProfile)); } - }; + } + }; - /// - /// Overridden to direct the launch to the current active provider as determined by the active launch profile - /// - public override async Task LaunchAsync(DebugLaunchOptions launchOptions) - { - IReadOnlyList targets = await QueryDebugTargetsInternalAsync(launchOptions, fromDebugLaunch: true); + /// + /// Overridden to direct the launch to the current active provider as determined by the active launch profile + /// + public override async Task LaunchAsync(DebugLaunchOptions launchOptions) + { + IReadOnlyList targets = await QueryDebugTargetsInternalAsync(launchOptions, fromDebugLaunch: true); - ILaunchProfile? activeProfile = _launchSettingsProvider.ActiveProfile; + ILaunchProfile? activeProfile = _launchSettingsProvider.ActiveProfile; - Assumes.NotNull(activeProfile); + Assumes.NotNull(activeProfile); - IDebugProfileLaunchTargetsProvider? targetProvider = GetLaunchTargetsProvider(activeProfile); - if (targetProvider is IDebugProfileLaunchTargetsProvider4 targetsProvider4) - { - await targetsProvider4.OnBeforeLaunchAsync(launchOptions, activeProfile, targets); - } - else if (targetProvider is not null) - { - await targetProvider.OnBeforeLaunchAsync(launchOptions, activeProfile); - } - - await DoLaunchAsync(new LaunchCompleteCallback(ThreadingService, launchOptions, targetProvider, activeProfile), targets.ToArray()); + IDebugProfileLaunchTargetsProvider? targetProvider = GetLaunchTargetsProvider(activeProfile); + if (targetProvider is IDebugProfileLaunchTargetsProvider4 targetsProvider4) + { + await targetsProvider4.OnBeforeLaunchAsync(launchOptions, activeProfile, targets); } + else if (targetProvider is not null) + { + await targetProvider.OnBeforeLaunchAsync(launchOptions, activeProfile); + } + + await DoLaunchAsync(new LaunchCompleteCallback(ThreadingService, launchOptions, targetProvider, activeProfile), targets.ToArray()); + } - /// - /// Launches the Visual Studio debugger. - /// - protected async Task DoLaunchAsync(IVsDebuggerLaunchCompletionCallback cb, params IDebugLaunchSettings[] launchSettings) + /// + /// Launches the Visual Studio debugger. + /// + protected async Task DoLaunchAsync(IVsDebuggerLaunchCompletionCallback cb, params IDebugLaunchSettings[] launchSettings) + { + if (launchSettings.Length == 0) { - if (launchSettings.Length == 0) - { - cb.OnComplete(0, 0, null); - return; - } + cb.OnComplete(0, 0, null); + return; + } - VsDebugTargetInfo4[] launchSettingsNative = launchSettings.Select(GetDebuggerStruct4).ToArray(); + VsDebugTargetInfo4[] launchSettingsNative = launchSettings.Select(GetDebuggerStruct4).ToArray(); - try - { - // The debugger needs to be called on the UI thread - await ThreadingService.SwitchToUIThread(); + try + { + // The debugger needs to be called on the UI thread + await ThreadingService.SwitchToUIThread(); - IVsDebuggerLaunchAsync shellDebugger = await _vsDebuggerService.GetValueAsync(); - shellDebugger.LaunchDebugTargetsAsync((uint)launchSettingsNative.Length, launchSettingsNative, cb); - } - finally + IVsDebuggerLaunchAsync shellDebugger = await _vsDebuggerService.GetValueAsync(); + shellDebugger.LaunchDebugTargetsAsync((uint)launchSettingsNative.Length, launchSettingsNative, cb); + } + finally + { + // Free up the memory allocated to the (mostly) managed debugger structure. + foreach (VsDebugTargetInfo4 nativeStruct in launchSettingsNative) { - // Free up the memory allocated to the (mostly) managed debugger structure. - foreach (VsDebugTargetInfo4 nativeStruct in launchSettingsNative) - { - FreeVsDebugTargetInfoStruct(nativeStruct); - } + FreeVsDebugTargetInfoStruct(nativeStruct); } } + } - /// - /// Copy information over from the contract struct to the native one. - /// - /// The native struct. - internal static VsDebugTargetInfo4 GetDebuggerStruct4(IDebugLaunchSettings info) + /// + /// Copy information over from the contract struct to the native one. + /// + /// The native struct. + internal static VsDebugTargetInfo4 GetDebuggerStruct4(IDebugLaunchSettings info) + { + var debugInfo = new VsDebugTargetInfo4 { - var debugInfo = new VsDebugTargetInfo4 - { - // **Begin common section -- keep this in sync with GetDebuggerStruct** - dlo = (uint)info.LaunchOperation, - LaunchFlags = (uint)info.LaunchOptions, - bstrRemoteMachine = info.RemoteMachine, - bstrArg = info.Arguments, - bstrCurDir = info.CurrentDirectory, - bstrExe = info.Executable, - - bstrEnv = GetSerializedEnvironmentString(info.Environment), - guidLaunchDebugEngine = info.LaunchDebugEngineGuid - }; + // **Begin common section -- keep this in sync with GetDebuggerStruct** + dlo = (uint)info.LaunchOperation, + LaunchFlags = (uint)info.LaunchOptions, + bstrRemoteMachine = info.RemoteMachine, + bstrArg = info.Arguments, + bstrCurDir = info.CurrentDirectory, + bstrExe = info.Executable, + + bstrEnv = GetSerializedEnvironmentString(info.Environment), + guidLaunchDebugEngine = info.LaunchDebugEngineGuid + }; - var guids = new List(1 + info.AdditionalDebugEngines?.Count ?? 0) - { - info.LaunchDebugEngineGuid - }; + var guids = new List(1 + info.AdditionalDebugEngines?.Count ?? 0) + { + info.LaunchDebugEngineGuid + }; - if (info.AdditionalDebugEngines is not null) - { - guids.AddRange(info.AdditionalDebugEngines); - } + if (info.AdditionalDebugEngines is not null) + { + guids.AddRange(info.AdditionalDebugEngines); + } - debugInfo.dwDebugEngineCount = (uint)guids.Count; + debugInfo.dwDebugEngineCount = (uint)guids.Count; - byte[] guidBytes = GetGuidBytes(guids); - debugInfo.pDebugEngines = Marshal.AllocCoTaskMem(guidBytes.Length); - Marshal.Copy(guidBytes, 0, debugInfo.pDebugEngines, guidBytes.Length); + byte[] guidBytes = GetGuidBytes(guids); + debugInfo.pDebugEngines = Marshal.AllocCoTaskMem(guidBytes.Length); + Marshal.Copy(guidBytes, 0, debugInfo.pDebugEngines, guidBytes.Length); - debugInfo.guidPortSupplier = info.PortSupplierGuid; - debugInfo.bstrPortName = info.PortName; - debugInfo.bstrOptions = info.Options; - debugInfo.fSendToOutputWindow = info.SendToOutputWindow ? true : false; - debugInfo.dwProcessId = unchecked((uint)info.ProcessId); - debugInfo.pUnknown = info.Unknown; - debugInfo.guidProcessLanguage = info.ProcessLanguageGuid; + debugInfo.guidPortSupplier = info.PortSupplierGuid; + debugInfo.bstrPortName = info.PortName; + debugInfo.bstrOptions = info.Options; + debugInfo.fSendToOutputWindow = info.SendToOutputWindow ? true : false; + debugInfo.dwProcessId = unchecked((uint)info.ProcessId); + debugInfo.pUnknown = info.Unknown; + debugInfo.guidProcessLanguage = info.ProcessLanguageGuid; - // **End common section** + // **End common section** - if (info.StandardErrorHandle != IntPtr.Zero || info.StandardInputHandle != IntPtr.Zero || info.StandardOutputHandle != IntPtr.Zero) + if (info.StandardErrorHandle != IntPtr.Zero || info.StandardInputHandle != IntPtr.Zero || info.StandardOutputHandle != IntPtr.Zero) + { + var processStartupInfo = new VsDebugStartupInfo { - var processStartupInfo = new VsDebugStartupInfo - { - hStdInput = unchecked(info.StandardInputHandle), - hStdOutput = unchecked(info.StandardOutputHandle), - hStdError = unchecked(info.StandardErrorHandle), - flags = (uint)__DSI_FLAGS.DSI_USESTDHANDLES, - }; - debugInfo.pStartupInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(processStartupInfo)); - Marshal.StructureToPtr(processStartupInfo, debugInfo.pStartupInfo, false); - } + hStdInput = unchecked(info.StandardInputHandle), + hStdOutput = unchecked(info.StandardOutputHandle), + hStdError = unchecked(info.StandardErrorHandle), + flags = (uint)__DSI_FLAGS.DSI_USESTDHANDLES, + }; + debugInfo.pStartupInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(processStartupInfo)); + Marshal.StructureToPtr(processStartupInfo, debugInfo.pStartupInfo, false); + } - debugInfo.AppPackageLaunchInfo = info.AppPackageLaunchInfo; - debugInfo.project = info.Project; + debugInfo.AppPackageLaunchInfo = info.AppPackageLaunchInfo; + debugInfo.project = info.Project; - return debugInfo; - } + return debugInfo; + } - /// - /// Frees memory allocated by GetDebuggerStruct. - /// - internal static void FreeVsDebugTargetInfoStruct(VsDebugTargetInfo4 nativeStruct) + /// + /// Frees memory allocated by GetDebuggerStruct. + /// + internal static void FreeVsDebugTargetInfoStruct(VsDebugTargetInfo4 nativeStruct) + { + if (nativeStruct.pDebugEngines != IntPtr.Zero) { - if (nativeStruct.pDebugEngines != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(nativeStruct.pDebugEngines); - } - - if (nativeStruct.pStartupInfo != IntPtr.Zero) - { - Marshal.DestroyStructure(nativeStruct.pStartupInfo, typeof(VsDebugStartupInfo)); - Marshal.FreeCoTaskMem(nativeStruct.pStartupInfo); - } + Marshal.FreeCoTaskMem(nativeStruct.pDebugEngines); } - /// - /// Converts the environment key value pairs to a valid environment string of the form - /// key=value/0key2=value2/0/0, with nulls between each entry and a double null terminator. - /// - private static string? GetSerializedEnvironmentString(IDictionary environment) + if (nativeStruct.pStartupInfo != IntPtr.Zero) { - // If no dictionary was set, or its empty, the debugger wants null for its environment block. - if (environment is null || environment.Count == 0) - { - return null; - } + Marshal.DestroyStructure(nativeStruct.pStartupInfo, typeof(VsDebugStartupInfo)); + Marshal.FreeCoTaskMem(nativeStruct.pStartupInfo); + } + } - // Collect all the variables as a null delimited list of key=value pairs. - var result = PooledStringBuilder.GetInstance(); - foreach ((string key, string value) in environment) - { - result.Append(key); - result.Append('='); - result.Append(value); - result.Append('\0'); - } + /// + /// Converts the environment key value pairs to a valid environment string of the form + /// key=value/0key2=value2/0/0, with nulls between each entry and a double null terminator. + /// + private static string? GetSerializedEnvironmentString(IDictionary environment) + { + // If no dictionary was set, or its empty, the debugger wants null for its environment block. + if (environment is null || environment.Count == 0) + { + return null; + } - // Add a final list-terminating null character. - // This is sent to native code as a BSTR and no null is added automatically. - // But the contract of the format of the data requires that this be a null-delimited, - // null-terminated list. + // Collect all the variables as a null delimited list of key=value pairs. + var result = PooledStringBuilder.GetInstance(); + foreach ((string key, string value) in environment) + { + result.Append(key); + result.Append('='); + result.Append(value); result.Append('\0'); - return result.ToStringAndFree(); } - /// - /// Collects an array of GUIDs into an array of bytes. - /// - /// - /// The order of the GUIDs are preserved, and each GUID is copied exactly one after the other in the byte array. - /// - private static byte[] GetGuidBytes(IList guids) - { - const int sizeOfGuid = 16; - byte[] bytes = new byte[guids.Count * sizeOfGuid]; - for (int i = 0; i < guids.Count; i++) - { - byte[] guidBytes = guids[i].ToByteArray(); - guidBytes.CopyTo(bytes, i * sizeOfGuid); - } + // Add a final list-terminating null character. + // This is sent to native code as a BSTR and no null is added automatically. + // But the contract of the format of the data requires that this be a null-delimited, + // null-terminated list. + result.Append('\0'); + return result.ToStringAndFree(); + } - return bytes; + /// + /// Collects an array of GUIDs into an array of bytes. + /// + /// + /// The order of the GUIDs are preserved, and each GUID is copied exactly one after the other in the byte array. + /// + private static byte[] GetGuidBytes(IList guids) + { + const int sizeOfGuid = 16; + byte[] bytes = new byte[guids.Count * sizeOfGuid]; + for (int i = 0; i < guids.Count; i++) + { + byte[] guidBytes = guids[i].ToByteArray(); + guidBytes.CopyTo(bytes, i * sizeOfGuid); } - /// - /// IDeployedProjectItemMappingProvider - /// Implemented so that we can map URL's back to local file item paths - /// - public bool TryGetProjectItemPathFromDeployedPath(string deployedPath, out string? localPath) - { - // Just delegate to the last provider. It needs to figure out how best to map the items - if (_lastLaunchProvider is IDeployedProjectItemMappingProvider deployedItemMapper) - { - return deployedItemMapper.TryGetProjectItemPathFromDeployedPath(deployedPath, out localPath); - } + return bytes; + } - // Return false to allow normal processing - localPath = null; - return false; + /// + /// IDeployedProjectItemMappingProvider + /// Implemented so that we can map URL's back to local file item paths + /// + public bool TryGetProjectItemPathFromDeployedPath(string deployedPath, out string? localPath) + { + // Just delegate to the last provider. It needs to figure out how best to map the items + if (_lastLaunchProvider is IDeployedProjectItemMappingProvider deployedItemMapper) + { + return deployedItemMapper.TryGetProjectItemPathFromDeployedPath(deployedPath, out localPath); } + + // Return false to allow normal processing + localPath = null; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProvider.cs index f819a0971e..6dbb7d3a73 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProvider.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +[Export(typeof(IDebugPageGuidProvider))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +[Order(Order.Default)] +internal class LaunchProfilesDebugPageGuidProvider : IDebugPageGuidProvider { - [Export(typeof(IDebugPageGuidProvider))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - [Order(Order.Default)] - internal class LaunchProfilesDebugPageGuidProvider : IDebugPageGuidProvider - { - // This is the Guid of C#, VB and F# Debug property page - private static readonly Task s_guid = Task.FromResult(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}")); + // This is the Guid of C#, VB and F# Debug property page + private static readonly Task s_guid = Task.FromResult(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}")); - public Task GetDebugPropertyPageGuidAsync() - { - return s_guid; - } + public Task GetDebugPropertyPageGuidAsync() + { + return s_guid; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ProjectLaunchTargetsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ProjectLaunchTargetsProvider.cs index b222d483fe..ceaad6cc68 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ProjectLaunchTargetsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/ProjectLaunchTargetsProvider.cs @@ -14,566 +14,565 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Provides QueryDebugTargetsAsync() support for running the project output or any random executable. It is not an exported +/// CPS debugger but hooks into the launch profiles extensibility point. The order of this provider is +/// near the bottom to ensure other providers get chance to handle it first +/// +[Export(typeof(IDebugProfileLaunchTargetsProvider))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +[Order(Order.Default)] // The higher the number the higher priority and we want this one last +internal class ProjectLaunchTargetsProvider : + IDebugProfileLaunchTargetsProvider, + IDebugProfileLaunchTargetsProvider2, + IDebugProfileLaunchTargetsProvider3, + IDebugProfileLaunchTargetsProvider4 { + private static readonly char[] s_escapedChars = new[] { '^', '<', '>', '&' }; + private readonly ConfiguredProject _project; + private readonly IUnconfiguredProjectVsServices _unconfiguredProjectVsServices; + private readonly IDebugTokenReplacer _tokenReplacer; + private readonly IFileSystem _fileSystem; + private readonly IEnvironmentHelper _environment; + private readonly IActiveDebugFrameworkServices _activeDebugFramework; + private readonly IProjectThreadingService _threadingService; + private readonly IVsUIService _debugger; + private readonly IRemoteDebuggerAuthenticationService _remoteDebuggerAuthenticationService; + private readonly Lazy _hotReloadSessionManager; + private readonly Lazy _debuggerSettings; + private readonly IOutputTypeChecker _outputTypeChecker; + + [ImportingConstructor] + public ProjectLaunchTargetsProvider( + IUnconfiguredProjectVsServices unconfiguredProjectVsServices, + ConfiguredProject project, + IDebugTokenReplacer tokenReplacer, + IFileSystem fileSystem, + IEnvironmentHelper environment, + IActiveDebugFrameworkServices activeDebugFramework, + IOutputTypeChecker outputTypeChecker, + IProjectThreadingService threadingService, + IVsUIService debugger, + IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService, + Lazy hotReloadSessionManager, + Lazy debuggerSettings) + { + _project = project; + _unconfiguredProjectVsServices = unconfiguredProjectVsServices; + _tokenReplacer = tokenReplacer; + _fileSystem = fileSystem; + _environment = environment; + _activeDebugFramework = activeDebugFramework; + _outputTypeChecker = outputTypeChecker; + _threadingService = threadingService; + _debugger = debugger; + _remoteDebuggerAuthenticationService = remoteDebuggerAuthenticationService; + _hotReloadSessionManager = hotReloadSessionManager; + _debuggerSettings = debuggerSettings; + } + + private Task GetConfiguredProjectForDebugAsync() + => _activeDebugFramework.GetConfiguredProjectForActiveFrameworkAsync(); + + /// + /// This provider handles running the Project and empty commandName (this generally just runs the executable) + /// + public bool SupportsProfile(ILaunchProfile profile) + => string.IsNullOrWhiteSpace(profile.CommandName) || IsRunProjectCommand(profile) || IsRunExecutableCommand(profile); + /// - /// Provides QueryDebugTargetsAsync() support for running the project output or any random executable. It is not an exported - /// CPS debugger but hooks into the launch profiles extensibility point. The order of this provider is - /// near the bottom to ensure other providers get chance to handle it first + /// Called just prior to launch to do additional work (put up ui, do special configuration etc). /// - [Export(typeof(IDebugProfileLaunchTargetsProvider))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - [Order(Order.Default)] // The higher the number the higher priority and we want this one last - internal class ProjectLaunchTargetsProvider : - IDebugProfileLaunchTargetsProvider, - IDebugProfileLaunchTargetsProvider2, - IDebugProfileLaunchTargetsProvider3, - IDebugProfileLaunchTargetsProvider4 + public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) { - private static readonly char[] s_escapedChars = new[] { '^', '<', '>', '&' }; - private readonly ConfiguredProject _project; - private readonly IUnconfiguredProjectVsServices _unconfiguredProjectVsServices; - private readonly IDebugTokenReplacer _tokenReplacer; - private readonly IFileSystem _fileSystem; - private readonly IEnvironmentHelper _environment; - private readonly IActiveDebugFrameworkServices _activeDebugFramework; - private readonly IProjectThreadingService _threadingService; - private readonly IVsUIService _debugger; - private readonly IRemoteDebuggerAuthenticationService _remoteDebuggerAuthenticationService; - private readonly Lazy _hotReloadSessionManager; - private readonly Lazy _debuggerSettings; - private readonly IOutputTypeChecker _outputTypeChecker; - - [ImportingConstructor] - public ProjectLaunchTargetsProvider( - IUnconfiguredProjectVsServices unconfiguredProjectVsServices, - ConfiguredProject project, - IDebugTokenReplacer tokenReplacer, - IFileSystem fileSystem, - IEnvironmentHelper environment, - IActiveDebugFrameworkServices activeDebugFramework, - IOutputTypeChecker outputTypeChecker, - IProjectThreadingService threadingService, - IVsUIService debugger, - IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService, - Lazy hotReloadSessionManager, - Lazy debuggerSettings) - { - _project = project; - _unconfiguredProjectVsServices = unconfiguredProjectVsServices; - _tokenReplacer = tokenReplacer; - _fileSystem = fileSystem; - _environment = environment; - _activeDebugFramework = activeDebugFramework; - _outputTypeChecker = outputTypeChecker; - _threadingService = threadingService; - _debugger = debugger; - _remoteDebuggerAuthenticationService = remoteDebuggerAuthenticationService; - _hotReloadSessionManager = hotReloadSessionManager; - _debuggerSettings = debuggerSettings; - } + throw new InvalidOperationException($"Wrong overload of {nameof(OnBeforeLaunchAsync)} called."); + } - private Task GetConfiguredProjectForDebugAsync() - => _activeDebugFramework.GetConfiguredProjectForActiveFrameworkAsync(); + public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList debugLaunchSettings) + => Task.CompletedTask; - /// - /// This provider handles running the Project and empty commandName (this generally just runs the executable) - /// - public bool SupportsProfile(ILaunchProfile profile) - => string.IsNullOrWhiteSpace(profile.CommandName) || IsRunProjectCommand(profile) || IsRunExecutableCommand(profile); + /// + /// Called just after the launch to do additional work (put up ui, do special configuration etc). + /// + public Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) + { + throw new InvalidOperationException($"Wrong overload of {nameof(OnAfterLaunchAsync)} called."); + } - /// - /// Called just prior to launch to do additional work (put up ui, do special configuration etc). - /// - public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) - { - throw new InvalidOperationException($"Wrong overload of {nameof(OnBeforeLaunchAsync)} called."); - } + public async Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList processInfos) + { + await TaskScheduler.Default; - public Task OnBeforeLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList debugLaunchSettings) - => Task.CompletedTask; + bool runningUnderDebugger = (launchOptions & DebugLaunchOptions.NoDebug) != DebugLaunchOptions.NoDebug; - /// - /// Called just after the launch to do additional work (put up ui, do special configuration etc). - /// - public Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) - { - throw new InvalidOperationException($"Wrong overload of {nameof(OnAfterLaunchAsync)} called."); - } + await _hotReloadSessionManager.Value.ActivateSessionAsync((int)processInfos[0].dwProcessId, runningUnderDebugger, Path.GetFileNameWithoutExtension(_project.UnconfiguredProject.FullPath)); + } - public async Task OnAfterLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile, IReadOnlyList processInfos) + public async Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) + { + if (IsRunProjectCommand(profile)) { - await TaskScheduler.Default; + // If the profile uses the "Project" command, check that the project specifies + // something we can run. - bool runningUnderDebugger = (launchOptions & DebugLaunchOptions.NoDebug) != DebugLaunchOptions.NoDebug; + ConfiguredProject? configuredProject = await GetConfiguredProjectForDebugAsync(); + Assumes.NotNull(configuredProject); + Assumes.Present(configuredProject.Services.ProjectPropertiesProvider); - await _hotReloadSessionManager.Value.ActivateSessionAsync((int)processInfos[0].dwProcessId, runningUnderDebugger, Path.GetFileNameWithoutExtension(_project.UnconfiguredProject.FullPath)); - } + IProjectProperties properties = configuredProject.Services.ProjectPropertiesProvider.GetCommonProperties(); - public async Task CanBeStartupProjectAsync(DebugLaunchOptions launchOptions, ILaunchProfile profile) - { - if (IsRunProjectCommand(profile)) + string? runCommand = await ProjectAndExecutableLaunchHandlerHelpers.GetTargetCommandAsync(properties, _environment, _fileSystem, _outputTypeChecker, validateSettings: true); + if (string.IsNullOrWhiteSpace(runCommand)) { - // If the profile uses the "Project" command, check that the project specifies - // something we can run. - - ConfiguredProject? configuredProject = await GetConfiguredProjectForDebugAsync(); - Assumes.NotNull(configuredProject); - Assumes.Present(configuredProject.Services.ProjectPropertiesProvider); + return false; + } + } - IProjectProperties properties = configuredProject.Services.ProjectPropertiesProvider.GetCommonProperties(); + // Otherwise, the profile must be using the "Executable" command in which case it + // can always be a start-up project. + return true; + } - string? runCommand = await ProjectAndExecutableLaunchHandlerHelpers.GetTargetCommandAsync(properties, _environment, _fileSystem, _outputTypeChecker, validateSettings: true); - if (string.IsNullOrWhiteSpace(runCommand)) - { - return false; - } - } + public async Task> QueryDebugTargetsForDebugLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile) + => await QueryDebugTargetsAsync(launchOptions, activeProfile, validateSettings: true) ?? throw new Exception(VSResources.ProjectNotRunnableDirectly); - // Otherwise, the profile must be using the "Executable" command in which case it - // can always be a start-up project. - return true; - } + /// + /// This is called on F5/Ctrl-F5 to return the list of debug targets. What we return depends on the type + /// of project. + /// + public async Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile) + => await QueryDebugTargetsAsync(launchOptions, activeProfile, validateSettings: false) ?? throw new Exception(VSResources.ProjectNotRunnableDirectly); - public async Task> QueryDebugTargetsForDebugLaunchAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile) - => await QueryDebugTargetsAsync(launchOptions, activeProfile, validateSettings: true) ?? throw new Exception(VSResources.ProjectNotRunnableDirectly); + /// + /// Returns if the debug launch settings are . Otherwise, the list of debug launch settings. + /// + private async Task?> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile, bool validateSettings) + { + // Resolve the tokens in the profile + ILaunchProfile resolvedProfile = await _tokenReplacer.ReplaceTokensInProfileAsync(activeProfile); - /// - /// This is called on F5/Ctrl-F5 to return the list of debug targets. What we return depends on the type - /// of project. - /// - public async Task> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile) - => await QueryDebugTargetsAsync(launchOptions, activeProfile, validateSettings: false) ?? throw new Exception(VSResources.ProjectNotRunnableDirectly); + DebugLaunchSettings? consoleTarget = await GetConsoleTargetForProfileAsync(resolvedProfile, launchOptions, validateSettings); + return consoleTarget is null ? null : new[] { consoleTarget }; + } - /// - /// Returns if the debug launch settings are . Otherwise, the list of debug launch settings. - /// - private async Task?> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions, ILaunchProfile activeProfile, bool validateSettings) + /// + /// Does some basic validation of the settings. If we don't, the error messages are terrible. + /// + public void ValidateSettings([NotNull] string? executable, string workingDir, string? profileName) + { + if (Strings.IsNullOrEmpty(executable)) { - // Resolve the tokens in the profile - ILaunchProfile resolvedProfile = await _tokenReplacer.ReplaceTokensInProfileAsync(activeProfile); - - DebugLaunchSettings? consoleTarget = await GetConsoleTargetForProfileAsync(resolvedProfile, launchOptions, validateSettings); - return consoleTarget is null ? null : new[] { consoleTarget }; + throw new ProjectLaunchSettingValidationException(string.Format(VSResources.NoDebugExecutableSpecified, profileName)); } - /// - /// Does some basic validation of the settings. If we don't, the error messages are terrible. - /// - public void ValidateSettings([NotNull] string? executable, string workingDir, string? profileName) + if (executable.IndexOf(Path.DirectorySeparatorChar) != -1 && !_fileSystem.FileExists(executable)) { - if (Strings.IsNullOrEmpty(executable)) - { - throw new ProjectLaunchSettingValidationException(string.Format(VSResources.NoDebugExecutableSpecified, profileName)); - } - - if (executable.IndexOf(Path.DirectorySeparatorChar) != -1 && !_fileSystem.FileExists(executable)) - { - throw new ProjectLaunchSettingValidationException(string.Format(VSResources.DebugExecutableNotFound, executable, profileName)); - } + throw new ProjectLaunchSettingValidationException(string.Format(VSResources.DebugExecutableNotFound, executable, profileName)); + } - if (!string.IsNullOrEmpty(workingDir) && !_fileSystem.DirectoryExists(workingDir)) - { - throw new ProjectLaunchSettingValidationException(string.Format(VSResources.WorkingDirecotryInvalid, workingDir, profileName)); - } + if (!string.IsNullOrEmpty(workingDir) && !_fileSystem.DirectoryExists(workingDir)) + { + throw new ProjectLaunchSettingValidationException(string.Format(VSResources.WorkingDirecotryInvalid, workingDir, profileName)); } + } - private sealed class ProjectLaunchSettingValidationException : Exception + private sealed class ProjectLaunchSettingValidationException : Exception + { + public ProjectLaunchSettingValidationException(string message) + : base(message) { - public ProjectLaunchSettingValidationException(string message) - : base(message) - { - } } + } - /// - /// Helper returns cmd.exe as the launcher for Ctrl-F5 (useCmdShell == true), otherwise just the exe and args passed in. - /// - public static void GetExeAndArguments(bool useCmdShell, string? debugExe, string? debugArgs, out string? finalExePath, out string? finalArguments) + /// + /// Helper returns cmd.exe as the launcher for Ctrl-F5 (useCmdShell == true), otherwise just the exe and args passed in. + /// + public static void GetExeAndArguments(bool useCmdShell, string? debugExe, string? debugArgs, out string? finalExePath, out string? finalArguments) + { + if (useCmdShell) { - if (useCmdShell) - { - // Escape the characters ^<>& so that they are passed to the application rather than interpreted by cmd.exe. - string? escapedArgs = EscapeString(debugArgs, s_escapedChars); - finalArguments = $"/c \"\"{debugExe}\" {escapedArgs} & pause\""; - finalExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); - } - else - { - finalArguments = debugArgs; - finalExePath = debugExe; - } + // Escape the characters ^<>& so that they are passed to the application rather than interpreted by cmd.exe. + string? escapedArgs = EscapeString(debugArgs, s_escapedChars); + finalArguments = $"/c \"\"{debugExe}\" {escapedArgs} & pause\""; + finalExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); } + else + { + finalArguments = debugArgs; + finalExePath = debugExe; + } + } - private static bool IsRunExecutableCommand(ILaunchProfile profile) - => string.Equals(profile.CommandName, LaunchSettingsProvider.RunExecutableCommandName, StringComparisons.LaunchProfileCommandNames); + private static bool IsRunExecutableCommand(ILaunchProfile profile) + => string.Equals(profile.CommandName, LaunchSettingsProvider.RunExecutableCommandName, StringComparisons.LaunchProfileCommandNames); - private static bool IsRunProjectCommand(ILaunchProfile profile) - => string.Equals(profile.CommandName, LaunchSettingsProvider.RunProjectCommandName, StringComparisons.LaunchProfileCommandNames); + private static bool IsRunProjectCommand(ILaunchProfile profile) + => string.Equals(profile.CommandName, LaunchSettingsProvider.RunProjectCommandName, StringComparisons.LaunchProfileCommandNames); - private async Task IsIntegratedConsoleEnabledAsync() - { - if (!_project.Capabilities.Contains(ProjectCapabilities.IntegratedConsoleDebugging)) - return false; + private async Task IsIntegratedConsoleEnabledAsync() + { + if (!_project.Capabilities.Contains(ProjectCapabilities.IntegratedConsoleDebugging)) + return false; - await _threadingService.SwitchToUIThread(); + await _threadingService.SwitchToUIThread(); - _debugger.Value.IsIntegratedConsoleEnabled(out bool enabled); + _debugger.Value.IsIntegratedConsoleEnabled(out bool enabled); - return enabled; - } + return enabled; + } - /// - /// This is called on F5 to return the list of debug targets. What we return depends on the type - /// of project. - /// - /// if the runnable project information is . Otherwise, the debug launch settings. - internal async Task GetConsoleTargetForProfileAsync(ILaunchProfile resolvedProfile, DebugLaunchOptions launchOptions, bool validateSettings) - { - var settings = new DebugLaunchSettings(launchOptions); + /// + /// This is called on F5 to return the list of debug targets. What we return depends on the type + /// of project. + /// + /// if the runnable project information is . Otherwise, the debug launch settings. + internal async Task GetConsoleTargetForProfileAsync(ILaunchProfile resolvedProfile, DebugLaunchOptions launchOptions, bool validateSettings) + { + var settings = new DebugLaunchSettings(launchOptions); - string? executable, arguments; + string? executable, arguments; - ConfiguredProject? configuredProject = await GetConfiguredProjectForDebugAsync(); + ConfiguredProject? configuredProject = await GetConfiguredProjectForDebugAsync(); - Assumes.NotNull(configuredProject); + Assumes.NotNull(configuredProject); - // If no working directory specified in the profile, we default to output directory. If for some reason the output directory - // is not specified, fall back to the project folder. - string projectFolderFullPath = _project.UnconfiguredProject.GetProjectDirectory(); - string defaultWorkingDir = await ProjectAndExecutableLaunchHandlerHelpers.GetDefaultWorkingDirectoryAsync(configuredProject, projectFolderFullPath, _fileSystem); + // If no working directory specified in the profile, we default to output directory. If for some reason the output directory + // is not specified, fall back to the project folder. + string projectFolderFullPath = _project.UnconfiguredProject.GetProjectDirectory(); + string defaultWorkingDir = await ProjectAndExecutableLaunchHandlerHelpers.GetDefaultWorkingDirectoryAsync(configuredProject, projectFolderFullPath, _fileSystem); - string? commandLineArgs = resolvedProfile.CommandLineArgs is null - ? null - : Regex.Replace(resolvedProfile.CommandLineArgs, "[\r\n]+", " "); + string? commandLineArgs = resolvedProfile.CommandLineArgs is null + ? null + : Regex.Replace(resolvedProfile.CommandLineArgs, "[\r\n]+", " "); - // Is this profile just running the project? If so we ignore the exe - if (IsRunProjectCommand(resolvedProfile)) + // Is this profile just running the project? If so we ignore the exe + if (IsRunProjectCommand(resolvedProfile)) + { + // Get the executable to run, the arguments and the default working directory + (string, string, string)? runnableProjectInfo = await ProjectAndExecutableLaunchHandlerHelpers.GetRunnableProjectInformationAsync( + configuredProject, + _environment, + _fileSystem, + _outputTypeChecker, + validateSettings); + + if (runnableProjectInfo == null) { - // Get the executable to run, the arguments and the default working directory - (string, string, string)? runnableProjectInfo = await ProjectAndExecutableLaunchHandlerHelpers.GetRunnableProjectInformationAsync( - configuredProject, - _environment, - _fileSystem, - _outputTypeChecker, - validateSettings); - - if (runnableProjectInfo == null) - { - return null; - } - string workingDirectory; - (executable, arguments, workingDirectory) = runnableProjectInfo.Value; - - if (!string.IsNullOrWhiteSpace(workingDirectory)) - { - defaultWorkingDir = workingDirectory; - } - - if (!string.IsNullOrWhiteSpace(commandLineArgs)) - { - arguments = arguments + " " + commandLineArgs; - } + return null; } - else + string workingDirectory; + (executable, arguments, workingDirectory) = runnableProjectInfo.Value; + + if (!string.IsNullOrWhiteSpace(workingDirectory)) { - executable = resolvedProfile.ExecutablePath; - arguments = commandLineArgs; + defaultWorkingDir = workingDirectory; } - string workingDir; - if (Strings.IsNullOrWhiteSpace(resolvedProfile.WorkingDirectory)) + if (!string.IsNullOrWhiteSpace(commandLineArgs)) { - workingDir = defaultWorkingDir; + arguments = arguments + " " + commandLineArgs; } - else + } + else + { + executable = resolvedProfile.ExecutablePath; + arguments = commandLineArgs; + } + + string workingDir; + if (Strings.IsNullOrWhiteSpace(resolvedProfile.WorkingDirectory)) + { + workingDir = defaultWorkingDir; + } + else + { + // If the working directory is not rooted we assume it is relative to the project directory + workingDir = _fileSystem.GetFullPath(Path.Combine(projectFolderFullPath, resolvedProfile.WorkingDirectory.Replace("/", "\\"))); + } + + // IF the executable is not rooted, we want to make is relative to the workingDir unless is doesn't contain + // any path elements. In that case we are going to assume it is in the current directory of the VS process, or on + // the environment path. If we can't find it, we just launch it as before. + if (!Strings.IsNullOrWhiteSpace(executable)) + { + executable = executable.Replace("/", "\\"); + if (Path.GetPathRoot(executable) == "\\") { - // If the working directory is not rooted we assume it is relative to the project directory - workingDir = _fileSystem.GetFullPath(Path.Combine(projectFolderFullPath, resolvedProfile.WorkingDirectory.Replace("/", "\\"))); + // Root of current drive + executable = _fileSystem.GetFullPath(executable); } - - // IF the executable is not rooted, we want to make is relative to the workingDir unless is doesn't contain - // any path elements. In that case we are going to assume it is in the current directory of the VS process, or on - // the environment path. If we can't find it, we just launch it as before. - if (!Strings.IsNullOrWhiteSpace(executable)) + else if (!Path.IsPathRooted(executable)) { - executable = executable.Replace("/", "\\"); - if (Path.GetPathRoot(executable) == "\\") + if (executable.Contains("\\")) { - // Root of current drive - executable = _fileSystem.GetFullPath(executable); + // Combine with the working directory used by the profile + executable = _fileSystem.GetFullPath(Path.Combine(workingDir, executable)); } - else if (!Path.IsPathRooted(executable)) + else { - if (executable.Contains("\\")) + // Try to resolve against the current working directory (for compat) and failing that, the environment path. + string exeName = executable.EndsWith(".exe", StringComparisons.Paths) ? executable : executable + ".exe"; + string fullPath = _fileSystem.GetFullPath(exeName); + if (_fileSystem.FileExists(fullPath)) { - // Combine with the working directory used by the profile - executable = _fileSystem.GetFullPath(Path.Combine(workingDir, executable)); + executable = fullPath; } else { - // Try to resolve against the current working directory (for compat) and failing that, the environment path. - string exeName = executable.EndsWith(".exe", StringComparisons.Paths) ? executable : executable + ".exe"; - string fullPath = _fileSystem.GetFullPath(exeName); - if (_fileSystem.FileExists(fullPath)) - { - executable = fullPath; - } - else + string? fullPathFromEnv = ProjectAndExecutableLaunchHandlerHelpers.GetFullPathOfExeFromEnvironmentPath(exeName, _environment, _fileSystem); + if (fullPathFromEnv is not null) { - string? fullPathFromEnv = ProjectAndExecutableLaunchHandlerHelpers.GetFullPathOfExeFromEnvironmentPath(exeName, _environment, _fileSystem); - if (fullPathFromEnv is not null) - { - executable = fullPathFromEnv; - } + executable = fullPathFromEnv; } } } } + } - if (validateSettings) - { - ValidateSettings(executable, workingDir, resolvedProfile.Name); - } + if (validateSettings) + { + ValidateSettings(executable, workingDir, resolvedProfile.Name); + } - // Apply environment variables. - foreach ((string key, string value) in resolvedProfile.EnumerateEnvironmentVariables()) - { - settings.Environment[key] = value; - } + // Apply environment variables. + foreach ((string key, string value) in resolvedProfile.EnumerateEnvironmentVariables()) + { + settings.Environment[key] = value; + } - settings.LaunchOperation = DebugLaunchOperation.CreateProcess; - settings.LaunchDebugEngineGuid = await GetDebuggingEngineAsync(configuredProject); + settings.LaunchOperation = DebugLaunchOperation.CreateProcess; + settings.LaunchDebugEngineGuid = await GetDebuggingEngineAsync(configuredProject); - if (resolvedProfile.IsNativeDebuggingEnabled()) - { - settings.AdditionalDebugEngines.Add(DebuggerEngines.NativeOnlyEngine); - } + if (resolvedProfile.IsNativeDebuggingEnabled()) + { + settings.AdditionalDebugEngines.Add(DebuggerEngines.NativeOnlyEngine); + } + + if (resolvedProfile.IsSqlDebuggingEnabled()) + { + settings.AdditionalDebugEngines.Add(DebuggerEngines.SqlEngine); + } - if (resolvedProfile.IsSqlDebuggingEnabled()) + bool useCmdShell = false; + if (await _outputTypeChecker.IsConsoleAsync()) + { + if (await IsIntegratedConsoleEnabledAsync()) { - settings.AdditionalDebugEngines.Add(DebuggerEngines.SqlEngine); + settings.LaunchOptions |= DebugLaunchOptions.IntegratedConsole; } - bool useCmdShell = false; - if (await _outputTypeChecker.IsConsoleAsync()) - { - if (await IsIntegratedConsoleEnabledAsync()) - { - settings.LaunchOptions |= DebugLaunchOptions.IntegratedConsole; - } + useCmdShell = UseCmdShellForConsoleLaunch(resolvedProfile, settings.LaunchOptions); + } - useCmdShell = UseCmdShellForConsoleLaunch(resolvedProfile, settings.LaunchOptions); - } + GetExeAndArguments(useCmdShell, executable, arguments, out string? finalExecutable, out string? finalArguments); - GetExeAndArguments(useCmdShell, executable, arguments, out string? finalExecutable, out string? finalArguments); + settings.Executable = finalExecutable; + settings.Arguments = finalArguments; + settings.CurrentDirectory = workingDir; + settings.Project = _unconfiguredProjectVsServices.VsHierarchy; - settings.Executable = finalExecutable; - settings.Arguments = finalArguments; - settings.CurrentDirectory = workingDir; - settings.Project = _unconfiguredProjectVsServices.VsHierarchy; + if (resolvedProfile.IsRemoteDebugEnabled()) + { + settings.RemoteMachine = resolvedProfile.RemoteDebugMachine(); - if (resolvedProfile.IsRemoteDebugEnabled()) + string? remoteAuthenticationMode = resolvedProfile.RemoteAuthenticationMode(); + if (!Strings.IsNullOrEmpty(remoteAuthenticationMode)) { - settings.RemoteMachine = resolvedProfile.RemoteDebugMachine(); - - string? remoteAuthenticationMode = resolvedProfile.RemoteAuthenticationMode(); - if (!Strings.IsNullOrEmpty(remoteAuthenticationMode)) + IRemoteAuthenticationProvider? remoteAuthenticationProvider = _remoteDebuggerAuthenticationService.FindProviderForAuthenticationMode(remoteAuthenticationMode); + if (remoteAuthenticationProvider is not null) { - IRemoteAuthenticationProvider? remoteAuthenticationProvider = _remoteDebuggerAuthenticationService.FindProviderForAuthenticationMode(remoteAuthenticationMode); - if (remoteAuthenticationProvider is not null) - { - settings.PortSupplierGuid = remoteAuthenticationProvider.PortSupplierGuid; - } + settings.PortSupplierGuid = remoteAuthenticationProvider.PortSupplierGuid; } } + } - // WebView2 debugging is only supported for Project and Executable commands - if (resolvedProfile.IsJSWebView2DebuggingEnabled() && (IsRunExecutableCommand(resolvedProfile) || IsRunProjectCommand(resolvedProfile))) - { - // If JS Debugger is selected, we would need to change the launch debugger to that one - settings.LaunchDebugEngineGuid = DebuggerEngines.JavaScriptForWebView2Engine; - - // Create the launch params needed for the JS debugger - var debuggerLaunchOptions = new JObject( - new JProperty("type", "pwa-msedge"), - new JProperty("runtimeExecutable", finalExecutable), - new JProperty("webRoot", workingDir), // We use the Working Directory debugging option as the WebRoot, to map the urls to files on disk - new JProperty("useWebView", true), - new JProperty("runtimeArgs", finalArguments) - ); - - settings.Options = JsonConvert.SerializeObject(debuggerLaunchOptions); - } - - if (await HotReloadShouldBeEnabledAsync(resolvedProfile, launchOptions) - && await _hotReloadSessionManager.Value.TryCreatePendingSessionAsync(settings.Environment)) - { - // Enable XAML Hot Reload - settings.Environment["ENABLE_XAML_DIAGNOSTICS_SOURCE_INFO"] = "1"; - } + // WebView2 debugging is only supported for Project and Executable commands + if (resolvedProfile.IsJSWebView2DebuggingEnabled() && (IsRunExecutableCommand(resolvedProfile) || IsRunProjectCommand(resolvedProfile))) + { + // If JS Debugger is selected, we would need to change the launch debugger to that one + settings.LaunchDebugEngineGuid = DebuggerEngines.JavaScriptForWebView2Engine; + + // Create the launch params needed for the JS debugger + var debuggerLaunchOptions = new JObject( + new JProperty("type", "pwa-msedge"), + new JProperty("runtimeExecutable", finalExecutable), + new JProperty("webRoot", workingDir), // We use the Working Directory debugging option as the WebRoot, to map the urls to files on disk + new JProperty("useWebView", true), + new JProperty("runtimeArgs", finalArguments) + ); + + settings.Options = JsonConvert.SerializeObject(debuggerLaunchOptions); + } - if (settings.Environment.Count > 0) - { - settings.LaunchOptions |= DebugLaunchOptions.MergeEnvironment; - } + if (await HotReloadShouldBeEnabledAsync(resolvedProfile, launchOptions) + && await _hotReloadSessionManager.Value.TryCreatePendingSessionAsync(settings.Environment)) + { + // Enable XAML Hot Reload + settings.Environment["ENABLE_XAML_DIAGNOSTICS_SOURCE_INFO"] = "1"; + } - return settings; + if (settings.Environment.Count > 0) + { + settings.LaunchOptions |= DebugLaunchOptions.MergeEnvironment; } - private async Task HotReloadShouldBeEnabledAsync(ILaunchProfile resolvedProfile, DebugLaunchOptions launchOptions) + return settings; + } + + private async Task HotReloadShouldBeEnabledAsync(ILaunchProfile resolvedProfile, DebugLaunchOptions launchOptions) + { + bool hotReloadEnabledAtProjectLevel = IsRunProjectCommand(resolvedProfile) + && resolvedProfile.IsHotReloadEnabled() + && !resolvedProfile.IsRemoteDebugEnabled() + && (launchOptions & DebugLaunchOptions.Profiling) != DebugLaunchOptions.Profiling; + + if (hotReloadEnabledAtProjectLevel) { - bool hotReloadEnabledAtProjectLevel = IsRunProjectCommand(resolvedProfile) - && resolvedProfile.IsHotReloadEnabled() - && !resolvedProfile.IsRemoteDebugEnabled() - && (launchOptions & DebugLaunchOptions.Profiling) != DebugLaunchOptions.Profiling; + bool debugging = (launchOptions & DebugLaunchOptions.NoDebug) != DebugLaunchOptions.NoDebug; + bool hotReloadEnabledGlobally = await _debuggerSettings.Value.IsHotReloadEnabledAsync(debugging, default); - if (hotReloadEnabledAtProjectLevel) - { - bool debugging = (launchOptions & DebugLaunchOptions.NoDebug) != DebugLaunchOptions.NoDebug; - bool hotReloadEnabledGlobally = await _debuggerSettings.Value.IsHotReloadEnabledAsync(debugging, default); + return hotReloadEnabledGlobally; + } - return hotReloadEnabledGlobally; - } + return false; + } + private static bool UseCmdShellForConsoleLaunch(ILaunchProfile profile, DebugLaunchOptions options) + { + if (!IsRunProjectCommand(profile)) return false; - } - private static bool UseCmdShellForConsoleLaunch(ILaunchProfile profile, DebugLaunchOptions options) - { - if (!IsRunProjectCommand(profile)) - return false; + if ((options & DebugLaunchOptions.IntegratedConsole) == DebugLaunchOptions.IntegratedConsole) + return false; - if ((options & DebugLaunchOptions.IntegratedConsole) == DebugLaunchOptions.IntegratedConsole) - return false; + if ((options & DebugLaunchOptions.Profiling) == DebugLaunchOptions.Profiling) + return false; - if ((options & DebugLaunchOptions.Profiling) == DebugLaunchOptions.Profiling) - return false; + return (options & DebugLaunchOptions.NoDebug) == DebugLaunchOptions.NoDebug; + } - return (options & DebugLaunchOptions.NoDebug) == DebugLaunchOptions.NoDebug; - } + private static async Task GetDebuggingEngineAsync(ConfiguredProject configuredProject) + { + Assumes.Present(configuredProject.Services.ProjectPropertiesProvider); - private static async Task GetDebuggingEngineAsync(ConfiguredProject configuredProject) - { - Assumes.Present(configuredProject.Services.ProjectPropertiesProvider); + IProjectProperties properties = configuredProject.Services.ProjectPropertiesProvider.GetCommonProperties(); + string framework = await properties.GetEvaluatedPropertyValueAsync(ConfigurationGeneral.TargetFrameworkIdentifierProperty); - IProjectProperties properties = configuredProject.Services.ProjectPropertiesProvider.GetCommonProperties(); - string framework = await properties.GetEvaluatedPropertyValueAsync(ConfigurationGeneral.TargetFrameworkIdentifierProperty); + return GetManagedDebugEngineForFramework(framework); + } - return GetManagedDebugEngineForFramework(framework); + /// + /// Escapes the given characters in a given string, ignoring escape sequences when inside a quoted string. + /// + /// The string to escape. + /// The characters to escape in the string. + /// The escaped string. + [return: NotNullIfNotNull(nameof(unescaped))] + internal static string? EscapeString(string? unescaped, char[] toEscape) + { + if (Strings.IsNullOrWhiteSpace(unescaped)) + { + return unescaped; } - /// - /// Escapes the given characters in a given string, ignoring escape sequences when inside a quoted string. - /// - /// The string to escape. - /// The characters to escape in the string. - /// The escaped string. - [return: NotNullIfNotNull(nameof(unescaped))] - internal static string? EscapeString(string? unescaped, char[] toEscape) + bool ShouldEscape(char c) { - if (Strings.IsNullOrWhiteSpace(unescaped)) + foreach (char escapeChar in toEscape) { - return unescaped; - } - - bool ShouldEscape(char c) - { - foreach (char escapeChar in toEscape) - { - if (escapeChar == c) - return true; - } - return false; + if (escapeChar == c) + return true; } + return false; + } - StringState currentState = StringState.NormalCharacter; - var finalBuilder = PooledStringBuilder.GetInstance(); - foreach (char currentChar in unescaped) + StringState currentState = StringState.NormalCharacter; + var finalBuilder = PooledStringBuilder.GetInstance(); + foreach (char currentChar in unescaped) + { + switch (currentState) { - switch (currentState) - { - case StringState.NormalCharacter: - // If we're currently not in a quoted string, then we need to escape anything in toEscape. - // The valid transitions are to EscapedCharacter (for a '\', such as '\"'), and QuotedString. - if (currentChar == '\\') - { - currentState = StringState.EscapedCharacter; - } - else if (currentChar == '"') - { - currentState = StringState.QuotedString; - } - else if (ShouldEscape(currentChar)) - { - finalBuilder.Append('^'); - } + case StringState.NormalCharacter: + // If we're currently not in a quoted string, then we need to escape anything in toEscape. + // The valid transitions are to EscapedCharacter (for a '\', such as '\"'), and QuotedString. + if (currentChar == '\\') + { + currentState = StringState.EscapedCharacter; + } + else if (currentChar == '"') + { + currentState = StringState.QuotedString; + } + else if (ShouldEscape(currentChar)) + { + finalBuilder.Append('^'); + } - break; - case StringState.EscapedCharacter: - // If a '\' was the previous character, then we blindly append to the string, escaping if necessary, - // and move back to NormalCharacter. This handles '\"' - if (ShouldEscape(currentChar)) - { - finalBuilder.Append('^'); - } + break; + case StringState.EscapedCharacter: + // If a '\' was the previous character, then we blindly append to the string, escaping if necessary, + // and move back to NormalCharacter. This handles '\"' + if (ShouldEscape(currentChar)) + { + finalBuilder.Append('^'); + } + currentState = StringState.NormalCharacter; + break; + case StringState.QuotedString: + // If we're in a string, we don't escape any characters. If the current character is a '\', + // then we move to QuotedStringEscapedCharacter. This handles '\"'. If the current character + // is a '"', then we're out of the string. Otherwise, we stay in the string. + if (currentChar == '\\') + { + currentState = StringState.QuotedStringEscapedCharacter; + } + else if (currentChar == '"') + { currentState = StringState.NormalCharacter; - break; - case StringState.QuotedString: - // If we're in a string, we don't escape any characters. If the current character is a '\', - // then we move to QuotedStringEscapedCharacter. This handles '\"'. If the current character - // is a '"', then we're out of the string. Otherwise, we stay in the string. - if (currentChar == '\\') - { - currentState = StringState.QuotedStringEscapedCharacter; - } - else if (currentChar == '"') - { - currentState = StringState.NormalCharacter; - } - - break; - case StringState.QuotedStringEscapedCharacter: - // If we have one slash, then we blindly append to the string, no escaping, and move back to - // QuotedString. This handles escaped '"' inside strings. - currentState = StringState.QuotedString; - break; - default: - // We can't get here. - throw new InvalidOperationException(); - } + } - finalBuilder.Append(currentChar); + break; + case StringState.QuotedStringEscapedCharacter: + // If we have one slash, then we blindly append to the string, no escaping, and move back to + // QuotedString. This handles escaped '"' inside strings. + currentState = StringState.QuotedString; + break; + default: + // We can't get here. + throw new InvalidOperationException(); } - return finalBuilder.ToStringAndFree(); + finalBuilder.Append(currentChar); } - /// - /// Helper returns the correct debugger engine based on the targeted framework - /// - internal static Guid GetManagedDebugEngineForFramework(string targetFramework) - // The engine depends on the framework - => IsDotNetCoreFramework(targetFramework) - ? DebuggerEngines.ManagedCoreEngine - : DebuggerEngines.ManagedOnlyEngine; - - /// - /// TODO: This is a placeholder until issue https://github.com/dotnet/project-system/issues/423 is addressed. - /// This information should come from the targets file. - /// - private static bool IsDotNetCoreFramework(string targetFramework) - { - const string NetStandardPrefix = ".NetStandard"; - const string NetCorePrefix = ".NetCore"; - return targetFramework.StartsWith(NetCorePrefix, StringComparisons.FrameworkIdentifiers) || - targetFramework.StartsWith(NetStandardPrefix, StringComparisons.FrameworkIdentifiers); - } + return finalBuilder.ToStringAndFree(); + } - private enum StringState - { - NormalCharacter, EscapedCharacter, QuotedString, QuotedStringEscapedCharacter - } + /// + /// Helper returns the correct debugger engine based on the targeted framework + /// + internal static Guid GetManagedDebugEngineForFramework(string targetFramework) + // The engine depends on the framework + => IsDotNetCoreFramework(targetFramework) + ? DebuggerEngines.ManagedCoreEngine + : DebuggerEngines.ManagedOnlyEngine; + + /// + /// TODO: This is a placeholder until issue https://github.com/dotnet/project-system/issues/423 is addressed. + /// This information should come from the targets file. + /// + private static bool IsDotNetCoreFramework(string targetFramework) + { + const string NetStandardPrefix = ".NetStandard"; + const string NetCorePrefix = ".NetCore"; + return targetFramework.StartsWith(NetCorePrefix, StringComparisons.FrameworkIdentifiers) || + targetFramework.StartsWith(NetStandardPrefix, StringComparisons.FrameworkIdentifiers); + } + + private enum StringState + { + NormalCharacter, EscapedCharacter, QuotedString, QuotedStringEscapedCharacter } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/RemoteDebuggerAuthenticationService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/RemoteDebuggerAuthenticationService.cs index b06b0802f7..3e736065c8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/RemoteDebuggerAuthenticationService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/RemoteDebuggerAuthenticationService.cs @@ -4,55 +4,54 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +[Export(typeof(IRemoteDebuggerAuthenticationService))] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +internal class RemoteDebuggerAuthenticationService : IRemoteDebuggerAuthenticationService { - [Export(typeof(IRemoteDebuggerAuthenticationService))] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - internal class RemoteDebuggerAuthenticationService : IRemoteDebuggerAuthenticationService + private readonly IVsUIService _remoteDiscoveryUIService; + private readonly IProjectThreadingService _threadingService; + + [ImportingConstructor] + public RemoteDebuggerAuthenticationService( + UnconfiguredProject unconfiguredProject, + IVsUIService remoteDiscoveryUIService, + IProjectThreadingService threadingService) { - private readonly IVsUIService _remoteDiscoveryUIService; - private readonly IProjectThreadingService _threadingService; - - [ImportingConstructor] - public RemoteDebuggerAuthenticationService( - UnconfiguredProject unconfiguredProject, - IVsUIService remoteDiscoveryUIService, - IProjectThreadingService threadingService) - { - _remoteDiscoveryUIService = remoteDiscoveryUIService; - _threadingService = threadingService; - AuthenticationProviders = new OrderPrecedenceImportCollection(orderingStyle: ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesLast, projectCapabilityCheckProvider: unconfiguredProject); - } - - [ImportMany] - private OrderPrecedenceImportCollection AuthenticationProviders { get; } + _remoteDiscoveryUIService = remoteDiscoveryUIService; + _threadingService = threadingService; + AuthenticationProviders = new OrderPrecedenceImportCollection(orderingStyle: ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesLast, projectCapabilityCheckProvider: unconfiguredProject); + } - public IRemoteAuthenticationProvider? FindProviderForAuthenticationMode(string remoteAuthenticationMode) => AuthenticationProviders.FirstOrDefaultValue(p => p.Name.Equals(remoteAuthenticationMode, StringComparisons.LaunchProfileProperties)); + [ImportMany] + private OrderPrecedenceImportCollection AuthenticationProviders { get; } - public IEnumerable GetRemoteAuthenticationModes() => AuthenticationProviders.ExtensionValues(); + public IRemoteAuthenticationProvider? FindProviderForAuthenticationMode(string remoteAuthenticationMode) => AuthenticationProviders.FirstOrDefaultValue(p => p.Name.Equals(remoteAuthenticationMode, StringComparisons.LaunchProfileProperties)); - public bool ShowRemoteDiscoveryDialog(ref string remoteDebugMachine, ref IRemoteAuthenticationProvider? remoteAuthenticationProvider) - { - _threadingService.VerifyOnUIThread(); + public IEnumerable GetRemoteAuthenticationModes() => AuthenticationProviders.ExtensionValues(); - Guid currentPortSupplier = remoteAuthenticationProvider?.AuthenticationModeGuid ?? Guid.Empty; + public bool ShowRemoteDiscoveryDialog(ref string remoteDebugMachine, ref IRemoteAuthenticationProvider? remoteAuthenticationProvider) + { + _threadingService.VerifyOnUIThread(); - uint extraFlags = (uint)DEBUG_REMOTE_DISCOVERY_FLAGS.DRD_NONE; + Guid currentPortSupplier = remoteAuthenticationProvider?.AuthenticationModeGuid ?? Guid.Empty; - foreach (IRemoteAuthenticationProvider provider in AuthenticationProviders.ExtensionValues()) - { - extraFlags |= provider.AdditionalRemoteDiscoveryDialogFlags; - } + uint extraFlags = (uint)DEBUG_REMOTE_DISCOVERY_FLAGS.DRD_NONE; - if (ErrorHandler.Succeeded(_remoteDiscoveryUIService.Value.SelectRemoteInstanceViaDlg(remoteDebugMachine, currentPortSupplier, extraFlags, out string remoteMachine, out Guid portSupplier))) - { - remoteDebugMachine = remoteMachine; - remoteAuthenticationProvider = AuthenticationProviders.FirstOrDefaultValue(p => p.AuthenticationModeGuid.Equals(portSupplier)); + foreach (IRemoteAuthenticationProvider provider in AuthenticationProviders.ExtensionValues()) + { + extraFlags |= provider.AdditionalRemoteDiscoveryDialogFlags; + } - return true; - } + if (ErrorHandler.Succeeded(_remoteDiscoveryUIService.Value.SelectRemoteInstanceViaDlg(remoteDebugMachine, currentPortSupplier, extraFlags, out string remoteMachine, out Guid portSupplier))) + { + remoteDebugMachine = remoteMachine; + remoteAuthenticationProvider = AuthenticationProviders.FirstOrDefaultValue(p => p.AuthenticationModeGuid.Equals(portSupplier)); - return false; + return true; } + + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectHelper.cs index c9ab6bce70..f2efc5036c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectHelper.cs @@ -7,86 +7,85 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Handles populating a menu command on the debug dropdown when the menu reflects the IEnumValues for +/// a debug property. It shows the active framework used for running the app (F5/Ctrl+F5). +/// +[Export(typeof(IStartupProjectHelper))] +internal class StartupProjectHelper : IStartupProjectHelper { + private readonly IVsUIService _dte; + private readonly IVsUIService _solution; + private readonly IProjectExportProvider _projectExportProvider; + + [ImportingConstructor] + public StartupProjectHelper(IVsUIService dte, + IVsUIService solution, + IProjectExportProvider projectExportProvider) + { + _dte = dte; + _solution = solution; + _projectExportProvider = projectExportProvider; + } + /// - /// Handles populating a menu command on the debug dropdown when the menu reflects the IEnumValues for - /// a debug property. It shows the active framework used for running the app (F5/Ctrl+F5). + /// Returns the export T of the startup projects if those projects support the specified capabilities /// - [Export(typeof(IStartupProjectHelper))] - internal class StartupProjectHelper : IStartupProjectHelper + public ImmutableArray GetExportFromDotNetStartupProjects(string capabilityMatch) where T : class { - private readonly IVsUIService _dte; - private readonly IVsUIService _solution; - private readonly IProjectExportProvider _projectExportProvider; - - [ImportingConstructor] - public StartupProjectHelper(IVsUIService dte, - IVsUIService solution, - IProjectExportProvider projectExportProvider) + if (_dte.Value.Solution.SolutionBuild.StartupProjects is object[] { Length: > 0 } startupProjects) { - _dte = dte; - _solution = solution; - _projectExportProvider = projectExportProvider; - } + var results = PooledArray.GetInstance(); - /// - /// Returns the export T of the startup projects if those projects support the specified capabilities - /// - public ImmutableArray GetExportFromDotNetStartupProjects(string capabilityMatch) where T : class - { - if (_dte.Value.Solution.SolutionBuild.StartupProjects is object[] { Length: > 0 } startupProjects) + foreach (string projectName in startupProjects.Cast()) { - var results = PooledArray.GetInstance(); + _solution.Value.GetProjectOfUniqueName(projectName, out IVsHierarchy hier); - foreach (string projectName in startupProjects.Cast()) + if (hier?.IsCapabilityMatch(capabilityMatch) == true) { - _solution.Value.GetProjectOfUniqueName(projectName, out IVsHierarchy hier); + string? projectPath = hier.GetProjectFilePath(); - if (hier?.IsCapabilityMatch(capabilityMatch) == true) + if (projectPath is not null) { - string? projectPath = hier.GetProjectFilePath(); + T? export = _projectExportProvider.GetExport(projectPath); - if (projectPath is not null) + if (export is not null) { - T? export = _projectExportProvider.GetExport(projectPath); - - if (export is not null) - { - results.Add(export); - } + results.Add(export); } } } - - return results.ToImmutableAndFree(); } - return ImmutableArray.Empty; + return results.ToImmutableAndFree(); } - public ImmutableArray GetFullPathsOfStartupProjects() + return ImmutableArray.Empty; + } + + public ImmutableArray GetFullPathsOfStartupProjects() + { + if (_dte.Value.Solution.SolutionBuild.StartupProjects is object[] { Length: > 0 } startupProjects) { - if (_dte.Value.Solution.SolutionBuild.StartupProjects is object[] { Length: > 0 } startupProjects) - { - var results = PooledArray.GetInstance(startupProjects.Length); + var results = PooledArray.GetInstance(startupProjects.Length); - foreach (string projectName in startupProjects.Cast()) - { - _solution.Value.GetProjectOfUniqueName(projectName, out IVsHierarchy hier); + foreach (string projectName in startupProjects.Cast()) + { + _solution.Value.GetProjectOfUniqueName(projectName, out IVsHierarchy hier); - string? projectPath = hier?.GetProjectFilePath(); + string? projectPath = hier?.GetProjectFilePath(); - if (projectPath is not null) - { - results.Add(projectPath); - } + if (projectPath is not null) + { + results.Add(projectPath); } - - return results.ToImmutableAndFree(); } - return ImmutableArray.Empty; + return results.ToImmutableAndFree(); } + + return ImmutableArray.Empty; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs index b8319e7760..1567028c32 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/StartupProjectRegistrar.cs @@ -3,134 +3,133 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +/// +/// Responsible for adding or removing the project from the startup list based on whether the project +/// is debuggable or not. +/// +internal class StartupProjectRegistrar : OnceInitializedOnceDisposedAsync { - /// - /// Responsible for adding or removing the project from the startup list based on whether the project - /// is debuggable or not. - /// - internal class StartupProjectRegistrar : OnceInitializedOnceDisposedAsync + private readonly UnconfiguredProject _project; + private readonly IUnconfiguredProjectTasksService _projectTasksService; + private readonly IVsService _startupProjectsListService; + private readonly IProjectThreadingService _threadingService; + private readonly ISafeProjectGuidService _projectGuidService; + private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; + private readonly IActiveConfiguredValues _launchProviders; + + private Guid _projectGuid; + private IDisposable? _subscription; + + [ImportingConstructor] + public StartupProjectRegistrar( + UnconfiguredProject project, + IUnconfiguredProjectTasksService projectTasksService, + IVsService startupProjectsListService, + IProjectThreadingService threadingService, + ISafeProjectGuidService projectGuidService, + IActiveConfiguredProjectSubscriptionService projectSubscriptionService, + IActiveConfiguredValues launchProviders) + : base(threadingService.JoinableTaskContext) { - private readonly UnconfiguredProject _project; - private readonly IUnconfiguredProjectTasksService _projectTasksService; - private readonly IVsService _startupProjectsListService; - private readonly IProjectThreadingService _threadingService; - private readonly ISafeProjectGuidService _projectGuidService; - private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; - private readonly IActiveConfiguredValues _launchProviders; - - private Guid _projectGuid; - private IDisposable? _subscription; - - [ImportingConstructor] - public StartupProjectRegistrar( - UnconfiguredProject project, - IUnconfiguredProjectTasksService projectTasksService, - IVsService startupProjectsListService, - IProjectThreadingService threadingService, - ISafeProjectGuidService projectGuidService, - IActiveConfiguredProjectSubscriptionService projectSubscriptionService, - IActiveConfiguredValues launchProviders) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _projectTasksService = projectTasksService; - _startupProjectsListService = startupProjectsListService; - _threadingService = threadingService; - _projectGuidService = projectGuidService; - _projectSubscriptionService = projectSubscriptionService; - _launchProviders = launchProviders; - } + _project = project; + _projectTasksService = projectTasksService; + _startupProjectsListService = startupProjectsListService; + _threadingService = threadingService; + _projectGuidService = projectGuidService; + _projectSubscriptionService = projectSubscriptionService; + _launchProviders = launchProviders; + } - [ProjectAutoLoad(startAfter: ProjectLoadCheckpoint.ProjectFactoryCompleted)] - [AppliesTo(ProjectCapability.DotNet)] - public Task InitializeAsync() + [ProjectAutoLoad(startAfter: ProjectLoadCheckpoint.ProjectFactoryCompleted)] + [AppliesTo(ProjectCapability.DotNet)] + public Task InitializeAsync() + { + _threadingService.RunAndForget(async () => { - _threadingService.RunAndForget(async () => - { - await _projectTasksService.SolutionLoadedInHost; + await _projectTasksService.SolutionLoadedInHost; - await InitializeAsync(CancellationToken.None); - }, _project); + await InitializeAsync(CancellationToken.None); + }, _project); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _projectGuid = await _projectGuidService.GetProjectGuidAsync(cancellationToken); + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _projectGuid = await _projectGuidService.GetProjectGuidAsync(cancellationToken); - Assumes.False(_projectGuid == Guid.Empty); + Assumes.False(_projectGuid == Guid.Empty); - _subscription = _projectSubscriptionService.ProjectRuleSource.SourceBlock.LinkToAsyncAction( - target: OnProjectChangedAsync, - _project); - } + _subscription = _projectSubscriptionService.ProjectRuleSource.SourceBlock.LinkToAsyncAction( + target: OnProjectChangedAsync, + _project); + } - protected override Task DisposeCoreAsync(bool initialized) + protected override Task DisposeCoreAsync(bool initialized) + { + if (initialized) { - if (initialized) - { - _subscription?.Dispose(); - } - - return Task.CompletedTask; + _subscription?.Dispose(); } - internal Task OnProjectChangedAsync(IProjectVersionedValue? _ = null) + return Task.CompletedTask; + } + + internal Task OnProjectChangedAsync(IProjectVersionedValue? _ = null) + { + return _projectTasksService.LoadedProjectAsync(async () => { - return _projectTasksService.LoadedProjectAsync(async () => - { - bool isDebuggable = await IsDebuggableAsync(); + bool isDebuggable = await IsDebuggableAsync(); - IVsStartupProjectsListService? startupProjectsListService = await _startupProjectsListService.GetValueOrNullAsync(); + IVsStartupProjectsListService? startupProjectsListService = await _startupProjectsListService.GetValueOrNullAsync(); - if (startupProjectsListService is null) - { - return; - } + if (startupProjectsListService is null) + { + return; + } - if (isDebuggable) - { - // If we're already registered, the service no-ops - startupProjectsListService.AddProject(ref _projectGuid); - } - else - { - // If we're already unregistered, the service no-ops - startupProjectsListService.RemoveProject(ref _projectGuid); - } - }); - } + if (isDebuggable) + { + // If we're already registered, the service no-ops + startupProjectsListService.AddProject(ref _projectGuid); + } + else + { + // If we're already unregistered, the service no-ops + startupProjectsListService.RemoveProject(ref _projectGuid); + } + }); + } - private async Task IsDebuggableAsync() - { - var foundStartupProjectProvider = false; + private async Task IsDebuggableAsync() + { + var foundStartupProjectProvider = false; - foreach (Lazy provider in _launchProviders.Values) + foreach (Lazy provider in _launchProviders.Values) + { + if (provider.Value is IStartupProjectProvider startupProjectProvider) { - if (provider.Value is IStartupProjectProvider startupProjectProvider) + foundStartupProjectProvider = true; + if (await startupProjectProvider.CanBeStartupProjectAsync(DebugLaunchOptions.DesignTimeExpressionEvaluation)) { - foundStartupProjectProvider = true; - if (await startupProjectProvider.CanBeStartupProjectAsync(DebugLaunchOptions.DesignTimeExpressionEvaluation)) - { - return true; - } + return true; } } + } - if (!foundStartupProjectProvider) + if (!foundStartupProjectProvider) + { + foreach (Lazy provider in _launchProviders.Values) { - foreach (Lazy provider in _launchProviders.Values) + if (await provider.Value.CanLaunchAsync(DebugLaunchOptions.DesignTimeExpressionEvaluation)) { - if (await provider.Value.CanLaunchAsync(DebugLaunchOptions.DesignTimeExpressionEvaluation)) - { - return true; - } + return true; } } - - return false; } + + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs index ae933d937f..86fde73197 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Debug/VBDefineConstantsEncoding.cs @@ -2,152 +2,151 @@ using Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages.Designer; -namespace Microsoft.VisualStudio.ProjectSystem.Debug -{ +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + - [Export(typeof(INameValuePairListEncoding))] - [ExportMetadata("Encoding", "VBDefineConstantsEncoding")] - internal sealed class VBDefineConstantsEncoding : INameValuePairListEncoding +[Export(typeof(INameValuePairListEncoding))] +[ExportMetadata("Encoding", "VBDefineConstantsEncoding")] +internal sealed class VBDefineConstantsEncoding : INameValuePairListEncoding +{ + public IEnumerable<(string Name, string Value)> Parse(string value) { - public IEnumerable<(string Name, string Value)> Parse(string value) + Requires.NotNull(value); + if (string.IsNullOrEmpty(value)) { - Requires.NotNull(value); - if (string.IsNullOrEmpty(value)) - { - yield break; - } + yield break; + } - foreach (string entry in ReadEntries(value)) + foreach (string entry in ReadEntries(value)) + { + if (!TryParseSplitPairedEntry(entry, out (string, string) resultingEntry) + && !TryParseSingleEntry(entry, out resultingEntry)) { - if (!TryParseSplitPairedEntry(entry, out (string, string) resultingEntry) - && !TryParseSingleEntry(entry, out resultingEntry)) - { - throw new FormatException("Expected valid name value pair for defining custom constants."); - } + throw new FormatException("Expected valid name value pair for defining custom constants."); + } - yield return resultingEntry; + yield return resultingEntry; - } + } - static IEnumerable ReadEntries(string rawText) + static IEnumerable ReadEntries(string rawText) + { + bool escaped = false; + int entryStart = 0; + for (int i = 0; i < rawText.Length; i++) { - bool escaped = false; - int entryStart = 0; - for (int i = 0; i < rawText.Length; i++) + if (rawText[i] == ',' && !escaped) { - if (rawText[i] == ',' && !escaped) - { - yield return rawText.Substring(entryStart, i - entryStart); - entryStart = i + 1; - escaped = false; - } - else if (rawText[i] == '/') - { - escaped = !escaped; - } - else - { - escaped = false; - } + yield return rawText.Substring(entryStart, i - entryStart); + entryStart = i + 1; + escaped = false; } - - yield return rawText.Substring(entryStart); - } - - static bool TryParseSplitPairedEntry(string entry, out (string, string) result) - { - bool escaped = false; - for (int i = 0; i < entry.Length; i++) + else if (rawText[i] == '/') { - if (entry[i] == '=' && !escaped) - { - string name = entry.Substring(0, i); - string value = entry.Substring(i + 1); - if (name.Length == 0 || value.Length == 0) - { - break; - } - result = (DecodeCharacters(name), DecodeCharacters(value)); - - return true; - } - else if (entry[i] == '/') - { - escaped = !escaped; - } - else - { - escaped = false; - } + escaped = !escaped; + } + else + { + escaped = false; } - - result = default; - return false; } - static bool TryParseSingleEntry(string entry, out (string, string) result) + yield return rawText.Substring(entryStart); + } + + static bool TryParseSplitPairedEntry(string entry, out (string, string) result) + { + bool escaped = false; + for (int i = 0; i < entry.Length; i++) { - bool escaped = false; - for (int i = 0; i < entry.Length; i++) + if (entry[i] == '=' && !escaped) { - if (entry[i] == '=' && !escaped) + string name = entry.Substring(0, i); + string value = entry.Substring(i + 1); + if (name.Length == 0 || value.Length == 0) { break; } - else if (i == entry.Length - 1) - { - result = (DecodeCharacters(entry), ""); - return true; - } - escaped = entry[i] == '/' && !escaped; - } + result = (DecodeCharacters(name), DecodeCharacters(value)); - result = default; - return false; + return true; + } + else if (entry[i] == '/') + { + escaped = !escaped; + } + else + { + escaped = false; + } } - static string DecodeCharacters(string value) - { - return value.Replace("/\"", "\"").Replace("/=", "=").Replace("/,", ",").Replace("//", "/"); - } + result = default; + return false; } - public string Format(IEnumerable<(string Name, string Value)> pairs) + static bool TryParseSingleEntry(string entry, out (string, string) result) { - return string.Join(",", pairs.Select(EncodePair)); - - static string EncodePair((string Name, string Value) pair) + bool escaped = false; + for (int i = 0; i < entry.Length; i++) { - if (string.IsNullOrEmpty(pair.Value)) + if (entry[i] == '=' && !escaped) { - return EncodeCharacters(pair.Name); + break; } - else + else if (i == entry.Length - 1) { - return $"{EncodeCharacters(pair.Name)}={EncodeCharacters(pair.Value)}"; + result = (DecodeCharacters(entry), ""); + return true; } + escaped = entry[i] == '/' && !escaped; } - static string EncodeCharacters(string value) + result = default; + return false; + } + + static string DecodeCharacters(string value) + { + return value.Replace("/\"", "\"").Replace("/=", "=").Replace("/,", ",").Replace("//", "/"); + } + } + + public string Format(IEnumerable<(string Name, string Value)> pairs) + { + return string.Join(",", pairs.Select(EncodePair)); + + static string EncodePair((string Name, string Value) pair) + { + if (string.IsNullOrEmpty(pair.Value)) + { + return EncodeCharacters(pair.Name); + } + else + { + return $"{EncodeCharacters(pair.Name)}={EncodeCharacters(pair.Value)}"; + } + } + + static string EncodeCharacters(string value) + { + int i = value.Length - 1; + while (i > -1) { - int i = value.Length - 1; - while (i > -1) + if (value[i] == '/' || value[i] == ',' || value[i] == '=' || value[i] == '"') { - if (value[i] == '/' || value[i] == ',' || value[i] == '=' || value[i] == '"') + if (i == 0 || value[i - 1] != '/') + { + value = value.Insert(i, "/"); + } + else if (value[i - 1] == '/') { - if (i == 0 || value[i - 1] != '/') - { - value = value.Insert(i, "/"); - } - else if (value[i - 1] == '/') - { - i--; - } + i--; } - i--; } - return value; + i--; } + return value; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/DteEnvironmentOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/DteEnvironmentOptions.cs index d583e525b8..9c54e0fb06 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/DteEnvironmentOptions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/DteEnvironmentOptions.cs @@ -4,42 +4,41 @@ using EnvDTE80; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides an implementation of that calls into . +/// +[Export(typeof(IEnvironmentOptions))] +internal class DteEnvironmentOptions : IEnvironmentOptions { - /// - /// Provides an implementation of that calls into . - /// - [Export(typeof(IEnvironmentOptions))] - internal class DteEnvironmentOptions : IEnvironmentOptions + private readonly IVsUIService _dte; + + [ImportingConstructor] + public DteEnvironmentOptions(IVsUIService dte) { - private readonly IVsUIService _dte; + _dte = dte; + } - [ImportingConstructor] - public DteEnvironmentOptions(IVsUIService dte) - { - _dte = dte; - } + public T GetOption(string category, string page, string option, T defaultValue) + { + EnvDTE.Properties? properties = _dte.Value.get_Properties(category, page); - public T GetOption(string category, string page, string option, T defaultValue) + if (properties is not null) { - EnvDTE.Properties? properties = _dte.Value.get_Properties(category, page); + return (T)properties.Item(option).Value; + } - if (properties is not null) - { - return (T)properties.Item(option).Value; - } + return defaultValue; + } - return defaultValue; - } + public void SetOption(string category, string page, string option, T newValue) + { + EnvDTE.Properties? properties = _dte.Value.get_Properties(category, page); - public void SetOption(string category, string page, string option, T newValue) + if (properties is not null) { - EnvDTE.Properties? properties = _dte.Value.get_Properties(category, page); - - if (properties is not null) - { - properties.Item(option).Value = newValue; - } + properties.Item(option).Value = newValue; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/IRunningDocumentTable.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/IRunningDocumentTable.cs index 38717eb858..4c49b94ce5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/IRunningDocumentTable.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/IRunningDocumentTable.cs @@ -2,20 +2,19 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Editor +namespace Microsoft.VisualStudio.ProjectSystem.VS.Editor; + +/// +/// Allows subscribing to running document table (RDT) events via the +/// family of event handler interfaces. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.System)] +internal interface IRunningDocumentTable { /// - /// Allows subscribing to running document table (RDT) events via the - /// family of event handler interfaces. + /// Creates a new subscription for RDT events that will call back via . /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.System)] - internal interface IRunningDocumentTable - { - /// - /// Creates a new subscription for RDT events that will call back via . - /// - /// The callback for events. Note that it may also implement additional version(s) of this interface. - /// An object that unsubscribes when disposed. - Task SubscribeEventsAsync(IVsRunningDocTableEvents eventListener); - } + /// The callback for events. Note that it may also implement additional version(s) of this interface. + /// An object that unsubscribes when disposed. + Task SubscribeEventsAsync(IVsRunningDocTableEvents eventListener); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/RunningDocumentTable.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/RunningDocumentTable.cs index 164d1e27cd..d587d63e21 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/RunningDocumentTable.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Editor/RunningDocumentTable.cs @@ -4,54 +4,53 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Editor +namespace Microsoft.VisualStudio.ProjectSystem.VS.Editor; + +[Export(typeof(IRunningDocumentTable))] +internal sealed class RunningDocumentTable : OnceInitializedOnceDisposedAsync, IRunningDocumentTable { - [Export(typeof(IRunningDocumentTable))] - internal sealed class RunningDocumentTable : OnceInitializedOnceDisposedAsync, IRunningDocumentTable + private readonly IVsService _rdtService; + + private IVsRunningDocumentTable? _rdt; + + [ImportingConstructor] + public RunningDocumentTable( + IVsService rdtService, + JoinableTaskContext joinableTaskContext) + : base(new(joinableTaskContext)) { - private readonly IVsService _rdtService; + _rdtService = rdtService; + } - private IVsRunningDocumentTable? _rdt; + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); - [ImportingConstructor] - public RunningDocumentTable( - IVsService rdtService, - JoinableTaskContext joinableTaskContext) - : base(new(joinableTaskContext)) - { - _rdtService = rdtService; - } + _rdt = await _rdtService.GetValueAsync(cancellationToken); + } - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); + protected override Task DisposeCoreAsync(bool initialized) + { + return Task.CompletedTask; + } - _rdt = await _rdtService.GetValueAsync(cancellationToken); - } + public async Task SubscribeEventsAsync(IVsRunningDocTableEvents eventListener) + { + await InitializeAsync(); - protected override Task DisposeCoreAsync(bool initialized) - { - return Task.CompletedTask; - } + Assumes.NotNull(_rdt); - public async Task SubscribeEventsAsync(IVsRunningDocTableEvents eventListener) - { - await InitializeAsync(); + HResult.Verify( + _rdt.AdviseRunningDocTableEvents(eventListener, out uint cookie), + $"Error advising RDT events in {typeof(RunningDocumentTable)}."); - Assumes.NotNull(_rdt); + return new AsyncDisposable(async () => + { + await JoinableFactory.SwitchToMainThreadAsync(); HResult.Verify( - _rdt.AdviseRunningDocTableEvents(eventListener, out uint cookie), - $"Error advising RDT events in {typeof(RunningDocumentTable)}."); - - return new AsyncDisposable(async () => - { - await JoinableFactory.SwitchToMainThreadAsync(); - - HResult.Verify( - _rdt.UnadviseRunningDocTableEvents(cookie), - $"Error unadvising RDT events in {typeof(RunningDocumentTable)}."); - }); - } + _rdt.UnadviseRunningDocTableEvents(cookie), + $"Error unadvising RDT events in {typeof(RunningDocumentTable)}."); + }); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ExportProjectNodeComServiceAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ExportProjectNodeComServiceAttribute.cs index 3ebc44af00..0589661345 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ExportProjectNodeComServiceAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ExportProjectNodeComServiceAttribute.cs @@ -1,35 +1,34 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Exports a service to be aggregated with the project node. +/// +[MetadataAttribute] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface, AllowMultiple = true)] +internal class ExportProjectNodeComServiceAttribute : ExportAttribute { - /// - /// Exports a service to be aggregated with the project node. - /// - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface, AllowMultiple = true)] - internal class ExportProjectNodeComServiceAttribute : ExportAttribute + public ExportProjectNodeComServiceAttribute(params Type[] comTypes) + : base(ExportContractNames.VsTypes.ProjectNodeComExtension) { - public ExportProjectNodeComServiceAttribute(params Type[] comTypes) - : base(ExportContractNames.VsTypes.ProjectNodeComExtension) - { - Iid = GetIids(comTypes); - } + Iid = GetIids(comTypes); + } - public string[] Iid { get; } + public string[] Iid { get; } - public static bool IncludeInherited - { - get => false; - } + public static bool IncludeInherited + { + get => false; + } - private static string[] GetIids(Type[] comTypes) - { - Requires.NotNull(comTypes); + private static string[] GetIids(Type[] comTypes) + { + Requires.NotNull(comTypes); - // Reuse ComServiceIdAttribute's logic for calculating IIDs. - return comTypes.Select(type => new ComServiceIidAttribute(type)) - .SelectMany(attribute => attribute.Iid) - .ToArray(); - } + // Reuse ComServiceIdAttribute's logic for calculating IIDs. + return comTypes.Select(type => new ComServiceIidAttribute(type)) + .SelectMany(attribute => attribute.Iid) + .ToArray(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectSelector.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectSelector.cs index 4d83701b80..c18ac3ce5a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectSelector.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectSelector.cs @@ -9,70 +9,69 @@ using Microsoft.VisualStudio.Threading; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; -namespace Microsoft.VisualStudio.ProjectSystem.VS.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.FSharp; + +[Export(typeof(IPackageService))] +[Guid("E720DAD0-1854-47FC-93AF-E719B54B84E6")] +[ProvideObject(typeof(FSharpProjectSelector), RegisterUsing = RegistrationMethod.Assembly)] +internal sealed class FSharpProjectSelector : IVsProjectSelector, IPackageService, IDisposable { - [Export(typeof(IPackageService))] - [Guid("E720DAD0-1854-47FC-93AF-E719B54B84E6")] - [ProvideObject(typeof(FSharpProjectSelector), RegisterUsing = RegistrationMethod.Assembly)] - internal sealed class FSharpProjectSelector : IVsProjectSelector, IPackageService, IDisposable + private const string MSBuildXmlNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; + + private readonly JoinableTaskContext _context; + private IVsRegisterProjectSelector? _projectSelector; + private uint _cookie = VSConstants.VSCOOKIE_NIL; + + [ImportingConstructor] + public FSharpProjectSelector(JoinableTaskContext context) { - private const string MSBuildXmlNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; + _context = context; + } - private readonly JoinableTaskContext _context; - private IVsRegisterProjectSelector? _projectSelector; - private uint _cookie = VSConstants.VSCOOKIE_NIL; + public async Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) + { + Assumes.Null(_projectSelector); - [ImportingConstructor] - public FSharpProjectSelector(JoinableTaskContext context) - { - _context = context; - } + _context.VerifyIsOnMainThread(); - public async Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) - { - Assumes.Null(_projectSelector); + _projectSelector = await asyncServiceProvider.GetServiceAsync(); - _context.VerifyIsOnMainThread(); + Guid selectorGuid = GetType().GUID; + _projectSelector.RegisterProjectSelector(ref selectorGuid, this, out _cookie); + } - _projectSelector = await asyncServiceProvider.GetServiceAsync(); + public void GetProjectFactoryGuid(Guid guidProjectType, string pszFilename, out Guid guidProjectFactory) + { + var doc = XDocument.Load(pszFilename); + GetProjectFactoryGuid(doc, out guidProjectFactory); + } - Guid selectorGuid = GetType().GUID; - _projectSelector.RegisterProjectSelector(ref selectorGuid, this, out _cookie); - } + internal static void GetProjectFactoryGuid(XDocument doc, out Guid guidProjectFactory) + { + var nsm = new XmlNamespaceManager(new NameTable()); + nsm.AddNamespace("msb", MSBuildXmlNamespace); - public void GetProjectFactoryGuid(Guid guidProjectType, string pszFilename, out Guid guidProjectFactory) - { - var doc = XDocument.Load(pszFilename); - GetProjectFactoryGuid(doc, out guidProjectFactory); - } + // If the project has either a Project-level SDK attribute or an Import-level SDK attribute, we'll open it with the new project system. + // Check both namespace-qualified and unqualified forms to include projects with and without the xmlns attribute. + bool hasProjectElementWithSdkAttribute = doc.XPathSelectElement("/msb:Project[@Sdk]", nsm) is not null || doc.XPathSelectElement("/Project[@Sdk]") is not null; + bool hasImportElementWithSdkAttribute = doc.XPathSelectElement("/*/msb:Import[@Sdk]", nsm) is not null || doc.XPathSelectElement("/*/Import[@Sdk]") is not null; - internal static void GetProjectFactoryGuid(XDocument doc, out Guid guidProjectFactory) + if (hasProjectElementWithSdkAttribute || hasImportElementWithSdkAttribute) { - var nsm = new XmlNamespaceManager(new NameTable()); - nsm.AddNamespace("msb", MSBuildXmlNamespace); - - // If the project has either a Project-level SDK attribute or an Import-level SDK attribute, we'll open it with the new project system. - // Check both namespace-qualified and unqualified forms to include projects with and without the xmlns attribute. - bool hasProjectElementWithSdkAttribute = doc.XPathSelectElement("/msb:Project[@Sdk]", nsm) is not null || doc.XPathSelectElement("/Project[@Sdk]") is not null; - bool hasImportElementWithSdkAttribute = doc.XPathSelectElement("/*/msb:Import[@Sdk]", nsm) is not null || doc.XPathSelectElement("/*/Import[@Sdk]") is not null; + guidProjectFactory = ProjectType.FSharpGuid; + return; + } - if (hasProjectElementWithSdkAttribute || hasImportElementWithSdkAttribute) - { - guidProjectFactory = ProjectType.FSharpGuid; - return; - } + guidProjectFactory = ProjectType.LegacyFSharpGuid; + } - guidProjectFactory = ProjectType.LegacyFSharpGuid; - } + public void Dispose() + { + _context.VerifyIsOnMainThread(); - public void Dispose() + if (_cookie != VSConstants.VSCOOKIE_NIL) { - _context.VerifyIsOnMainThread(); - - if (_cookie != VSConstants.VSCOOKIE_NIL) - { - _projectSelector?.UnregisterProjectSelector(_cookie); - } + _projectSelector?.UnregisterProjectSelector(_cookie); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectTypeGuidProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectTypeGuidProvider.cs index faccaa1c5b..faa11b987a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectTypeGuidProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FSharp/FSharpProjectTypeGuidProvider.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.FSharp; + +/// +/// Provides the Visual Basic implementation of . +/// +[Export(typeof(IItemTypeGuidProvider))] +[AppliesTo(ProjectCapability.FSharp)] +internal class FSharpProjectTypeGuidProvider : IItemTypeGuidProvider { - /// - /// Provides the Visual Basic implementation of . - /// - [Export(typeof(IItemTypeGuidProvider))] - [AppliesTo(ProjectCapability.FSharp)] - internal class FSharpProjectTypeGuidProvider : IItemTypeGuidProvider + [ImportingConstructor] + public FSharpProjectTypeGuidProvider() { - [ImportingConstructor] - public FSharpProjectTypeGuidProvider() - { - } + } - public Guid ProjectTypeGuid - { - get { return ProjectType.LegacyFSharpGuid; } - } + public Guid ProjectTypeGuid + { + get { return ProjectType.LegacyFSharpGuid; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformIdentifierEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformIdentifierEnumProvider.cs index 082715ef84..3bfaa21e42 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformIdentifierEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformIdentifierEnumProvider.cs @@ -3,36 +3,35 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks +namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks; + +/// +/// Responsible for producing valid values for the SdkSupportedTargetPlatformIdentifier property from evaluation. +/// +[ExportDynamicEnumValuesProvider("SdkSupportedTargetPlatformIdentifierEnumProvider")] +[AppliesTo(ProjectCapability.DotNet)] +internal class SdkSupportedTargetPlatformIdentifierEnumProvider : SingleRuleSupportedValuesProvider { - /// - /// Responsible for producing valid values for the SdkSupportedTargetPlatformIdentifier property from evaluation. - /// - [ExportDynamicEnumValuesProvider("SdkSupportedTargetPlatformIdentifierEnumProvider")] - [AppliesTo(ProjectCapability.DotNet)] - internal class SdkSupportedTargetPlatformIdentifierEnumProvider : SingleRuleSupportedValuesProvider - { - [ImportingConstructor] - public SdkSupportedTargetPlatformIdentifierEnumProvider( - ConfiguredProject project, - IProjectSubscriptionService subscriptionService) - : base(project, subscriptionService, ruleName: SdkSupportedTargetPlatformIdentifier.SchemaName, useNoneValue: true) { } + [ImportingConstructor] + public SdkSupportedTargetPlatformIdentifierEnumProvider( + ConfiguredProject project, + IProjectSubscriptionService subscriptionService) + : base(project, subscriptionService, ruleName: SdkSupportedTargetPlatformIdentifier.SchemaName, useNoneValue: true) { } - protected override IEnumValue ToEnumValue(KeyValuePair> item) + protected override IEnumValue ToEnumValue(KeyValuePair> item) + { + return new PageEnumValue(new EnumValue() { - return new PageEnumValue(new EnumValue() - { - // Example: - // + // Example: + // - Name = item.Key, - DisplayName = item.Value[SdkSupportedTargetPlatformIdentifier.DisplayNameProperty] - }); - } + Name = item.Key, + DisplayName = item.Value[SdkSupportedTargetPlatformIdentifier.DisplayNameProperty] + }); + } - protected override int SortValues(IEnumValue a, IEnumValue b) - { - return StringComparer.OrdinalIgnoreCase.Compare(a.DisplayName, b.DisplayName); - } + protected override int SortValues(IEnumValue a, IEnumValue b) + { + return StringComparer.OrdinalIgnoreCase.Compare(a.DisplayName, b.DisplayName); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs index 28e93622ea..264de3b5bf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SdkSupportedTargetPlatformVersionEnumProvider.cs @@ -3,35 +3,34 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks +namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks; + +/// +/// Responsible for producing valid values for the SdkSupportedTargetPlatformVersion property from evaluation. +/// +[ExportDynamicEnumValuesProvider("SdkSupportedTargetPlatformVersionEnumProvider")] +[AppliesTo(ProjectCapability.DotNet)] +internal class SdkSupportedTargetPlatformVersionEnumProvider : SingleRuleSupportedValuesProvider { - /// - /// Responsible for producing valid values for the SdkSupportedTargetPlatformVersion property from evaluation. - /// - [ExportDynamicEnumValuesProvider("SdkSupportedTargetPlatformVersionEnumProvider")] - [AppliesTo(ProjectCapability.DotNet)] - internal class SdkSupportedTargetPlatformVersionEnumProvider : SingleRuleSupportedValuesProvider - { - [ImportingConstructor] - public SdkSupportedTargetPlatformVersionEnumProvider( - ConfiguredProject project, - IProjectSubscriptionService subscriptionService) - : base(project, subscriptionService, ruleName: SdkSupportedTargetPlatformVersion.SchemaName) { } + [ImportingConstructor] + public SdkSupportedTargetPlatformVersionEnumProvider( + ConfiguredProject project, + IProjectSubscriptionService subscriptionService) + : base(project, subscriptionService, ruleName: SdkSupportedTargetPlatformVersion.SchemaName) { } - protected override IEnumValue ToEnumValue(KeyValuePair> item) + protected override IEnumValue ToEnumValue(KeyValuePair> item) + { + return new PageEnumValue(new EnumValue() { - return new PageEnumValue(new EnumValue() - { - // Example: - // + // Example: + // - Name = item.Key - }); - } + Name = item.Key + }); + } - protected override int SortValues(IEnumValue a, IEnumValue b) - { - return NaturalStringComparer.Instance.Compare(a.Name, b.Name); - } + protected override int SortValues(IEnumValue a, IEnumValue b) + { + return NaturalStringComparer.Instance.Compare(a.Name, b.Name); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SupportedTargetFrameworkAliasEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SupportedTargetFrameworkAliasEnumProvider.cs index 2274544975..fb67c3d8f2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SupportedTargetFrameworkAliasEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Frameworks/SupportedTargetFrameworkAliasEnumProvider.cs @@ -7,116 +7,115 @@ using EnumCollection = System.Collections.Generic.ICollection; using EnumCollectionProjectValue = Microsoft.VisualStudio.ProjectSystem.IProjectVersionedValue>; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks +namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks; + +/// +/// Responsible for producing valid values for the TargetFramework property from a design-time build. +/// +[ExportDynamicEnumValuesProvider("SupportedTargetFrameworkAliasEnumProvider")] +[AppliesTo(ProjectCapability.DotNet)] +[ExportInitialBuildRulesSubscriptions(SupportedTargetFrameworkAlias.SchemaName)] +internal class SupportedTargetFrameworkAliasEnumProvider : ChainedProjectValueDataSourceBase, IDynamicEnumValuesProvider, IDynamicEnumValuesGenerator { - /// - /// Responsible for producing valid values for the TargetFramework property from a design-time build. - /// - [ExportDynamicEnumValuesProvider("SupportedTargetFrameworkAliasEnumProvider")] - [AppliesTo(ProjectCapability.DotNet)] - [ExportInitialBuildRulesSubscriptions(SupportedTargetFrameworkAlias.SchemaName)] - internal class SupportedTargetFrameworkAliasEnumProvider : ChainedProjectValueDataSourceBase, IDynamicEnumValuesProvider, IDynamicEnumValuesGenerator - { - private readonly IProjectSubscriptionService _subscriptionService; + private readonly IProjectSubscriptionService _subscriptionService; - [ImportingConstructor] - public SupportedTargetFrameworkAliasEnumProvider( - ConfiguredProject project, - IProjectSubscriptionService subscriptionService) - : base(project, synchronousDisposal: false, registerDataSource: false) - { - _subscriptionService = subscriptionService; - - ReadyToBuild = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + [ImportingConstructor] + public SupportedTargetFrameworkAliasEnumProvider( + ConfiguredProject project, + IProjectSubscriptionService subscriptionService) + : base(project, synchronousDisposal: false, registerDataSource: false) + { + _subscriptionService = subscriptionService; - [ImportMany] - public OrderPrecedenceImportCollection ReadyToBuild - { - get; - } + ReadyToBuild = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ConfiguredProjectAutoLoad] - [AppliesTo(ProjectCapability.DotNet)] - public void Load() - { - // To avoid UI delays when opening the AppDesigner for the first time, - // we auto-load so that we are included in the first design-time build - // for the project. - EnsureInitialized(); - } + [ImportMany] + public OrderPrecedenceImportCollection ReadyToBuild + { + get; + } - protected override IDisposable? LinkExternalInput(ITargetBlock targetBlock) - { - IProjectValueDataSource source = _subscriptionService.ProjectBuildRuleSource; + [ConfiguredProjectAutoLoad] + [AppliesTo(ProjectCapability.DotNet)] + public void Load() + { + // To avoid UI delays when opening the AppDesigner for the first time, + // we auto-load so that we are included in the first design-time build + // for the project. + EnsureInitialized(); + } - // Transform the changes from design-time build -> Supported target frameworks - DisposableValue> transformBlock = source.SourceBlock.TransformWithNoDelta( - update => update.Derive(Transform), - suppressVersionOnlyUpdates: false, - ruleNames: SupportedTargetFrameworkAlias.SchemaName); + protected override IDisposable? LinkExternalInput(ITargetBlock targetBlock) + { + IProjectValueDataSource source = _subscriptionService.ProjectBuildRuleSource; - // Set the link up so that we publish changes to target block - transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + // Transform the changes from design-time build -> Supported target frameworks + DisposableValue> transformBlock = source.SourceBlock.TransformWithNoDelta( + update => update.Derive(Transform), + suppressVersionOnlyUpdates: false, + ruleNames: SupportedTargetFrameworkAlias.SchemaName); - // Join the source blocks, so if they need to switch to UI thread to complete - // and someone is blocked on us on the same thread, the call proceeds - JoinUpstreamDataSources(source); + // Set the link up so that we publish changes to target block + transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - return transformBlock; - } + // Join the source blocks, so if they need to switch to UI thread to complete + // and someone is blocked on us on the same thread, the call proceeds + JoinUpstreamDataSources(source); - private static EnumCollection Transform(IProjectSubscriptionUpdate input) - { - IProjectRuleSnapshot snapshot = input.CurrentState[SupportedTargetFrameworkAlias.SchemaName]; + return transformBlock; + } - return snapshot.Items.Select(ToEnumValue) - .ToList(); - } + private static EnumCollection Transform(IProjectSubscriptionUpdate input) + { + IProjectRuleSnapshot snapshot = input.CurrentState[SupportedTargetFrameworkAlias.SchemaName]; - private static IEnumValue ToEnumValue(KeyValuePair> item) - { - return new PageEnumValue(new EnumValue() - { - // Example: - - Name = item.Key, - DisplayName = item.Value[SupportedTargetFrameworkAlias.DisplayNameProperty] - }); - } + return snapshot.Items.Select(ToEnumValue) + .ToList(); + } - public Task GetProviderAsync(IList? options) + private static IEnumValue ToEnumValue(KeyValuePair> item) + { + return new PageEnumValue(new EnumValue() { - return Task.FromResult(this); - } + // Example: - public async Task GetListedValuesAsync() - { - if (!IsReadyToBuild()) - throw new InvalidOperationException("This configuration is not set to build"); + Name = item.Key, + DisplayName = item.Value[SupportedTargetFrameworkAlias.DisplayNameProperty] + }); + } - // NOTE: This has a race, if called off the UI thread, the configuration could become - // inactive underneath us and hence not ready for build, causing below to block forever. + public Task GetProviderAsync(IList? options) + { + return Task.FromResult(this); + } - using (JoinableCollection.Join()) - { - EnumCollectionProjectValue snapshot = await SourceBlock.ReceiveAsync(); + public async Task GetListedValuesAsync() + { + if (!IsReadyToBuild()) + throw new InvalidOperationException("This configuration is not set to build"); - // TODO: This is a hotfix for item ordering. Remove this OrderBy when completing: https://github.com/dotnet/project-system/issues/7025 - return snapshot.Value.OrderBy(e => e.DisplayName).ToArray(); - } - } + // NOTE: This has a race, if called off the UI thread, the configuration could become + // inactive underneath us and hence not ready for build, causing below to block forever. - private bool IsReadyToBuild() + using (JoinableCollection.Join()) { - IConfiguredProjectReadyToBuild? readyToBuild = ReadyToBuild.FirstOrDefault()?.Value; + EnumCollectionProjectValue snapshot = await SourceBlock.ReceiveAsync(); - return readyToBuild?.IsValidToBuild == true; + // TODO: This is a hotfix for item ordering. Remove this OrderBy when completing: https://github.com/dotnet/project-system/issues/7025 + return snapshot.Value.OrderBy(e => e.DisplayName).ToArray(); } + } - bool IDynamicEnumValuesGenerator.AllowCustomValues => false; + private bool IsReadyToBuild() + { + IConfiguredProjectReadyToBuild? readyToBuild = ReadyToBuild.FirstOrDefault()?.Value; - Task IDynamicEnumValuesGenerator.TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); + return readyToBuild?.IsValidToBuild == true; } + + bool IDynamicEnumValuesGenerator.AllowCustomValues => false; + + Task IDynamicEnumValuesGenerator.TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs index f381074d9a..a991be8615 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/HotReloadDiagnosticOutputService.cs @@ -3,29 +3,28 @@ using System.Diagnostics; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +[Export(typeof(IHotReloadDiagnosticOutputService))] +internal class HotReloadDiagnosticOutputService : IHotReloadDiagnosticOutputService { - [Export(typeof(IHotReloadDiagnosticOutputService))] - internal class HotReloadDiagnosticOutputService : IHotReloadDiagnosticOutputService - { - private readonly IProjectThreadingService _threadingService; - private readonly IHotReloadLogger _hotReloadLogger; + private readonly IProjectThreadingService _threadingService; + private readonly IHotReloadLogger _hotReloadLogger; - [ImportingConstructor] - public HotReloadDiagnosticOutputService(IProjectThreadingService threadingService, IHotReloadLogger hotReloadLogger) - { - _threadingService = threadingService; - _hotReloadLogger = hotReloadLogger; - } + [ImportingConstructor] + public HotReloadDiagnosticOutputService(IProjectThreadingService threadingService, IHotReloadLogger hotReloadLogger) + { + _threadingService = threadingService; + _hotReloadLogger = hotReloadLogger; + } - public void WriteLine(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken) - { - _threadingService.RunAndForget(() => _hotReloadLogger.LogAsync(hotReloadLogMessage, cancellationToken).AsTask(), unconfiguredProject: null); - } + public void WriteLine(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken) + { + _threadingService.RunAndForget(() => _hotReloadLogger.LogAsync(hotReloadLogMessage, cancellationToken).AsTask(), unconfiguredProject: null); + } - public static uint GetProcessId(Process? process = null) - { - return (uint)(process?.Id ?? 0); - } + public static uint GetProcessId(Process? process = null) + { + return (uint)(process?.Id ?? 0); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IHotReloadDiagnosticOutputService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IHotReloadDiagnosticOutputService.cs index dc7d3b9b73..7e9e6e4392 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IHotReloadDiagnosticOutputService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IHotReloadDiagnosticOutputService.cs @@ -2,19 +2,18 @@ using Microsoft.VisualStudio.Debugger.Contracts.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +/// +/// Defines a service to print out messages to the Hot Reload output window pane. +/// +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.OneOrZero)] +internal interface IHotReloadDiagnosticOutputService { /// - /// Defines a service to print out messages to the Hot Reload output window pane. + /// Writes a message to the Hot Reload diagnostic output window. /// - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.OneOrZero)] - internal interface IHotReloadDiagnosticOutputService - { - /// - /// Writes a message to the Hot Reload diagnostic output window. - /// - /// The message to write. - /// The cancellation token to pass to the IHotReloadLogger - void WriteLine(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken); - } + /// The message to write. + /// The cancellation token to pass to the IHotReloadLogger + void WriteLine(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadAgent.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadAgent.cs index b38259ce00..63932fa58a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadAgent.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadAgent.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.ExactlyOne)] +public interface IProjectHotReloadAgent { - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.ExactlyOne)] - public interface IProjectHotReloadAgent - { - IProjectHotReloadSession? CreateHotReloadSession(string id, int variant, string runtimeVersion, IProjectHotReloadSessionCallback callback); + IProjectHotReloadSession? CreateHotReloadSession(string id, int variant, string runtimeVersion, IProjectHotReloadSessionCallback callback); - IProjectHotReloadSession? CreateHotReloadSession(string id, string runtimeVersion, IProjectHotReloadSessionCallback callback); - } + IProjectHotReloadSession? CreateHotReloadSession(string id, string runtimeVersion, IProjectHotReloadSessionCallback callback); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSession.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSession.cs index baa5612366..dbc2cc2830 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSession.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSession.cs @@ -1,51 +1,50 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +/// +/// Represents a Hot Reload session within the project system. +/// +public interface IProjectHotReloadSession { /// - /// Represents a Hot Reload session within the project system. + /// A name used to identify the session in diagnostic messages. Not guaranteed to be + /// unique. /// - public interface IProjectHotReloadSession - { - /// - /// A name used to identify the session in diagnostic messages. Not guaranteed to be - /// unique. - /// - string Name { get; } + string Name { get; } - /// - /// Starts the Hot Reload Session. - /// - /// - /// TODO: remove when Web Tools is no longer calling this method. - /// - [Obsolete("This should no longer be used; please use StartSessionAsync(bool, CancellationToken) instead.", false)] - Task StartSessionAsync(CancellationToken cancellationToken); + /// + /// Starts the Hot Reload Session. + /// + /// + /// TODO: remove when Web Tools is no longer calling this method. + /// + [Obsolete("This should no longer be used; please use StartSessionAsync(bool, CancellationToken) instead.", false)] + Task StartSessionAsync(CancellationToken cancellationToken); - /// - /// Starts the Hot Reload Session. - /// - /// - /// if the process is being run under a debugger; - /// otherwise. - /// - /// A token indicating if the operation has been cancelled. - Task StartSessionAsync(bool runningUnderDebugger, CancellationToken cancellationToken); + /// + /// Starts the Hot Reload Session. + /// + /// + /// if the process is being run under a debugger; + /// otherwise. + /// + /// A token indicating if the operation has been cancelled. + Task StartSessionAsync(bool runningUnderDebugger, CancellationToken cancellationToken); - /// - /// Stops the Hot Reload Session. - /// - Task StopSessionAsync(CancellationToken cancellationToken); + /// + /// Stops the Hot Reload Session. + /// + Task StopSessionAsync(CancellationToken cancellationToken); - /// - /// Applies any pending changes to the Hot Reload session. - /// - Task ApplyChangesAsync(CancellationToken cancellationToken); + /// + /// Applies any pending changes to the Hot Reload session. + /// + Task ApplyChangesAsync(CancellationToken cancellationToken); - /// - /// Updates the environment variables in to include - /// settings needed by a process using Hot Reload. - /// - Task ApplyLaunchVariablesAsync(IDictionary envVars, CancellationToken cancellationToken); - } + /// + /// Updates the environment variables in to include + /// settings needed by a process using Hot Reload. + /// + Task ApplyLaunchVariablesAsync(IDictionary envVars, CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionCallback.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionCallback.cs index b92f3148d2..78a3ec5fd8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionCallback.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionCallback.cs @@ -3,29 +3,28 @@ using System.Diagnostics; using Microsoft.VisualStudio.HotReload.Components.DeltaApplier; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +public interface IProjectHotReloadSessionCallback { - public interface IProjectHotReloadSessionCallback - { - bool SupportsRestart { get; } + bool SupportsRestart { get; } - Task OnAfterChangesAppliedAsync(CancellationToken cancellationToken); + Task OnAfterChangesAppliedAsync(CancellationToken cancellationToken); - Task StopProjectAsync(CancellationToken cancellationToken); + Task StopProjectAsync(CancellationToken cancellationToken); - Task RestartProjectAsync(CancellationToken cancellationToken); + Task RestartProjectAsync(CancellationToken cancellationToken); - IDeltaApplier? GetDeltaApplier(); - } + IDeltaApplier? GetDeltaApplier(); +} - internal interface IProjectHotReloadSessionCallback2 : IProjectHotReloadSessionCallback - { - UnconfiguredProject? Project { get; } +internal interface IProjectHotReloadSessionCallback2 : IProjectHotReloadSessionCallback +{ + UnconfiguredProject? Project { get; } - Process? Process { get; } + Process? Process { get; } - IProjectHotReloadSession? Session { get; } + IProjectHotReloadSession? Session { get; } - Task RestartProjectAsync(bool isRunningUnderDebug, CancellationToken cancellationToken); - } + Task RestartProjectAsync(bool isRunningUnderDebug, CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionInternal.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionInternal.cs index 419b2a25cd..b9c122929c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionInternal.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionInternal.cs @@ -2,16 +2,15 @@ using Microsoft.VisualStudio.HotReload.Components.DeltaApplier; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +/// +/// Internal interface which Hot Reload sessions implement to provide access to the IDeltaApplier +/// +internal interface IProjectHotReloadSessionInternal { /// - /// Internal interface which Hot Reload sessions implement to provide access to the IDeltaApplier + /// Returns the delta applier for this session /// - internal interface IProjectHotReloadSessionInternal - { - /// - /// Returns the delta applier for this session - /// - IDeltaApplier? DeltaApplier { get; } - } + IDeltaApplier? DeltaApplier { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs index 2bc4648b3a..fa08a89f6f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadSessionManager.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +/// +/// Tracks and manages the pending and active Hot Reload sessions for the project. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IProjectHotReloadSessionManager { /// - /// Tracks and manages the pending and active Hot Reload sessions for the project. + /// Creates a pending Hot Reload session for the project as possible, and updates + /// the given accordingly. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectHotReloadSessionManager - { - /// - /// Creates a pending Hot Reload session for the project as possible, and updates - /// the given accordingly. - /// - /// - /// if the session was created; - /// otherwise. - /// - Task TryCreatePendingSessionAsync(IDictionary environmentVariables); + /// + /// if the session was created; + /// otherwise. + /// + Task TryCreatePendingSessionAsync(IDictionary environmentVariables); - /// - /// Activates the pending Hot Reload session and associates it with the specified - /// process. - /// - Task ActivateSessionAsync(int processId, bool runningUnderDebugger, string projectName); - } + /// + /// Activates the pending Hot Reload session and associates it with the specified + /// process. + /// + Task ActivateSessionAsync(int processId, bool runningUnderDebugger, string projectName); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadUpdateApplier.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadUpdateApplier.cs index af2b116bda..94f427008b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadUpdateApplier.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/IProjectHotReloadUpdateApplier.cs @@ -2,25 +2,24 @@ using Microsoft.VisualStudio.HotReload.Components.DeltaApplier; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +/// +/// Provides a mechanism to apply custom updates to the running hot reload sessions. Assumes all updates can be applied via the +/// IDeltaApplier created for this session +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.ExactlyOne)] +public interface IProjectHotReloadUpdateApplier { /// - /// Provides a mechanism to apply custom updates to the running hot reload sessions. Assumes all updates can be applied via the - /// IDeltaApplier created for this session + /// Returns true if there is at least one hot reload session active in the project /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.ExactlyOne)] - public interface IProjectHotReloadUpdateApplier - { - /// - /// Returns true if there is at least one hot reload session active in the project - /// - bool HasActiveHotReloadSessions { get; } + bool HasActiveHotReloadSessions { get; } - /// - /// Called to apply a custom update via the IDeltaApplier. The applyFunction will be called once for every active session, and passed the IDeltaApplier - /// for that session. In the case of multiple sessions, updates will stop being applied on the first call to an applyFunction that throws an exception, and - /// that exception propagates back to the caller. - /// - Task ApplyHotReloadUpdateAsync(Func applyFunction, CancellationToken cancelToken); - } + /// + /// Called to apply a custom update via the IDeltaApplier. The applyFunction will be called once for every active session, and passed the IDeltaApplier + /// for that session. In the case of multiple sessions, updates will stop being applied on the first call to an applyFunction that throws an exception, and + /// that exception propagates back to the caller. + /// + Task ApplyHotReloadUpdateAsync(Func applyFunction, CancellationToken cancelToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadAgent.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadAgent.cs index ea6f641581..211a6b69a1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadAgent.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadAgent.cs @@ -3,41 +3,40 @@ using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Microsoft.VisualStudio.HotReload.Components.DeltaApplier; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +[Export(typeof(IProjectHotReloadAgent))] +internal class ProjectHotReloadAgent : IProjectHotReloadAgent { - [Export(typeof(IProjectHotReloadAgent))] - internal class ProjectHotReloadAgent : IProjectHotReloadAgent - { - private readonly Lazy _hotReloadAgentManagerClient; - private readonly Lazy _hotReloadDiagnosticOutputService; - private readonly Lazy _managedDeltaApplierCreator; + private readonly Lazy _hotReloadAgentManagerClient; + private readonly Lazy _hotReloadDiagnosticOutputService; + private readonly Lazy _managedDeltaApplierCreator; - [ImportingConstructor] - public ProjectHotReloadAgent( - Lazy hotReloadAgentManagerClient, - Lazy hotReloadDiagnosticOutputService, - Lazy managedDeltaApplierCreator) - { - _hotReloadAgentManagerClient = hotReloadAgentManagerClient; - _hotReloadDiagnosticOutputService = hotReloadDiagnosticOutputService; - _managedDeltaApplierCreator = managedDeltaApplierCreator; - } + [ImportingConstructor] + public ProjectHotReloadAgent( + Lazy hotReloadAgentManagerClient, + Lazy hotReloadDiagnosticOutputService, + Lazy managedDeltaApplierCreator) + { + _hotReloadAgentManagerClient = hotReloadAgentManagerClient; + _hotReloadDiagnosticOutputService = hotReloadDiagnosticOutputService; + _managedDeltaApplierCreator = managedDeltaApplierCreator; + } - public IProjectHotReloadSession? CreateHotReloadSession(string id, int variant, string runtimeVersion, IProjectHotReloadSessionCallback callback) - { - return new ProjectHotReloadSession( - id, - variant, - runtimeVersion, - _hotReloadAgentManagerClient, - _hotReloadDiagnosticOutputService, - _managedDeltaApplierCreator, - callback); - } + public IProjectHotReloadSession? CreateHotReloadSession(string id, int variant, string runtimeVersion, IProjectHotReloadSessionCallback callback) + { + return new ProjectHotReloadSession( + id, + variant, + runtimeVersion, + _hotReloadAgentManagerClient, + _hotReloadDiagnosticOutputService, + _managedDeltaApplierCreator, + callback); + } - public IProjectHotReloadSession? CreateHotReloadSession(string id, string runtimeVersion, IProjectHotReloadSessionCallback callback) - { - return CreateHotReloadSession(id, 0, runtimeVersion, callback); - } + public IProjectHotReloadSession? CreateHotReloadSession(string id, string runtimeVersion, IProjectHotReloadSessionCallback callback) + { + return CreateHotReloadSession(id, 0, runtimeVersion, callback); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSession.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSession.cs index ebb7823aa1..b346e88c0e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSession.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSession.cs @@ -4,229 +4,228 @@ using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Microsoft.VisualStudio.HotReload.Components.DeltaApplier; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +internal class ProjectHotReloadSession : IManagedHotReloadAgent, IManagedHotReloadAgent2, IManagedHotReloadAgent4, IProjectHotReloadSession, IProjectHotReloadSessionInternal { - internal class ProjectHotReloadSession : IManagedHotReloadAgent, IManagedHotReloadAgent2, IManagedHotReloadAgent4, IProjectHotReloadSession, IProjectHotReloadSessionInternal - { - private readonly string _variant; - private readonly string _runtimeVersion; - private readonly Lazy _hotReloadAgentManagerClient; - private readonly Lazy _hotReloadOutputService; - private readonly Lazy _deltaApplierCreator; - private readonly IProjectHotReloadSessionCallback _callback; - - private bool _sessionActive; - - // This flag is used to identify Debug|NonDebug cases - private bool _isRunningUnderDebugger; - private IDeltaApplier? _deltaApplier; - - public ProjectHotReloadSession( - string name, - int variant, - string runtimeVersion, - Lazy hotReloadAgentManagerClient, - Lazy hotReloadOutputService, - Lazy deltaApplierCreator, - IProjectHotReloadSessionCallback callback) - { - Name = name; - _variant = variant.ToString(); - _runtimeVersion = runtimeVersion; - _hotReloadAgentManagerClient = hotReloadAgentManagerClient; - _hotReloadOutputService = hotReloadOutputService; - _deltaApplierCreator = deltaApplierCreator; - _callback = callback; - } + private readonly string _variant; + private readonly string _runtimeVersion; + private readonly Lazy _hotReloadAgentManagerClient; + private readonly Lazy _hotReloadOutputService; + private readonly Lazy _deltaApplierCreator; + private readonly IProjectHotReloadSessionCallback _callback; + + private bool _sessionActive; + + // This flag is used to identify Debug|NonDebug cases + private bool _isRunningUnderDebugger; + private IDeltaApplier? _deltaApplier; + + public ProjectHotReloadSession( + string name, + int variant, + string runtimeVersion, + Lazy hotReloadAgentManagerClient, + Lazy hotReloadOutputService, + Lazy deltaApplierCreator, + IProjectHotReloadSessionCallback callback) + { + Name = name; + _variant = variant.ToString(); + _runtimeVersion = runtimeVersion; + _hotReloadAgentManagerClient = hotReloadAgentManagerClient; + _hotReloadOutputService = hotReloadOutputService; + _deltaApplierCreator = deltaApplierCreator; + _callback = callback; + } - // IProjectHotReloadSession + // IProjectHotReloadSession - public string Name { get; } + public string Name { get; } - public IDeltaApplier? DeltaApplier => _deltaApplier; + public IDeltaApplier? DeltaApplier => _deltaApplier; - public async Task ApplyChangesAsync(CancellationToken cancellationToken) + public async Task ApplyChangesAsync(CancellationToken cancellationToken) + { + if (_sessionActive) { - if (_sessionActive) - { - await _hotReloadAgentManagerClient.Value.ApplyUpdatesAsync(cancellationToken); - } + await _hotReloadAgentManagerClient.Value.ApplyUpdatesAsync(cancellationToken); } + } - public async Task ApplyLaunchVariablesAsync(IDictionary envVars, CancellationToken cancellationToken) - { - EnsureDeltaApplierForSession(); + public async Task ApplyLaunchVariablesAsync(IDictionary envVars, CancellationToken cancellationToken) + { + EnsureDeltaApplierForSession(); - return await _deltaApplier.ApplyProcessEnvironmentVariablesAsync(envVars, cancellationToken); - } + return await _deltaApplier.ApplyProcessEnvironmentVariablesAsync(envVars, cancellationToken); + } + + // TODO: remove when Web Tools is no longer calling this method. + public Task StartSessionAsync(CancellationToken cancellationToken) + { + return StartSessionAsync(runningUnderDebugger: false, cancellationToken); + } - // TODO: remove when Web Tools is no longer calling this method. - public Task StartSessionAsync(CancellationToken cancellationToken) + public async Task StartSessionAsync(bool runningUnderDebugger, CancellationToken cancellationToken) + { + if (_sessionActive) { - return StartSessionAsync(runningUnderDebugger: false, cancellationToken); + throw new InvalidOperationException("Attempting to start a Hot Reload session that is already running."); } - public async Task StartSessionAsync(bool runningUnderDebugger, CancellationToken cancellationToken) - { - if (_sessionActive) - { - throw new InvalidOperationException("Attempting to start a Hot Reload session that is already running."); - } + HotReloadAgentFlags flags = runningUnderDebugger ? HotReloadAgentFlags.IsDebuggedProcess : HotReloadAgentFlags.None; + await _hotReloadAgentManagerClient.Value.AgentStartedAsync(this, flags, cancellationToken); - HotReloadAgentFlags flags = runningUnderDebugger ? HotReloadAgentFlags.IsDebuggedProcess : HotReloadAgentFlags.None; - await _hotReloadAgentManagerClient.Value.AgentStartedAsync(this, flags, cancellationToken); + WriteToOutputWindow(VSResources.HotReloadStartSession, default); + _sessionActive = true; + _isRunningUnderDebugger = runningUnderDebugger; + EnsureDeltaApplierForSession(); + } - WriteToOutputWindow(VSResources.HotReloadStartSession, default); - _sessionActive = true; - _isRunningUnderDebugger = runningUnderDebugger; - EnsureDeltaApplierForSession(); + public async Task StopSessionAsync(CancellationToken cancellationToken) + { + if (_sessionActive) + { + _sessionActive = false; + await _hotReloadAgentManagerClient.Value.AgentTerminatedAsync(this, cancellationToken); + + WriteToOutputWindow(VSResources.HotReloadStopSession, default); } + } - public async Task StopSessionAsync(CancellationToken cancellationToken) - { - if (_sessionActive) - { - _sessionActive = false; - await _hotReloadAgentManagerClient.Value.AgentTerminatedAsync(this, cancellationToken); + // IManagedHotReloadAgent - WriteToOutputWindow(VSResources.HotReloadStopSession, default); - } + public async ValueTask ApplyUpdatesAsync(ImmutableArray updates, CancellationToken cancellationToken) + { + if (!_sessionActive) + { + WriteToOutputWindow($"{nameof(ApplyUpdatesAsync)} called but the session is not active.", default, HotReloadVerbosity.Detailed); + return; } - // IManagedHotReloadAgent + if (_deltaApplier is null) + { + WriteToOutputWindow($"{nameof(ApplyUpdatesAsync)} called but we have no delta applier.", default, HotReloadVerbosity.Detailed); + return; + } - public async ValueTask ApplyUpdatesAsync(ImmutableArray updates, CancellationToken cancellationToken) + try { - if (!_sessionActive) - { - WriteToOutputWindow($"{nameof(ApplyUpdatesAsync)} called but the session is not active.", default, HotReloadVerbosity.Detailed); - return; - } + WriteToOutputWindow(VSResources.HotReloadSendingUpdates, cancellationToken, HotReloadVerbosity.Detailed); - if (_deltaApplier is null) - { - WriteToOutputWindow($"{nameof(ApplyUpdatesAsync)} called but we have no delta applier.", default, HotReloadVerbosity.Detailed); - return; - } + ApplyResult result = await _deltaApplier.ApplyUpdatesAsync(updates, cancellationToken); - try + if (result is ApplyResult.Success or ApplyResult.SuccessRefreshUI) { - WriteToOutputWindow(VSResources.HotReloadSendingUpdates, cancellationToken, HotReloadVerbosity.Detailed); - - ApplyResult result = await _deltaApplier.ApplyUpdatesAsync(updates, cancellationToken); + WriteToOutputWindow(VSResources.HotReloadApplyUpdatesSuccessful, cancellationToken, HotReloadVerbosity.Detailed); - if (result is ApplyResult.Success or ApplyResult.SuccessRefreshUI) + if (_callback is not null) { - WriteToOutputWindow(VSResources.HotReloadApplyUpdatesSuccessful, cancellationToken, HotReloadVerbosity.Detailed); - - if (_callback is not null) - { - await _callback.OnAfterChangesAppliedAsync(cancellationToken); - } + await _callback.OnAfterChangesAppliedAsync(cancellationToken); } } - catch (Exception ex) - { - WriteToOutputWindow( - string.Format(VSResources.HotReloadApplyUpdatesFailure, $"{ex.GetType()}: {ex.Message}"), - cancellationToken, - errorLevel: HotReloadDiagnosticErrorLevel.Error); - throw; - } } - - public async ValueTask> GetCapabilitiesAsync(CancellationToken cancellationToken) + catch (Exception ex) { - // Delegate to the delta applier for the session - if (_deltaApplier is not null) - { - return await _deltaApplier.GetCapabilitiesAsync(cancellationToken); - } - - return []; + WriteToOutputWindow( + string.Format(VSResources.HotReloadApplyUpdatesFailure, $"{ex.GetType()}: {ex.Message}"), + cancellationToken, + errorLevel: HotReloadDiagnosticErrorLevel.Error); + throw; } + } - public ValueTask ReportDiagnosticsAsync(ImmutableArray diagnostics, CancellationToken cancellationToken) + public async ValueTask> GetCapabilitiesAsync(CancellationToken cancellationToken) + { + // Delegate to the delta applier for the session + if (_deltaApplier is not null) { - WriteToOutputWindow(VSResources.HotReloadErrorsInApplication, cancellationToken, errorLevel: HotReloadDiagnosticErrorLevel.Error); - - foreach (ManagedHotReloadDiagnostic diagnostic in diagnostics) - { - WriteToOutputWindow( - $"{diagnostic.FilePath}({diagnostic.Span.StartLine},{diagnostic.Span.StartColumn},{diagnostic.Span.EndLine},{diagnostic.Span.EndColumn}): {diagnostic.Message}", - cancellationToken, - errorLevel: HotReloadDiagnosticErrorLevel.Error); - } - - return new ValueTask(Task.CompletedTask); + return await _deltaApplier.GetCapabilitiesAsync(cancellationToken); } - public async ValueTask RestartAsync(CancellationToken cancellationToken) - { - WriteToOutputWindow(VSResources.HotReloadRestartInProgress, cancellationToken); + return []; + } - if (_callback is IProjectHotReloadSessionCallback2 callBack2) - { - await callBack2.RestartProjectAsync(_isRunningUnderDebugger, cancellationToken); - } - else - { - await _callback.RestartProjectAsync(cancellationToken); - } - } + public ValueTask ReportDiagnosticsAsync(ImmutableArray diagnostics, CancellationToken cancellationToken) + { + WriteToOutputWindow(VSResources.HotReloadErrorsInApplication, cancellationToken, errorLevel: HotReloadDiagnosticErrorLevel.Error); - public async ValueTask StopAsync(CancellationToken cancellationToken) + foreach (ManagedHotReloadDiagnostic diagnostic in diagnostics) { - WriteToOutputWindow(VSResources.HotReloadStoppingApplication, cancellationToken); + WriteToOutputWindow( + $"{diagnostic.FilePath}({diagnostic.Span.StartLine},{diagnostic.Span.StartColumn},{diagnostic.Span.EndLine},{diagnostic.Span.EndColumn}): {diagnostic.Message}", + cancellationToken, + errorLevel: HotReloadDiagnosticErrorLevel.Error); + } - await _callback.StopProjectAsync(cancellationToken); + return new ValueTask(Task.CompletedTask); + } - // TODO: Should we stop the session here? Or does someone else do it? - } + public async ValueTask RestartAsync(CancellationToken cancellationToken) + { + WriteToOutputWindow(VSResources.HotReloadRestartInProgress, cancellationToken); - public ValueTask SupportsRestartAsync(CancellationToken cancellationToken) + if (_callback is IProjectHotReloadSessionCallback2 callBack2) { - return new ValueTask(_callback.SupportsRestart); + await callBack2.RestartProjectAsync(_isRunningUnderDebugger, cancellationToken); } - - private void WriteToOutputWindow(string message, CancellationToken cancellationToken, HotReloadVerbosity verbosity = HotReloadVerbosity.Minimal, HotReloadDiagnosticErrorLevel errorLevel = HotReloadDiagnosticErrorLevel.Info) + else { - _hotReloadOutputService.Value.WriteLine( - new HotReloadLogMessage( - verbosity, - message, - Name, - _variant, - errorLevel: errorLevel), - cancellationToken); + await _callback.RestartProjectAsync(cancellationToken); } + } - [MemberNotNull(nameof(_deltaApplier))] - private void EnsureDeltaApplierForSession() - { - _deltaApplier ??= _callback.GetDeltaApplier() ?? _deltaApplierCreator.Value.CreateManagedDeltaApplier(_runtimeVersion); + public async ValueTask StopAsync(CancellationToken cancellationToken) + { + WriteToOutputWindow(VSResources.HotReloadStoppingApplication, cancellationToken); - Assumes.NotNull(_deltaApplier); - } + await _callback.StopProjectAsync(cancellationToken); - public ValueTask GetTargetLocalProcessIdAsync(CancellationToken cancellationToken) - { - if (_callback is IProjectHotReloadSessionCallback2 callback2) - { - return new ValueTask(callback2.Process?.Id); - } + // TODO: Should we stop the session here? Or does someone else do it? + } - return new ValueTask(); - } + public ValueTask SupportsRestartAsync(CancellationToken cancellationToken) + { + return new ValueTask(_callback.SupportsRestart); + } + + private void WriteToOutputWindow(string message, CancellationToken cancellationToken, HotReloadVerbosity verbosity = HotReloadVerbosity.Minimal, HotReloadDiagnosticErrorLevel errorLevel = HotReloadDiagnosticErrorLevel.Info) + { + _hotReloadOutputService.Value.WriteLine( + new HotReloadLogMessage( + verbosity, + message, + Name, + _variant, + errorLevel: errorLevel), + cancellationToken); + } + + [MemberNotNull(nameof(_deltaApplier))] + private void EnsureDeltaApplierForSession() + { + _deltaApplier ??= _callback.GetDeltaApplier() ?? _deltaApplierCreator.Value.CreateManagedDeltaApplier(_runtimeVersion); - public ValueTask GetProjectFullPathAsync(CancellationToken cancellationToken) + Assumes.NotNull(_deltaApplier); + } + + public ValueTask GetTargetLocalProcessIdAsync(CancellationToken cancellationToken) + { + if (_callback is IProjectHotReloadSessionCallback2 callback2) { - if (_callback is IProjectHotReloadSessionCallback2 callback2) - { - return new ValueTask(callback2.Project?.FullPath); - } + return new ValueTask(callback2.Process?.Id); + } - return new ValueTask(); + return new ValueTask(); + } + + public ValueTask GetProjectFullPathAsync(CancellationToken cancellationToken) + { + if (_callback is IProjectHotReloadSessionCallback2 callback2) + { + return new ValueTask(callback2.Project?.FullPath); } + + return new ValueTask(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs index 91fbd3b578..2fc71bbdb6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManager.cs @@ -9,525 +9,524 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +[Export(typeof(IProjectHotReloadSessionManager))] +[Export(typeof(IProjectHotReloadUpdateApplier))] +internal class ProjectHotReloadSessionManager : OnceInitializedOnceDisposedAsync, IProjectHotReloadSessionManager, IProjectHotReloadUpdateApplier { - [Export(typeof(IProjectHotReloadSessionManager))] - [Export(typeof(IProjectHotReloadUpdateApplier))] - internal class ProjectHotReloadSessionManager : OnceInitializedOnceDisposedAsync, IProjectHotReloadSessionManager, IProjectHotReloadUpdateApplier + private readonly UnconfiguredProject _project; + private readonly IProjectFaultHandlerService _projectFaultHandlerService; + private readonly IActiveDebugFrameworkServices _activeDebugFrameworkServices; + private readonly Lazy _projectHotReloadAgent; + private readonly Lazy _hotReloadDiagnosticOutputService; + private readonly Lazy _projectHotReloadNotificationService; + private readonly IVsService _vsSolutionBuildManagerService; + private IVsSolutionBuildManager2? _vsSolutionBuildManager2; + private readonly IProjectThreadingService _projectThreadingService; + + // Protect the state from concurrent access. For example, our Process.Exited event + // handler may run on one thread while we're still setting up the session on + // another. To ensure consistent and proper behavior we need to serialize access. + private readonly ReentrantSemaphore _semaphore; + + private readonly Dictionary _activeSessions = []; + private HotReloadState? _pendingSessionState = null; + private int _nextUniqueId = 1; + + public bool HasActiveHotReloadSessions => _activeSessions.Count != 0; + + [ImportingConstructor] + public ProjectHotReloadSessionManager( + UnconfiguredProject project, + IProjectThreadingService threadingService, + IProjectFaultHandlerService projectFaultHandlerService, + IActiveDebugFrameworkServices activeDebugFrameworkServices, + Lazy projectHotReloadAgent, + Lazy hotReloadDiagnosticOutputService, + Lazy projectHotReloadNotificationService, + IVsService solutionBuildManagerService) + : base(threadingService.JoinableTaskContext) { - private readonly UnconfiguredProject _project; - private readonly IProjectFaultHandlerService _projectFaultHandlerService; - private readonly IActiveDebugFrameworkServices _activeDebugFrameworkServices; - private readonly Lazy _projectHotReloadAgent; - private readonly Lazy _hotReloadDiagnosticOutputService; - private readonly Lazy _projectHotReloadNotificationService; - private readonly IVsService _vsSolutionBuildManagerService; - private IVsSolutionBuildManager2? _vsSolutionBuildManager2; - private readonly IProjectThreadingService _projectThreadingService; - - // Protect the state from concurrent access. For example, our Process.Exited event - // handler may run on one thread while we're still setting up the session on - // another. To ensure consistent and proper behavior we need to serialize access. - private readonly ReentrantSemaphore _semaphore; - - private readonly Dictionary _activeSessions = []; - private HotReloadState? _pendingSessionState = null; - private int _nextUniqueId = 1; - - public bool HasActiveHotReloadSessions => _activeSessions.Count != 0; - - [ImportingConstructor] - public ProjectHotReloadSessionManager( - UnconfiguredProject project, - IProjectThreadingService threadingService, - IProjectFaultHandlerService projectFaultHandlerService, - IActiveDebugFrameworkServices activeDebugFrameworkServices, - Lazy projectHotReloadAgent, - Lazy hotReloadDiagnosticOutputService, - Lazy projectHotReloadNotificationService, - IVsService solutionBuildManagerService) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _projectThreadingService = threadingService; - _projectFaultHandlerService = projectFaultHandlerService; - _activeDebugFrameworkServices = activeDebugFrameworkServices; - _projectHotReloadAgent = projectHotReloadAgent; - _hotReloadDiagnosticOutputService = hotReloadDiagnosticOutputService; - _projectHotReloadNotificationService = projectHotReloadNotificationService; - _vsSolutionBuildManagerService = solutionBuildManagerService; - - _semaphore = ReentrantSemaphore.Create( - initialCount: 1, - joinableTaskContext: project.Services.ThreadingPolicy.JoinableTaskContext.Context, - mode: ReentrantSemaphore.ReentrancyMode.Freeform); - } + _project = project; + _projectThreadingService = threadingService; + _projectFaultHandlerService = projectFaultHandlerService; + _activeDebugFrameworkServices = activeDebugFrameworkServices; + _projectHotReloadAgent = projectHotReloadAgent; + _hotReloadDiagnosticOutputService = hotReloadDiagnosticOutputService; + _projectHotReloadNotificationService = projectHotReloadNotificationService; + _vsSolutionBuildManagerService = solutionBuildManagerService; + + _semaphore = ReentrantSemaphore.Create( + initialCount: 1, + joinableTaskContext: project.Services.ThreadingPolicy.JoinableTaskContext.Context, + mode: ReentrantSemaphore.ReentrancyMode.Freeform); + } - public async Task ActivateSessionAsync(int processId, bool runningUnderDebugger, string projectName) - { - await _semaphore.ExecuteAsync(ActivateSessionInternalAsync); + public async Task ActivateSessionAsync(int processId, bool runningUnderDebugger, string projectName) + { + await _semaphore.ExecuteAsync(ActivateSessionInternalAsync); - async Task ActivateSessionInternalAsync() + async Task ActivateSessionInternalAsync() + { + if (_pendingSessionState is not null) { - if (_pendingSessionState is not null) - { - Assumes.NotNull(_pendingSessionState.Session); + Assumes.NotNull(_pendingSessionState.Session); - try - { - Process? process = Process.GetProcessById(processId); - - WriteOutputMessage( - new HotReloadLogMessage( - HotReloadVerbosity.Detailed, - VSResources.ProjectHotReloadSessionManager_AttachingToProcess, - projectName, - _pendingSessionState.Session.Name, - (uint)processId, - HotReloadDiagnosticErrorLevel.Info - ), - default); + try + { + Process? process = Process.GetProcessById(processId); - process.Exited += _pendingSessionState.OnProcessExited; - process.EnableRaisingEvents = true; + WriteOutputMessage( + new HotReloadLogMessage( + HotReloadVerbosity.Detailed, + VSResources.ProjectHotReloadSessionManager_AttachingToProcess, + projectName, + _pendingSessionState.Session.Name, + (uint)processId, + HotReloadDiagnosticErrorLevel.Info + ), + default); - if (process.HasExited) - { - WriteOutputMessage( - new HotReloadLogMessage( - HotReloadVerbosity.Detailed, - VSResources.ProjectHotReloadSessionManager_ProcessAlreadyExited, - projectName, - _pendingSessionState.Session.Name, - (uint)processId, - HotReloadDiagnosticErrorLevel.Info - ), - default); - - process.Exited -= _pendingSessionState.OnProcessExited; - process = null; - } + process.Exited += _pendingSessionState.OnProcessExited; + process.EnableRaisingEvents = true; - _pendingSessionState.Process = process; - } - catch (Exception ex) + if (process.HasExited) { WriteOutputMessage( new HotReloadLogMessage( - HotReloadVerbosity.Minimal, - $"${ex.GetType()}: ${ex.Message}", - projectName, - _pendingSessionState.Session.Name, - (uint)processId, - HotReloadDiagnosticErrorLevel.Error - ), - default); - } - - if (_pendingSessionState.Process is null) - { - WriteOutputMessage( - new HotReloadLogMessage( - HotReloadVerbosity.Minimal, - VSResources.ProjectHotReloadSessionManager_NoActiveProcess, + HotReloadVerbosity.Detailed, + VSResources.ProjectHotReloadSessionManager_ProcessAlreadyExited, projectName, _pendingSessionState.Session.Name, (uint)processId, - HotReloadDiagnosticErrorLevel.Warning + HotReloadDiagnosticErrorLevel.Info ), default); - } - else - { - await _pendingSessionState.Session.StartSessionAsync(runningUnderDebugger, cancellationToken: default); - _activeSessions.Add(processId, _pendingSessionState); - // Addition of the first session, puts the project in hot reload mode - if (_activeSessions.Count == 1) - { - await _projectHotReloadNotificationService.Value.SetHotReloadStateAsync(isInHotReload: true); - } + process.Exited -= _pendingSessionState.OnProcessExited; + process = null; } - _pendingSessionState = null; + _pendingSessionState.Process = process; } - } - } - - public Task TryCreatePendingSessionAsync(IDictionary environmentVariables) - { - return _semaphore.ExecuteAsync(TryCreatePendingSessionInternalAsync).AsTask(); - - async ValueTask TryCreatePendingSessionInternalAsync() - { - if (await DebugFrameworkSupportsHotReloadAsync() - && await GetDebugFrameworkVersionAsync() is string frameworkVersion - && !string.IsNullOrWhiteSpace(frameworkVersion)) + catch (Exception ex) { - if (await DebugFrameworkSupportsStartupHooksAsync()) - { - string name = Path.GetFileNameWithoutExtension(_project.FullPath); - HotReloadState state = new(this); - IProjectHotReloadSession? projectHotReloadSession = _projectHotReloadAgent.Value.CreateHotReloadSession(name, _nextUniqueId++, frameworkVersion, state); + WriteOutputMessage( + new HotReloadLogMessage( + HotReloadVerbosity.Minimal, + $"${ex.GetType()}: ${ex.Message}", + projectName, + _pendingSessionState.Session.Name, + (uint)processId, + HotReloadDiagnosticErrorLevel.Error + ), + default); + } - if (projectHotReloadSession is not null) - { - state.Session = projectHotReloadSession; - await projectHotReloadSession.ApplyLaunchVariablesAsync(environmentVariables, default); - _pendingSessionState = state; + if (_pendingSessionState.Process is null) + { + WriteOutputMessage( + new HotReloadLogMessage( + HotReloadVerbosity.Minimal, + VSResources.ProjectHotReloadSessionManager_NoActiveProcess, + projectName, + _pendingSessionState.Session.Name, + (uint)processId, + HotReloadDiagnosticErrorLevel.Warning + ), + default); + } + else + { + await _pendingSessionState.Session.StartSessionAsync(runningUnderDebugger, cancellationToken: default); + _activeSessions.Add(processId, _pendingSessionState); - return true; - } - } - else + // Addition of the first session, puts the project in hot reload mode + if (_activeSessions.Count == 1) { - // If startup hooks are not supported then tell the user why Hot Reload isn't available. - string projectName = Path.GetFileNameWithoutExtension(_project.FullPath); - - WriteOutputMessage( - new HotReloadLogMessage( - HotReloadVerbosity.Minimal, - VSResources.ProjectHotReloadSessionManager_StartupHooksDisabled, - projectName, - null, - HotReloadDiagnosticOutputService.GetProcessId(), - HotReloadDiagnosticErrorLevel.Warning - ), - default); + await _projectHotReloadNotificationService.Value.SetHotReloadStateAsync(isInHotReload: true); } } _pendingSessionState = null; - return false; } } + } - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } + public Task TryCreatePendingSessionAsync(IDictionary environmentVariables) + { + return _semaphore.ExecuteAsync(TryCreatePendingSessionInternalAsync).AsTask(); - protected override Task DisposeCoreAsync(bool initialized) + async ValueTask TryCreatePendingSessionInternalAsync() { - return _semaphore.ExecuteAsync(DisposeCoreInternalAsync); - - Task DisposeCoreInternalAsync() + if (await DebugFrameworkSupportsHotReloadAsync() + && await GetDebugFrameworkVersionAsync() is string frameworkVersion + && !string.IsNullOrWhiteSpace(frameworkVersion)) { - foreach (HotReloadState sessionState in _activeSessions.Values) + if (await DebugFrameworkSupportsStartupHooksAsync()) { - Assumes.NotNull(sessionState.Process); - Assumes.NotNull(sessionState.Session); + string name = Path.GetFileNameWithoutExtension(_project.FullPath); + HotReloadState state = new(this); + IProjectHotReloadSession? projectHotReloadSession = _projectHotReloadAgent.Value.CreateHotReloadSession(name, _nextUniqueId++, frameworkVersion, state); - sessionState.Process.Exited -= sessionState.OnProcessExited; - _projectFaultHandlerService.Forget(sessionState.Session.StopSessionAsync(default), _project); - } + if (projectHotReloadSession is not null) + { + state.Session = projectHotReloadSession; + await projectHotReloadSession.ApplyLaunchVariablesAsync(environmentVariables, default); + _pendingSessionState = state; - _activeSessions.Clear(); + return true; + } + } + else + { + // If startup hooks are not supported then tell the user why Hot Reload isn't available. + string projectName = Path.GetFileNameWithoutExtension(_project.FullPath); - return Task.CompletedTask; + WriteOutputMessage( + new HotReloadLogMessage( + HotReloadVerbosity.Minimal, + VSResources.ProjectHotReloadSessionManager_StartupHooksDisabled, + projectName, + null, + HotReloadDiagnosticOutputService.GetProcessId(), + HotReloadDiagnosticErrorLevel.Warning + ), + default); + } } - } - - private void WriteOutputMessage(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken) => _hotReloadDiagnosticOutputService.Value.WriteLine(hotReloadLogMessage, cancellationToken); - /// - /// Checks if the project configuration targeted for debugging/launch meets the - /// basic requirements to support Hot Reload. Note that there may be other, specific - /// settings that prevent the use of Hot Reload even if the basic requirements are met. - /// - private async Task DebugFrameworkSupportsHotReloadAsync() - { - return await ConfiguredProjectForDebugHasHotReloadCapabilityAsync() - && await DebugSymbolsEnabledInConfiguredProjectForDebugAsync() - && !await OptimizeEnabledInConfiguredProjectForDebugAsync(); + _pendingSessionState = null; + return false; } + } - private Task GetConfiguredProjectForDebugAsync() - => _activeDebugFrameworkServices.GetConfiguredProjectForActiveFrameworkAsync(); + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + protected override Task DisposeCoreAsync(bool initialized) + { + return _semaphore.ExecuteAsync(DisposeCoreInternalAsync); - private async Task GetDebugFrameworkVersionAsync() + Task DisposeCoreInternalAsync() { - if (await GetPropertyFromDebugFrameworkAsync(ConfigurationGeneral.TargetFrameworkVersionProperty) is string targetFrameworkVersion) + foreach (HotReloadState sessionState in _activeSessions.Values) { - if (targetFrameworkVersion.StartsWith("v", StringComparison.OrdinalIgnoreCase)) - { - targetFrameworkVersion = targetFrameworkVersion.Substring(startIndex: 1); - } + Assumes.NotNull(sessionState.Process); + Assumes.NotNull(sessionState.Session); - return targetFrameworkVersion; + sessionState.Process.Exited -= sessionState.OnProcessExited; + _projectFaultHandlerService.Forget(sessionState.Session.StopSessionAsync(default), _project); } - return null; + _activeSessions.Clear(); + + return Task.CompletedTask; } + } - /// - /// Returns whether or not the project configuration targeted for debugging/launch - /// supports startup hooks. These are used to start Hot Reload in the launched - /// process. - /// - private async Task DebugFrameworkSupportsStartupHooksAsync() - { - if (await GetPropertyFromDebugFrameworkAsync(ConfigurationGeneral.StartupHookSupportProperty) is string startupHookSupport) - { - return !StringComparers.PropertyLiteralValues.Equals(startupHookSupport, "false"); - } + private void WriteOutputMessage(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken) => _hotReloadDiagnosticOutputService.Value.WriteLine(hotReloadLogMessage, cancellationToken); - return true; - } + /// + /// Checks if the project configuration targeted for debugging/launch meets the + /// basic requirements to support Hot Reload. Note that there may be other, specific + /// settings that prevent the use of Hot Reload even if the basic requirements are met. + /// + private async Task DebugFrameworkSupportsHotReloadAsync() + { + return await ConfiguredProjectForDebugHasHotReloadCapabilityAsync() + && await DebugSymbolsEnabledInConfiguredProjectForDebugAsync() + && !await OptimizeEnabledInConfiguredProjectForDebugAsync(); + } - /// - /// Returns whether or not the project configuration targeted for debugging/launch - /// optimizes binaries. Defaults to false if the property is not defined. - /// - private async Task OptimizeEnabledInConfiguredProjectForDebugAsync() + private Task GetConfiguredProjectForDebugAsync() + => _activeDebugFrameworkServices.GetConfiguredProjectForActiveFrameworkAsync(); + + private async Task GetDebugFrameworkVersionAsync() + { + if (await GetPropertyFromDebugFrameworkAsync(ConfigurationGeneral.TargetFrameworkVersionProperty) is string targetFrameworkVersion) { - if (await GetPropertyFromDebugFrameworkAsync("Optimize") is string optimize) + if (targetFrameworkVersion.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { - return StringComparers.PropertyLiteralValues.Equals(optimize, "true"); + targetFrameworkVersion = targetFrameworkVersion.Substring(startIndex: 1); } - return false; + return targetFrameworkVersion; } - /// - /// Returns whether or not the project configuration targeted for debugging/launch - /// emits debug symbols. Defaults to false if the property is not defined. - /// - private async Task DebugSymbolsEnabledInConfiguredProjectForDebugAsync() - { - if (await GetPropertyFromDebugFrameworkAsync("DebugSymbols") is string debugSymbols) - { - return StringComparers.PropertyLiteralValues.Equals(debugSymbols, "true"); - } + return null; + } - return false; + /// + /// Returns whether or not the project configuration targeted for debugging/launch + /// supports startup hooks. These are used to start Hot Reload in the launched + /// process. + /// + private async Task DebugFrameworkSupportsStartupHooksAsync() + { + if (await GetPropertyFromDebugFrameworkAsync(ConfigurationGeneral.StartupHookSupportProperty) is string startupHookSupport) + { + return !StringComparers.PropertyLiteralValues.Equals(startupHookSupport, "false"); } - private async Task ConfiguredProjectForDebugHasHotReloadCapabilityAsync() - { - ConfiguredProject? configuredProjectForDebug = await GetConfiguredProjectForDebugAsync(); - if (configuredProjectForDebug is null) - { - return false; - } + return true; + } - return configuredProjectForDebug.Capabilities.AppliesTo("SupportsHotReload"); + /// + /// Returns whether or not the project configuration targeted for debugging/launch + /// optimizes binaries. Defaults to false if the property is not defined. + /// + private async Task OptimizeEnabledInConfiguredProjectForDebugAsync() + { + if (await GetPropertyFromDebugFrameworkAsync("Optimize") is string optimize) + { + return StringComparers.PropertyLiteralValues.Equals(optimize, "true"); } - private async Task GetPropertyFromDebugFrameworkAsync(string propertyName) + return false; + } + + /// + /// Returns whether or not the project configuration targeted for debugging/launch + /// emits debug symbols. Defaults to false if the property is not defined. + /// + private async Task DebugSymbolsEnabledInConfiguredProjectForDebugAsync() + { + if (await GetPropertyFromDebugFrameworkAsync("DebugSymbols") is string debugSymbols) { - ConfiguredProject? configuredProjectForDebug = await GetConfiguredProjectForDebugAsync(); - if (configuredProjectForDebug is null) - { - return null; - } + return StringComparers.PropertyLiteralValues.Equals(debugSymbols, "true"); + } - Assumes.Present(configuredProjectForDebug.Services.ProjectPropertiesProvider); - IProjectProperties commonProperties = configuredProjectForDebug.Services.ProjectPropertiesProvider.GetCommonProperties(); - string propertyValue = await commonProperties.GetEvaluatedPropertyValueAsync(propertyName); + return false; + } - return propertyValue; + private async Task ConfiguredProjectForDebugHasHotReloadCapabilityAsync() + { + ConfiguredProject? configuredProjectForDebug = await GetConfiguredProjectForDebugAsync(); + if (configuredProjectForDebug is null) + { + return false; } - private void OnProcessExited(HotReloadState hotReloadState) + return configuredProjectForDebug.Capabilities.AppliesTo("SupportsHotReload"); + } + + private async Task GetPropertyFromDebugFrameworkAsync(string propertyName) + { + ConfiguredProject? configuredProjectForDebug = await GetConfiguredProjectForDebugAsync(); + if (configuredProjectForDebug is null) { - _projectFaultHandlerService.Forget(OnProcessExitedAsync(hotReloadState), _project); + return null; } - private async Task OnProcessExitedAsync(HotReloadState hotReloadState) - { - Assumes.NotNull(hotReloadState.Session); - Assumes.NotNull(hotReloadState.Process); + Assumes.Present(configuredProjectForDebug.Services.ProjectPropertiesProvider); + IProjectProperties commonProperties = configuredProjectForDebug.Services.ProjectPropertiesProvider.GetCommonProperties(); + string propertyValue = await commonProperties.GetEvaluatedPropertyValueAsync(propertyName); + + return propertyValue; + } - WriteOutputMessage( - new HotReloadLogMessage( - HotReloadVerbosity.Minimal, - VSResources.ProjectHotReloadSessionManager_ProcessExited, - hotReloadState.Session?.Name, - (_nextUniqueId - 1).ToString(), - HotReloadDiagnosticOutputService.GetProcessId(hotReloadState.Process), - HotReloadDiagnosticErrorLevel.Info - ), - default); + private void OnProcessExited(HotReloadState hotReloadState) + { + _projectFaultHandlerService.Forget(OnProcessExitedAsync(hotReloadState), _project); + } - await StopProjectAsync(hotReloadState, default); + private async Task OnProcessExitedAsync(HotReloadState hotReloadState) + { + Assumes.NotNull(hotReloadState.Session); + Assumes.NotNull(hotReloadState.Process); + + WriteOutputMessage( + new HotReloadLogMessage( + HotReloadVerbosity.Minimal, + VSResources.ProjectHotReloadSessionManager_ProcessExited, + hotReloadState.Session?.Name, + (_nextUniqueId - 1).ToString(), + HotReloadDiagnosticOutputService.GetProcessId(hotReloadState.Process), + HotReloadDiagnosticErrorLevel.Info + ), + default); + + await StopProjectAsync(hotReloadState, default); + + hotReloadState.Process.Exited -= hotReloadState.OnProcessExited; + } - hotReloadState.Process.Exited -= hotReloadState.OnProcessExited; - } + private static IDeltaApplier? GetDeltaApplier(HotReloadState hotReloadState) + { + return null; + } - private static IDeltaApplier? GetDeltaApplier(HotReloadState hotReloadState) + private async Task RestartProjectAsync( + HotReloadState hotReloadState, + bool isRunningUnderDebug, + CancellationToken cancellationToken) + { + Assumes.NotNull(_project.Services.HostObject); + await _projectThreadingService.SwitchToUIThread(); + + if (_vsSolutionBuildManager2 is null) { - return null; + _vsSolutionBuildManager2 = await _vsSolutionBuildManagerService.GetValueAsync(cancellationToken); } - private async Task RestartProjectAsync( - HotReloadState hotReloadState, - bool isRunningUnderDebug, - CancellationToken cancellationToken) - { - Assumes.NotNull(_project.Services.HostObject); - await _projectThreadingService.SwitchToUIThread(); + // Step 1: Debug or NonDebug? + uint dbgLaunchFlag = isRunningUnderDebug ? (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG : (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH; - if (_vsSolutionBuildManager2 is null) - { - _vsSolutionBuildManager2 = await _vsSolutionBuildManagerService.GetValueAsync(cancellationToken); - } + // Step 2: Build and Launch Debug + var projectVsHierarchy = (IVsHierarchy)_project.Services.HostObject; - // Step 1: Debug or NonDebug? - uint dbgLaunchFlag = isRunningUnderDebug ? (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG : (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH; + var result = _vsSolutionBuildManager2.StartSimpleUpdateProjectConfiguration( + pIVsHierarchyToBuild: projectVsHierarchy, + pIVsHierarchyDependent: null, + pszDependentConfigurationCanonicalName: null, + dwFlags: (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD | dbgLaunchFlag, + dwDefQueryResults: (uint)VSSOLNBUILDQUERYRESULTS.VSSBQR_SAVEBEFOREBUILD_QUERY_YES, + fSuppressUI: 0); - // Step 2: Build and Launch Debug - var projectVsHierarchy = (IVsHierarchy)_project.Services.HostObject; + ErrorHandler.ThrowOnFailure(result); - var result = _vsSolutionBuildManager2.StartSimpleUpdateProjectConfiguration( - pIVsHierarchyToBuild: projectVsHierarchy, - pIVsHierarchyDependent: null, - pszDependentConfigurationCanonicalName: null, - dwFlags: (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD | dbgLaunchFlag, - dwDefQueryResults: (uint)VSSOLNBUILDQUERYRESULTS.VSSBQR_SAVEBEFOREBUILD_QUERY_YES, - fSuppressUI: 0); + return result == HResult.OK; + } - ErrorHandler.ThrowOnFailure(result); + private static Task OnAfterChangesAppliedAsync(HotReloadState hotReloadState, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } - return result == HResult.OK; - } + private ValueTask StopProjectAsync(HotReloadState hotReloadState, CancellationToken cancellationToken) + { + return _semaphore.ExecuteAsync(StopProjectInternalAsync, cancellationToken); - private static Task OnAfterChangesAppliedAsync(HotReloadState hotReloadState, CancellationToken cancellationToken) + async ValueTask StopProjectInternalAsync() { - return Task.CompletedTask; - } + Assumes.NotNull(hotReloadState.Session); + Assumes.NotNull(hotReloadState.Process); - private ValueTask StopProjectAsync(HotReloadState hotReloadState, CancellationToken cancellationToken) - { - return _semaphore.ExecuteAsync(StopProjectInternalAsync, cancellationToken); + int sessionCountOnEntry = _activeSessions.Count; - async ValueTask StopProjectInternalAsync() + try { - Assumes.NotNull(hotReloadState.Session); - Assumes.NotNull(hotReloadState.Process); - - int sessionCountOnEntry = _activeSessions.Count; - - try + if (_activeSessions.Remove(hotReloadState.Process.Id)) { - if (_activeSessions.Remove(hotReloadState.Process.Id)) - { - await hotReloadState.Session.StopSessionAsync(cancellationToken); + await hotReloadState.Session.StopSessionAsync(cancellationToken); - if (!hotReloadState.Process.HasExited) + if (!hotReloadState.Process.HasExited) + { + // First try to close the process nicely and if that doesn't work kill it. + if (!hotReloadState.Process.CloseMainWindow()) { - // First try to close the process nicely and if that doesn't work kill it. - if (!hotReloadState.Process.CloseMainWindow()) - { - hotReloadState.Process.Kill(); - } + hotReloadState.Process.Kill(); } } } - catch (Exception ex) - { - WriteOutputMessage( - new HotReloadLogMessage( - HotReloadVerbosity.Minimal, - string.Format(VSResources.ProjectHotReloadSessionManager_ErrorStoppingTheSession, ex.GetType(), ex.Message), - hotReloadState.Session?.Name, - null, - HotReloadDiagnosticOutputService.GetProcessId(hotReloadState.Process), - HotReloadDiagnosticErrorLevel.Error - ), - cancellationToken); - } - finally + } + catch (Exception ex) + { + WriteOutputMessage( + new HotReloadLogMessage( + HotReloadVerbosity.Minimal, + string.Format(VSResources.ProjectHotReloadSessionManager_ErrorStoppingTheSession, ex.GetType(), ex.Message), + hotReloadState.Session?.Name, + null, + HotReloadDiagnosticOutputService.GetProcessId(hotReloadState.Process), + HotReloadDiagnosticErrorLevel.Error + ), + cancellationToken); + } + finally + { + // No more sessions removes the project from hot reload mode + if (sessionCountOnEntry == 1 && _activeSessions.Count == 0) { - // No more sessions removes the project from hot reload mode - if (sessionCountOnEntry == 1 && _activeSessions.Count == 0) - { - await _projectHotReloadNotificationService.Value.SetHotReloadStateAsync(isInHotReload: false); - } + await _projectHotReloadNotificationService.Value.SetHotReloadStateAsync(isInHotReload: false); } - - return true; } + + return true; } + } - /// - public Task ApplyHotReloadUpdateAsync(Func applyFunction, CancellationToken cancellationToken) + /// + public Task ApplyHotReloadUpdateAsync(Func applyFunction, CancellationToken cancellationToken) + { + return _semaphore.ExecuteAsync(ApplyHotReloadUpdateInternalAsync, cancellationToken); + + async Task ApplyHotReloadUpdateInternalAsync() { - return _semaphore.ExecuteAsync(ApplyHotReloadUpdateInternalAsync, cancellationToken); + // Run the updates in parallel + List? updateTasks = null; - async Task ApplyHotReloadUpdateInternalAsync() + foreach (HotReloadState sessionState in _activeSessions.Values) { - // Run the updates in parallel - List? updateTasks = null; + cancellationToken.ThrowIfCancellationRequested(); - foreach (HotReloadState sessionState in _activeSessions.Values) + if (sessionState.Session is IProjectHotReloadSessionInternal sessionInternal) { - cancellationToken.ThrowIfCancellationRequested(); - - if (sessionState.Session is IProjectHotReloadSessionInternal sessionInternal) + IDeltaApplier? deltaApplier = sessionInternal.DeltaApplier; + if (deltaApplier is not null) { - IDeltaApplier? deltaApplier = sessionInternal.DeltaApplier; - if (deltaApplier is not null) - { - updateTasks ??= []; - updateTasks.Add(applyFunction(deltaApplier, cancellationToken)); - } + updateTasks ??= []; + updateTasks.Add(applyFunction(deltaApplier, cancellationToken)); } } + } - // Wait for their completion - if (updateTasks is not null) - { - await Task.WhenAll(updateTasks); - } + // Wait for their completion + if (updateTasks is not null) + { + await Task.WhenAll(updateTasks); } } + } - private class HotReloadState : IProjectHotReloadSessionCallback2 - { - private readonly ProjectHotReloadSessionManager _sessionManager; + private class HotReloadState : IProjectHotReloadSessionCallback2 + { + private readonly ProjectHotReloadSessionManager _sessionManager; - public Process? Process { get; set; } - public IProjectHotReloadSession? Session { get; set; } + public Process? Process { get; set; } + public IProjectHotReloadSession? Session { get; set; } - public bool SupportsRestart => true; + public bool SupportsRestart => true; - public UnconfiguredProject? Project => _sessionManager._project; + public UnconfiguredProject? Project => _sessionManager._project; - public HotReloadState(ProjectHotReloadSessionManager sessionManager) - { - _sessionManager = sessionManager; - } + public HotReloadState(ProjectHotReloadSessionManager sessionManager) + { + _sessionManager = sessionManager; + } - internal void OnProcessExited(object sender, EventArgs e) - { - _sessionManager.OnProcessExited(this); - } + internal void OnProcessExited(object sender, EventArgs e) + { + _sessionManager.OnProcessExited(this); + } - public Task OnAfterChangesAppliedAsync(CancellationToken cancellationToken) - { - return ProjectHotReloadSessionManager.OnAfterChangesAppliedAsync(this, cancellationToken); - } + public Task OnAfterChangesAppliedAsync(CancellationToken cancellationToken) + { + return ProjectHotReloadSessionManager.OnAfterChangesAppliedAsync(this, cancellationToken); + } - public Task StopProjectAsync(CancellationToken cancellationToken) - { - return _sessionManager.StopProjectAsync(this, cancellationToken).AsTask(); - } + public Task StopProjectAsync(CancellationToken cancellationToken) + { + return _sessionManager.StopProjectAsync(this, cancellationToken).AsTask(); + } - public Task RestartProjectAsync(CancellationToken cancellationToken) - { - return TaskResult.False; - } + public Task RestartProjectAsync(CancellationToken cancellationToken) + { + return TaskResult.False; + } - public Task RestartProjectAsync(bool isRunningUnderDebug, CancellationToken cancellationToken) - { - return _sessionManager.RestartProjectAsync(this, isRunningUnderDebug, cancellationToken); - } + public Task RestartProjectAsync(bool isRunningUnderDebug, CancellationToken cancellationToken) + { + return _sessionManager.RestartProjectAsync(this, isRunningUnderDebug, cancellationToken); + } - public IDeltaApplier? GetDeltaApplier() - { - return ProjectHotReloadSessionManager.GetDeltaApplier(this); - } + public IDeltaApplier? GetDeltaApplier() + { + return ProjectHotReloadSessionManager.GetDeltaApplier(this); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IEnvironmentOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IEnvironmentOptions.cs index cd6161c553..a19a149bf2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IEnvironmentOptions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IEnvironmentOptions.cs @@ -1,46 +1,45 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides a method for retrieving options from the host environment. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IEnvironmentOptions { /// - /// Provides a method for retrieving options from the host environment. + /// Returns the value associated with the specified category, page and option, if it exists, + /// otherwise, returns . /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IEnvironmentOptions - { - /// - /// Returns the value associated with the specified category, page and option, if it exists, - /// otherwise, returns . - /// - /// - /// A containing the category of the option to return. - /// - /// - /// A containing the page of the option to return. - /// - /// - /// A containing the name of the option to return. - /// - /// - /// The value to return if the value does not exist. - /// - T GetOption(string category, string page, string option, T defaultValue); + /// + /// A containing the category of the option to return. + /// + /// + /// A containing the page of the option to return. + /// + /// + /// A containing the name of the option to return. + /// + /// + /// The value to return if the value does not exist. + /// + T GetOption(string category, string page, string option, T defaultValue); - /// - /// Sets a value associated with the specified category, page and option, if it exists, - /// - /// - /// A containing the category of the option to set. - /// - /// - /// A containing the page of the option to set. - /// - /// - /// A containing the name of the option to set. - /// - /// - /// The value to set the property. - /// - void SetOption(string category, string page, string option, T newValue); - } + /// + /// Sets a value associated with the specified category, page and option, if it exists, + /// + /// + /// A containing the category of the option to set. + /// + /// + /// A containing the page of the option to set. + /// + /// + /// A containing the name of the option to set. + /// + /// + /// The value to set the property. + /// + void SetOption(string category, string page, string option, T newValue); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IPackageService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IPackageService.cs index 6fb9d76825..659c22b98a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IPackageService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IPackageService.cs @@ -2,24 +2,23 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// A service that is initialized when the VS package is initialized. +/// +/// +/// Implementations must be exported in either global or project service scopes. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore, ContractName = ExportContractNames.Scopes.ProjectService)] +internal interface IPackageService { /// - /// A service that is initialized when the VS package is initialized. + /// Called when the package is initializing. /// /// - /// Implementations must be exported in either global or project service scopes. + /// Always called on the UI thread. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore, ContractName = ExportContractNames.Scopes.ProjectService)] - internal interface IPackageService - { - /// - /// Called when the package is initializing. - /// - /// - /// Always called on the UI thread. - /// - Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider); - } + Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IProjectServiceExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IProjectServiceExtensions.cs index 6c6b744691..e540d60d5a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IProjectServiceExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IProjectServiceExtensions.cs @@ -3,50 +3,49 @@ using Microsoft.VisualStudio.Shell.Interop; using static Microsoft.VisualStudio.VSConstants; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Extension methods for . +/// +internal static class IProjectServiceExtensions { /// - /// Extension methods for . + /// Obtains the for , + /// optionally testing whether it has a capability match to . /// - internal static class IProjectServiceExtensions + /// The project service to invoke this method on. + /// The VS hierarchy object, which must represent a CPS project. + /// An optional project capability match expression to filter out non-matching projects. + /// + /// The CPS for the given hierarchy node, matching any (optional) + /// capability expression, or if no suitable project exists. + /// + public static UnconfiguredProject? GetUnconfiguredProject(this IProjectService projectService, IVsHierarchy vsHierarchy, string? appliesToExpression = null) { - /// - /// Obtains the for , - /// optionally testing whether it has a capability match to . - /// - /// The project service to invoke this method on. - /// The VS hierarchy object, which must represent a CPS project. - /// An optional project capability match expression to filter out non-matching projects. - /// - /// The CPS for the given hierarchy node, matching any (optional) - /// capability expression, or if no suitable project exists. - /// - public static UnconfiguredProject? GetUnconfiguredProject(this IProjectService projectService, IVsHierarchy vsHierarchy, string? appliesToExpression = null) + // We need IProjectService2.GetLoadedProject. + if (projectService is IProjectService2 projectService2) { - // We need IProjectService2.GetLoadedProject. - if (projectService is IProjectService2 projectService2) + vsHierarchy.GetCanonicalName((uint)VSITEMID.Root, out string? projectFilePath); + + // Ensure we have a path. + if (!Strings.IsNullOrEmpty(projectFilePath)) { - vsHierarchy.GetCanonicalName((uint)VSITEMID.Root, out string? projectFilePath); + UnconfiguredProject? unconfiguredProject = projectService2.GetLoadedProject(projectFilePath); - // Ensure we have a path. - if (!Strings.IsNullOrEmpty(projectFilePath)) + // Ensure CPS knows a project having that path. + if (unconfiguredProject is not null) { - UnconfiguredProject? unconfiguredProject = projectService2.GetLoadedProject(projectFilePath); - - // Ensure CPS knows a project having that path. - if (unconfiguredProject is not null) + // If capabilites were requested, match them. + if (appliesToExpression is null || unconfiguredProject.Capabilities.AppliesTo(appliesToExpression)) { - // If capabilites were requested, match them. - if (appliesToExpression is null || unconfiguredProject.Capabilities.AppliesTo(appliesToExpression)) - { - // Found a matching project. - return unconfiguredProject; - } + // Found a matching project. + return unconfiguredProject; } } } - - return null; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IRoslynServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IRoslynServices.cs index bccf56ca24..29f70f7929 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IRoslynServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IRoslynServices.cs @@ -2,13 +2,12 @@ using Microsoft.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IRoslynServices { - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IRoslynServices - { - bool ApplyChangesToSolution(Workspace ws, Solution renamedSolution); + bool ApplyChangesToSolution(Workspace ws, Solution renamedSolution); - bool IsValidIdentifier(string identifierName); - } + bool IsValidIdentifier(string identifierName); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IServiceProviderExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IServiceProviderExtensions.cs index ec13020841..6b34a25b17 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IServiceProviderExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IServiceProviderExtensions.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IServiceProviderExtensions { - internal static class IServiceProviderExtensions + /// + /// Returns the specified interface from the service. This is useful when the service and interface differ + /// + public static InterfaceType GetService(this IServiceProvider sp) + where InterfaceType : class + where ServiceType : class { - /// - /// Returns the specified interface from the service. This is useful when the service and interface differ - /// - public static InterfaceType GetService(this IServiceProvider sp) - where InterfaceType : class - where ServiceType : class - { #pragma warning disable RS0030 // Do not used banned APIs - return (InterfaceType)sp.GetService(typeof(ServiceType)); + return (InterfaceType)sp.GetService(typeof(ServiceType)); #pragma warning restore RS0030 // Do not used banned APIs - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUnconfiguredProjectVsServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUnconfiguredProjectVsServices.cs index c39be89fe4..9a74ef0fc2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUnconfiguredProjectVsServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUnconfiguredProjectVsServices.cs @@ -2,27 +2,26 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides access to common Visual Studio project services provided by the . +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IUnconfiguredProjectVsServices : IUnconfiguredProjectCommonServices { /// - /// Provides access to common Visual Studio project services provided by the . + /// Gets provided by the . /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IUnconfiguredProjectVsServices : IUnconfiguredProjectCommonServices - { - /// - /// Gets provided by the . - /// - IVsHierarchy VsHierarchy { get; } + IVsHierarchy VsHierarchy { get; } - /// - /// Gets provided by the . - /// - IVsProject4 VsProject { get; } + /// + /// Gets provided by the . + /// + IVsProject4 VsProject { get; } - /// - /// Gets provided by the . - /// - IPhysicalProjectTree ProjectTree { get; } - } + /// + /// Gets provided by the . + /// + IPhysicalProjectTree ProjectTree { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUserNotificationServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUserNotificationServices.cs index 8781b8174d..59ea94672f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUserNotificationServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IUserNotificationServices.cs @@ -2,48 +2,47 @@ using System.Runtime.InteropServices; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IUserNotificationServices { - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IUserNotificationServices - { - /// - /// Shows a Yes/No confirmation box to the user. - /// - /// - /// if the user clicked the Yes button, otherwise; - /// if the user clicked the No button. - /// - /// - /// This method was not accessed from the UI thread. - /// - bool Confirm(string message); + /// + /// Shows a Yes/No confirmation box to the user. + /// + /// + /// if the user clicked the Yes button, otherwise; + /// if the user clicked the No button. + /// + /// + /// This method was not accessed from the UI thread. + /// + bool Confirm(string message); - /// - /// Shows a warning to the user. - /// - /// - /// This method was not accessed from the UI thread. - /// - void ShowWarning(string warning); + /// + /// Shows a warning to the user. + /// + /// + /// This method was not accessed from the UI thread. + /// + void ShowWarning(string warning); - /// - /// Shows a error to the user. - /// - /// - /// This method was not accessed from the UI thread. - /// - void ShowError(string error); + /// + /// Shows a error to the user. + /// + /// + /// This method was not accessed from the UI thread. + /// + void ShowError(string error); - /// - /// Shows a Yes/No confirmation with Don't Show Again checkbox - /// - /// The message related to the Yes/No question - /// checkbox selection - /// - /// if the user clicked the Yes button, otherwise; - /// if the user clicked the No button. - /// - bool Confirm(string message, out bool disablePromptMessage); - } + /// + /// Shows a Yes/No confirmation with Don't Show Again checkbox + /// + /// The message related to the Yes/No question + /// checkbox selection + /// + /// if the user clicked the Yes button, otherwise; + /// if the user clicked the No button. + /// + bool Confirm(string message, out bool disablePromptMessage); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsOnlineServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsOnlineServices.cs index 5e1de29cf4..3bbb341dc9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsOnlineServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsOnlineServices.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IVsOnlineServices { - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IVsOnlineServices - { - /// - /// Indicates whether or not the client is connected to VS Online. - /// - bool ConnectedToVSOnline { get; } - } + /// + /// Indicates whether or not the client is connected to VS Online. + /// + bool ConnectedToVSOnline { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`1.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`1.cs index f6a5aa2cef..cbe13f2195 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`1.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`1.cs @@ -2,31 +2,30 @@ using System.Runtime.InteropServices; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides access to a Visual Studio proffered service that must be used on the UI thread. +/// +/// +/// The type of the service to retrieve and return from . +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IVsUIService + where T : class? { /// - /// Provides access to a Visual Studio proffered service that must be used on the UI thread. + /// Gets the service object associated with . /// - /// - /// The type of the service to retrieve and return from . - /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IVsUIService - where T : class? - { - /// - /// Gets the service object associated with . - /// - /// - /// Must be called from the UI thread. - /// - /// - /// This property was not accessed from the UI thread. - /// - /// - /// The service associated with ; - /// otherwise, if it is not present. - /// - T Value { get; } - } + /// + /// Must be called from the UI thread. + /// + /// + /// This property was not accessed from the UI thread. + /// + /// + /// The service associated with ; + /// otherwise, if it is not present. + /// + T Value { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`2.cs index cdf400155d..92dacfcb66 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/IVsUIService`2.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides access to a Visual Studio proffered service that must be used on the UI thread. +/// +/// +/// The type of the service to retrieve. +/// +/// +/// The type of the service to return from +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IVsUIService : IVsUIService + where TService : class + where TInterface : class? { - /// - /// Provides access to a Visual Studio proffered service that must be used on the UI thread. - /// - /// - /// The type of the service to retrieve. - /// - /// - /// The type of the service to return from - /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IVsUIService : IVsUIService - where TService : class - where TInterface : class? - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandler.cs index 9a85fcbc37..2e7353171b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandler.cs @@ -5,91 +5,90 @@ using Microsoft.VisualStudio.ProjectSystem.VS.UI; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands -{ - /// - /// Provides support for all Add Item commands that operate on nodes, across C# and VB. - /// - internal abstract class AbstractAddItemCommandHandler : IAsyncCommandGroupHandler - { - internal sealed record class TemplateDetails( - string AppliesTo, - Guid DirNamePackageGuid, - uint DirNameResourceId, - Guid TemplateNamePackageGuid, - uint TemplateNameResourceId); +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; - protected static readonly Guid LegacyCSharpPackageGuid = new("{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}"); - protected static readonly Guid LegacyVBPackageGuid = new("{164B10B9-B200-11d0-8C61-00A0C91E29D5}"); +/// +/// Provides support for all Add Item commands that operate on nodes, across C# and VB. +/// +internal abstract class AbstractAddItemCommandHandler : IAsyncCommandGroupHandler +{ + internal sealed record class TemplateDetails( + string AppliesTo, + Guid DirNamePackageGuid, + uint DirNameResourceId, + Guid TemplateNamePackageGuid, + uint TemplateNameResourceId); - private readonly ConfiguredProject _configuredProject; - private readonly IAddItemDialogService _addItemDialogService; - private readonly IVsUIService _vsShell; + protected static readonly Guid LegacyCSharpPackageGuid = new("{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}"); + protected static readonly Guid LegacyVBPackageGuid = new("{164B10B9-B200-11d0-8C61-00A0C91E29D5}"); - protected AbstractAddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) - { - _configuredProject = configuredProject; - _addItemDialogService = addItemDialogService; - _vsShell = vsShell; - } + private readonly ConfiguredProject _configuredProject; + private readonly IAddItemDialogService _addItemDialogService; + private readonly IVsUIService _vsShell; - /// - /// Gets the list of potential templates that could apply to this handler, keyed by command ID. - /// - protected abstract ImmutableDictionary> TemplatesByCommandId { get; } + protected AbstractAddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) + { + _configuredProject = configuredProject; + _addItemDialogService = addItemDialogService; + _vsShell = vsShell; + } - public Task GetCommandStatusAsync(IImmutableSet nodes, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) - { - Requires.NotNull(nodes); + /// + /// Gets the list of potential templates that could apply to this handler, keyed by command ID. + /// + protected abstract ImmutableDictionary> TemplatesByCommandId { get; } - if (nodes.Count == 1 && TryGetTemplateDetails(commandId, out _) && _addItemDialogService.CanAddNewOrExistingItemTo(nodes.First())) - { - return GetCommandStatusResult.Handled(commandText, CommandStatus.Enabled); - } + public Task GetCommandStatusAsync(IImmutableSet nodes, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + { + Requires.NotNull(nodes); - return GetCommandStatusResult.Unhandled; + if (nodes.Count == 1 && TryGetTemplateDetails(commandId, out _) && _addItemDialogService.CanAddNewOrExistingItemTo(nodes.First())) + { + return GetCommandStatusResult.Handled(commandText, CommandStatus.Enabled); } - public async Task TryHandleCommandAsync(IImmutableSet nodes, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - Requires.NotNull(nodes); + return GetCommandStatusResult.Unhandled; + } - if (nodes.Count == 1 && TryGetTemplateDetails(commandId, out TemplateDetails? result) && _addItemDialogService.CanAddNewOrExistingItemTo(nodes.First())) - { - IVsShell vsShell = _vsShell.Value; + public async Task TryHandleCommandAsync(IImmutableSet nodes, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + Requires.NotNull(nodes); - // Look up the resources from each package to get the strings to pass to the Add Item dialog. - // These strings must match what is used in the template exactly, including localized versions. Rather than relying on - // our localizations being the same as the VS repository localizations we just load the right strings using the same - // resource IDs as the templates themselves use. - string localizedDirectoryName = vsShell.LoadPackageString(result.DirNamePackageGuid, result.DirNameResourceId); - string localizedTemplateName = vsShell.LoadPackageString(result.TemplateNamePackageGuid, result.TemplateNameResourceId); + if (nodes.Count == 1 && TryGetTemplateDetails(commandId, out TemplateDetails? result) && _addItemDialogService.CanAddNewOrExistingItemTo(nodes.First())) + { + IVsShell vsShell = _vsShell.Value; - await _addItemDialogService.ShowAddNewItemDialogAsync(nodes.First(), localizedDirectoryName, localizedTemplateName); - return true; - } + // Look up the resources from each package to get the strings to pass to the Add Item dialog. + // These strings must match what is used in the template exactly, including localized versions. Rather than relying on + // our localizations being the same as the VS repository localizations we just load the right strings using the same + // resource IDs as the templates themselves use. + string localizedDirectoryName = vsShell.LoadPackageString(result.DirNamePackageGuid, result.DirNameResourceId); + string localizedTemplateName = vsShell.LoadPackageString(result.TemplateNamePackageGuid, result.TemplateNameResourceId); - return false; + await _addItemDialogService.ShowAddNewItemDialogAsync(nodes.First(), localizedDirectoryName, localizedTemplateName); + return true; } - private bool TryGetTemplateDetails(long commandId, [NotNullWhen(returnValue: true)] out TemplateDetails? result) + return false; + } + + private bool TryGetTemplateDetails(long commandId, [NotNullWhen(returnValue: true)] out TemplateDetails? result) + { + if (TemplatesByCommandId.TryGetValue(commandId, out ImmutableArray templates)) { - if (TemplatesByCommandId.TryGetValue(commandId, out ImmutableArray templates)) - { - IProjectCapabilitiesScope capabilities = _configuredProject.Capabilities; + IProjectCapabilitiesScope capabilities = _configuredProject.Capabilities; - foreach (TemplateDetails template in templates) + foreach (TemplateDetails template in templates) + { + if (capabilities.AppliesTo(template.AppliesTo)) { - if (capabilities.AppliesTo(template.AppliesTo)) - { - result = template; - return true; - } + result = template; + return true; } } - - result = null; - return false; } + + result = null; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs index 35e8eb4080..2f6fa662b5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommand.cs @@ -5,168 +5,167 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Build; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +internal abstract class AbstractGenerateNuGetPackageCommand : AbstractSingleNodeProjectCommand, IVsUpdateSolutionEvents, IDisposable { - internal abstract class AbstractGenerateNuGetPackageCommand : AbstractSingleNodeProjectCommand, IVsUpdateSolutionEvents, IDisposable + private readonly IProjectThreadingService _threadingService; + private readonly ISolutionBuildManager _solutionBuildManager; + private readonly GeneratePackageOnBuildPropertyProvider _generatePackageOnBuildPropertyProvider; + + private IAsyncDisposable? _subscription; + + protected AbstractGenerateNuGetPackageCommand( + UnconfiguredProject project, + IProjectThreadingService threadingService, + ISolutionBuildManager vsSolutionBuildManagerService, + GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) { - private readonly IProjectThreadingService _threadingService; - private readonly ISolutionBuildManager _solutionBuildManager; - private readonly GeneratePackageOnBuildPropertyProvider _generatePackageOnBuildPropertyProvider; + Requires.NotNull(project); + Requires.NotNull(threadingService); + Requires.NotNull(vsSolutionBuildManagerService); + Requires.NotNull(generatePackageOnBuildPropertyProvider); + + Project = project; + _threadingService = threadingService; + _solutionBuildManager = vsSolutionBuildManagerService; + _generatePackageOnBuildPropertyProvider = generatePackageOnBuildPropertyProvider; + } - private IAsyncDisposable? _subscription; + protected UnconfiguredProject Project { get; } - protected AbstractGenerateNuGetPackageCommand( - UnconfiguredProject project, - IProjectThreadingService threadingService, - ISolutionBuildManager vsSolutionBuildManagerService, - GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) + protected abstract string GetCommandText(); + + protected abstract bool ShouldHandle(IProjectTree node); + + protected override async Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + { + if (ShouldHandle(node)) { - Requires.NotNull(project); - Requires.NotNull(threadingService); - Requires.NotNull(vsSolutionBuildManagerService); - Requires.NotNull(generatePackageOnBuildPropertyProvider); - - Project = project; - _threadingService = threadingService; - _solutionBuildManager = vsSolutionBuildManagerService; - _generatePackageOnBuildPropertyProvider = generatePackageOnBuildPropertyProvider; + // Enable the command if the build manager is ready to build. + CommandStatus commandStatus = await IsReadyToBuildAsync() ? CommandStatus.Enabled : CommandStatus.Supported; + return await GetCommandStatusResult.Handled(GetCommandText(), commandStatus); } - protected UnconfiguredProject Project { get; } + return CommandStatusResult.Unhandled; + } - protected abstract string GetCommandText(); + private async Task IsReadyToBuildAsync() + { + // Switch to UI thread for querying the build manager service. + await _threadingService.SwitchToUIThread(); - protected abstract bool ShouldHandle(IProjectTree node); + // Ensure build manager is initialized. + _subscription ??= await _solutionBuildManager.SubscribeSolutionEventsAsync(this); - protected override async Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) - { - if (ShouldHandle(node)) - { - // Enable the command if the build manager is ready to build. - CommandStatus commandStatus = await IsReadyToBuildAsync() ? CommandStatus.Enabled : CommandStatus.Supported; - return await GetCommandStatusResult.Handled(GetCommandText(), commandStatus); - } + int busy = _solutionBuildManager.QueryBuildManagerBusy(); + return busy == 0; + } - return CommandStatusResult.Unhandled; + protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + if (!ShouldHandle(node)) + { + return false; } - private async Task IsReadyToBuildAsync() + if (await IsReadyToBuildAsync()) { - // Switch to UI thread for querying the build manager service. + // Build manager APIs require UI thread access. await _threadingService.SwitchToUIThread(); - // Ensure build manager is initialized. - _subscription ??= await _solutionBuildManager.SubscribeSolutionEventsAsync(this); + Assumes.NotNull(Project.Services.HostObject); - int busy = _solutionBuildManager.QueryBuildManagerBusy(); - return busy == 0; - } + // Save documents before build. + var projectVsHierarchy = (IVsHierarchy)Project.Services.HostObject; + _solutionBuildManager.SaveDocumentsBeforeBuild(projectVsHierarchy, (uint)VSConstants.VSITEMID.Root, docCookie: 0); - protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - if (!ShouldHandle(node)) - { - return false; - } + // We need to make sure dependencies are built so they can go into the package + _solutionBuildManager.CalculateProjectDependencies(); - if (await IsReadyToBuildAsync()) + // Assembly our list of projects to build + var projects = new List { - // Build manager APIs require UI thread access. - await _threadingService.SwitchToUIThread(); - - Assumes.NotNull(Project.Services.HostObject); + projectVsHierarchy + }; - // Save documents before build. - var projectVsHierarchy = (IVsHierarchy)Project.Services.HostObject; - _solutionBuildManager.SaveDocumentsBeforeBuild(projectVsHierarchy, (uint)VSConstants.VSITEMID.Root, docCookie: 0); + projects.AddRange(_solutionBuildManager.GetProjectDependencies(projectVsHierarchy)); - // We need to make sure dependencies are built so they can go into the package - _solutionBuildManager.CalculateProjectDependencies(); - - // Assembly our list of projects to build - var projects = new List - { - projectVsHierarchy - }; + // Turn off "GeneratePackageOnBuild" because otherwise the Pack target will not do a build, even if there is no built output + _generatePackageOnBuildPropertyProvider.OverrideGeneratePackageOnBuild(false); - projects.AddRange(_solutionBuildManager.GetProjectDependencies(projectVsHierarchy)); + uint dwFlags = (uint)(VSSOLNBUILDUPDATEFLAGS.SBF_SUPPRESS_SAVEBEFOREBUILD_QUERY | VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD); - // Turn off "GeneratePackageOnBuild" because otherwise the Pack target will not do a build, even if there is no built output - _generatePackageOnBuildPropertyProvider.OverrideGeneratePackageOnBuild(false); + uint[] buildFlags = new uint[projects.Count]; + // We tell the Solution Build Manager to Package our project, which will call the Pack target, which will build if necessary. + // Any dependent projects will just do a normal build + buildFlags[0] = VSConstants.VS_BUILDABLEPROJECTCFGOPTS_PACKAGE; - uint dwFlags = (uint)(VSSOLNBUILDUPDATEFLAGS.SBF_SUPPRESS_SAVEBEFOREBUILD_QUERY | VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD); - - uint[] buildFlags = new uint[projects.Count]; - // We tell the Solution Build Manager to Package our project, which will call the Pack target, which will build if necessary. - // Any dependent projects will just do a normal build - buildFlags[0] = VSConstants.VS_BUILDABLEPROJECTCFGOPTS_PACKAGE; - - _solutionBuildManager.StartUpdateSpecificProjectConfigurations(projects.ToArray(), buildFlags, dwFlags); - } - - return true; + _solutionBuildManager.StartUpdateSpecificProjectConfigurations(projects.ToArray(), buildFlags, dwFlags); } - #region IVsUpdateSolutionEvents members - public int UpdateSolution_Begin(ref int pfCancelUpdate) - { - return HResult.OK; - } + return true; + } - public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) - { - _generatePackageOnBuildPropertyProvider.OverrideGeneratePackageOnBuild(null); - return HResult.OK; - } + #region IVsUpdateSolutionEvents members + public int UpdateSolution_Begin(ref int pfCancelUpdate) + { + return HResult.OK; + } - public int UpdateSolution_Cancel() - { - _generatePackageOnBuildPropertyProvider.OverrideGeneratePackageOnBuild(null); - return HResult.OK; - } + public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) + { + _generatePackageOnBuildPropertyProvider.OverrideGeneratePackageOnBuild(null); + return HResult.OK; + } - public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) - { - return HResult.OK; - } + public int UpdateSolution_Cancel() + { + _generatePackageOnBuildPropertyProvider.OverrideGeneratePackageOnBuild(null); + return HResult.OK; + } - public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) - { - return HResult.OK; - } - #endregion + public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) + { + return HResult.OK; + } + + public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) + { + return HResult.OK; + } + #endregion - #region IDisposable - private bool _disposedValue; + #region IDisposable + private bool _disposedValue; - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) { - if (!_disposedValue) + if (disposing && _subscription is not null) { - if (disposing && _subscription is not null) + // Build manager APIs require UI thread access. + _threadingService.ExecuteSynchronously(async () => { - // Build manager APIs require UI thread access. - _threadingService.ExecuteSynchronously(async () => - { - await _threadingService.SwitchToUIThread(); + await _threadingService.SwitchToUIThread(); - if (_subscription is not null) - { - // Unregister solution build events. - await _subscription.DisposeAsync(); - } - }); - } - - _disposedValue = true; + if (_subscription is not null) + { + // Unregister solution build events. + await _subscription.DisposeAsync(); + } + }); } - } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + _disposedValue = true; } - #endregion } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommand.cs index a94653b449..ca4961f7c0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommand.cs @@ -3,39 +3,38 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +internal abstract class AbstractOpenProjectDesignerCommand : AbstractSingleNodeProjectCommand { - internal abstract class AbstractOpenProjectDesignerCommand : AbstractSingleNodeProjectCommand - { - private readonly IProjectDesignerService _designerService; + private readonly IProjectDesignerService _designerService; - protected AbstractOpenProjectDesignerCommand(IProjectDesignerService designerService) - { - Requires.NotNull(designerService); + protected AbstractOpenProjectDesignerCommand(IProjectDesignerService designerService) + { + Requires.NotNull(designerService); - _designerService = designerService; - } + _designerService = designerService; + } - protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + { + // We assume that if the AppDesignerTreeModifier marked an AppDesignerFolder, that we must support the Project Designer + if (node.Flags.Contains(ProjectTreeFlags.Common.AppDesignerFolder)) { - // We assume that if the AppDesignerTreeModifier marked an AppDesignerFolder, that we must support the Project Designer - if (node.Flags.Contains(ProjectTreeFlags.Common.AppDesignerFolder)) - { - return GetCommandStatusResult.Handled(commandText, CommandStatus.Enabled); - } - - return GetCommandStatusResult.Unhandled; + return GetCommandStatusResult.Handled(commandText, CommandStatus.Enabled); } - protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - if (node.Flags.Contains(ProjectTreeFlags.Common.AppDesignerFolder)) - { - await _designerService.ShowProjectDesignerAsync(); - return true; - } + return GetCommandStatusResult.Unhandled; + } - return false; + protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + if (node.Flags.Contains(ProjectTreeFlags.Common.AppDesignerFolder)) + { + await _designerService.ShowProjectDesignerAsync(); + return true; } + + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommand.cs index 4eccc40925..c53509c970 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommand.cs @@ -5,123 +5,122 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Handles populating a menu command on the debug dropdown when the menu reflects the IEnumValues for +/// a debug property. It shows the active framework used for running the app (F5/Ctrl+F5). +/// +[Export(PackageCommandRegistrationService.PackageCommandContract, typeof(MenuCommand))] +internal class DebugFrameworksDynamicMenuCommand : DynamicMenuCommand { + private const int MaxFrameworks = 20; + private readonly IStartupProjectHelper _startupProjectHelper; + private readonly JoinableTaskContext _joinableTaskContext; + + [ImportingConstructor] + public DebugFrameworksDynamicMenuCommand(IStartupProjectHelper startupProjectHelper, JoinableTaskContext joinableTaskContext) + : base(new CommandID(new Guid(CommandGroup.ManagedProjectSystem), ManagedProjectSystemCommandId.DebugFrameworks), MaxFrameworks) + { + _startupProjectHelper = startupProjectHelper; + _joinableTaskContext = joinableTaskContext; + } + /// - /// Handles populating a menu command on the debug dropdown when the menu reflects the IEnumValues for - /// a debug property. It shows the active framework used for running the app (F5/Ctrl+F5). + /// Called by the base when one if our menu ids is clicked. Need to return true if the command was handled /// - [Export(PackageCommandRegistrationService.PackageCommandContract, typeof(MenuCommand))] - internal class DebugFrameworksDynamicMenuCommand : DynamicMenuCommand + public override bool ExecCommand(int cmdIndex, EventArgs e) { - private const int MaxFrameworks = 20; - private readonly IStartupProjectHelper _startupProjectHelper; - private readonly JoinableTaskContext _joinableTaskContext; - - [ImportingConstructor] - public DebugFrameworksDynamicMenuCommand(IStartupProjectHelper startupProjectHelper, JoinableTaskContext joinableTaskContext) - : base(new CommandID(new Guid(CommandGroup.ManagedProjectSystem), ManagedProjectSystemCommandId.DebugFrameworks), MaxFrameworks) + bool handled = false; + ImmutableArray activeDebugFrameworks = _startupProjectHelper.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles); + if (!activeDebugFrameworks.IsEmpty) { - _startupProjectHelper = startupProjectHelper; - _joinableTaskContext = joinableTaskContext; - } - - /// - /// Called by the base when one if our menu ids is clicked. Need to return true if the command was handled - /// - public override bool ExecCommand(int cmdIndex, EventArgs e) - { - bool handled = false; - ImmutableArray activeDebugFrameworks = _startupProjectHelper.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles); - if (!activeDebugFrameworks.IsEmpty) + ExecuteSynchronously(async () => { - ExecuteSynchronously(async () => + foreach (IActiveDebugFrameworkServices activeDebugFramework in activeDebugFrameworks) { - foreach (IActiveDebugFrameworkServices activeDebugFramework in activeDebugFrameworks) + List? frameworks = await activeDebugFramework.GetProjectFrameworksAsync(); + if (frameworks is not null && cmdIndex >= 0 && cmdIndex < frameworks.Count) { - List? frameworks = await activeDebugFramework.GetProjectFrameworksAsync(); - if (frameworks is not null && cmdIndex >= 0 && cmdIndex < frameworks.Count) - { - await activeDebugFramework.SetActiveDebuggingFrameworkPropertyAsync(frameworks[cmdIndex]); - handled = true; - } + await activeDebugFramework.SetActiveDebuggingFrameworkPropertyAsync(frameworks[cmdIndex]); + handled = true; } - }); - } - - return handled; + } + }); } - /// - /// Called by the base when one of our menu ids is queried for. If the index is - /// is greater than the count we want to return false - /// - public override bool QueryStatusCommand(int cmdIndex, EventArgs e) + return handled; + } + + /// + /// Called by the base when one of our menu ids is queried for. If the index is + /// is greater than the count we want to return false + /// + public override bool QueryStatusCommand(int cmdIndex, EventArgs e) + { + ImmutableArray activeDebugFrameworks = _startupProjectHelper.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles); + if (!activeDebugFrameworks.IsEmpty) { - ImmutableArray activeDebugFrameworks = _startupProjectHelper.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles); - if (!activeDebugFrameworks.IsEmpty) + // See if the projects support at least two runtimes + List? frameworks = null; + string? activeFramework = null; + ExecuteSynchronously(async () => { - // See if the projects support at least two runtimes - List? frameworks = null; - string? activeFramework = null; - ExecuteSynchronously(async () => + List? first = null; + + foreach (IActiveDebugFrameworkServices activeDebugFramework in activeDebugFrameworks) { - List? first = null; + frameworks = await activeDebugFramework.GetProjectFrameworksAsync(); - foreach (IActiveDebugFrameworkServices activeDebugFramework in activeDebugFrameworks) + if (first is null) { - frameworks = await activeDebugFramework.GetProjectFrameworksAsync(); - - if (first is null) - { - first = frameworks; - } - else - { - if (!first.SequenceEqual(frameworks)) - { - frameworks = null; - break; - } - } + first = frameworks; } - - if (frameworks?.Count > 1 && cmdIndex < frameworks.Count) + else { - // Only call this if we will need it down below. - activeFramework = await activeDebugFrameworks[0].GetActiveDebuggingFrameworkPropertyAsync(); + if (!first.SequenceEqual(frameworks)) + { + frameworks = null; + break; + } } - }); + } - if (frameworks is null || frameworks.Count < 2) + if (frameworks?.Count > 1 && cmdIndex < frameworks.Count) { - // Hide and disable the command - Visible = false; - Enabled = false; - Checked = false; - return true; + // Only call this if we will need it down below. + activeFramework = await activeDebugFrameworks[0].GetActiveDebuggingFrameworkPropertyAsync(); } - else if (cmdIndex >= 0 && cmdIndex < frameworks.Count) - { - Text = frameworks[cmdIndex]; - Visible = true; - Enabled = true; + }); - // Get's a check if it matches the active one, or there is no active one in which case the first one is the active one - Checked = (string.IsNullOrEmpty(activeFramework) && cmdIndex == 0) || string.Equals(frameworks[cmdIndex], activeFramework, StringComparisons.ConfigurationDimensionNames); - MatchedCommandId = 0; - return true; - } + if (frameworks is null || frameworks.Count < 2) + { + // Hide and disable the command + Visible = false; + Enabled = false; + Checked = false; + return true; } + else if (cmdIndex >= 0 && cmdIndex < frameworks.Count) + { + Text = frameworks[cmdIndex]; + Visible = true; + Enabled = true; - return false; + // Get's a check if it matches the active one, or there is no active one in which case the first one is the active one + Checked = (string.IsNullOrEmpty(activeFramework) && cmdIndex == 0) || string.Equals(frameworks[cmdIndex], activeFramework, StringComparisons.ConfigurationDimensionNames); + MatchedCommandId = 0; + return true; + } } - private void ExecuteSynchronously(Func asyncFunction) - { + return false; + } + + private void ExecuteSynchronously(Func asyncFunction) + { #pragma warning disable VSTHRD102 // Implement internal logic asynchronously - _joinableTaskContext.Factory.Run(asyncFunction); + _joinableTaskContext.Factory.Run(asyncFunction); #pragma warning restore VSTHRD102 // Implement internal logic asynchronously - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdater.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdater.cs index 79acaecfe0..9853c9dd8b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdater.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdater.cs @@ -5,115 +5,114 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Updates the text of the Frameworks menu to include the current active framework. Instead of just saying +/// Frameworks it will say Frameworks (netcoreapp1.0). +/// +[Export(PackageCommandRegistrationService.PackageCommandContract, typeof(MenuCommand))] +internal class DebugFrameworkPropertyMenuTextUpdater : OleMenuCommand { + [ImportingConstructor] + public DebugFrameworkPropertyMenuTextUpdater(IStartupProjectHelper startupProjectHelper) + : base( + ExecHandler, + delegate { }, + QueryStatusHandler, + new CommandID(new Guid(CommandGroup.ManagedProjectSystem), ManagedProjectSystemCommandId.DebugTargetMenuDebugFrameworkMenu)) + { + StartupProjectHelper = startupProjectHelper; + } + + private IStartupProjectHelper StartupProjectHelper { get; } + /// - /// Updates the text of the Frameworks menu to include the current active framework. Instead of just saying - /// Frameworks it will say Frameworks (netcoreapp1.0). + /// Exec handler called when one of the menu items is selected. Does some + /// basic validation before calling the commands QueryStatusCommand to update + /// its state /// - [Export(PackageCommandRegistrationService.PackageCommandContract, typeof(MenuCommand))] - internal class DebugFrameworkPropertyMenuTextUpdater : OleMenuCommand + public static void ExecHandler(object sender, EventArgs e) { - [ImportingConstructor] - public DebugFrameworkPropertyMenuTextUpdater(IStartupProjectHelper startupProjectHelper) - : base( - ExecHandler, - delegate { }, - QueryStatusHandler, - new CommandID(new Guid(CommandGroup.ManagedProjectSystem), ManagedProjectSystemCommandId.DebugTargetMenuDebugFrameworkMenu)) - { - StartupProjectHelper = startupProjectHelper; - } - - private IStartupProjectHelper StartupProjectHelper { get; } + } - /// - /// Exec handler called when one of the menu items is selected. Does some - /// basic validation before calling the commands QueryStatusCommand to update - /// its state - /// - public static void ExecHandler(object sender, EventArgs e) + /// + /// QueryStatus handler called to update the status of the menu items. Does some + /// basic validation before calling the commands QueryStatusCommand to update + /// its state + /// + public static void QueryStatusHandler(object sender, EventArgs e) + { + if (sender is DebugFrameworkPropertyMenuTextUpdater command) { + command.QueryStatus(); } + } - /// - /// QueryStatus handler called to update the status of the menu items. Does some - /// basic validation before calling the commands QueryStatusCommand to update - /// its state - /// - public static void QueryStatusHandler(object sender, EventArgs e) + public void QueryStatus() + { + ImmutableArray activeDebugFrameworks = StartupProjectHelper.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles); + if (!activeDebugFrameworks.IsEmpty) { - if (sender is DebugFrameworkPropertyMenuTextUpdater command) + string? activeFramework = null; + List? frameworks = null; + ExecuteSynchronously(async () => { - command.QueryStatus(); - } - } + List? first = null; - public void QueryStatus() - { - ImmutableArray activeDebugFrameworks = StartupProjectHelper.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles); - if (!activeDebugFrameworks.IsEmpty) - { - string? activeFramework = null; - List? frameworks = null; - ExecuteSynchronously(async () => + foreach (IActiveDebugFrameworkServices activeDebugFramework in activeDebugFrameworks) { - List? first = null; + frameworks = await activeDebugFramework.GetProjectFrameworksAsync(); - foreach (IActiveDebugFrameworkServices activeDebugFramework in activeDebugFrameworks) + if (first is null) { - frameworks = await activeDebugFramework.GetProjectFrameworksAsync(); - - if (first is null) - { - first = frameworks; - } - else - { - if (!first.SequenceEqual(frameworks)) - { - frameworks = null; - break; - } - } + first = frameworks; } - - if (frameworks?.Count > 1) + else { - // Only get this if we will need it down below - activeFramework = await activeDebugFrameworks[0].GetActiveDebuggingFrameworkPropertyAsync(); + if (!first.SequenceEqual(frameworks)) + { + frameworks = null; + break; + } } - }); + } if (frameworks?.Count > 1) { - // If no active framework or the current active property doesn't match any of the frameworks, then - // set it to the first one. - if (!Strings.IsNullOrEmpty(activeFramework) && frameworks.Contains(activeFramework)) - { - Text = string.Format(VSResources.DebugFrameworkMenuText, activeFramework); - } - else - { - Text = string.Format(VSResources.DebugFrameworkMenuText, frameworks[0]); - } + // Only get this if we will need it down below + activeFramework = await activeDebugFrameworks[0].GetActiveDebuggingFrameworkPropertyAsync(); + } + }); - Visible = true; - Enabled = true; + if (frameworks?.Count > 1) + { + // If no active framework or the current active property doesn't match any of the frameworks, then + // set it to the first one. + if (!Strings.IsNullOrEmpty(activeFramework) && frameworks.Contains(activeFramework)) + { + Text = string.Format(VSResources.DebugFrameworkMenuText, activeFramework); + } + else + { + Text = string.Format(VSResources.DebugFrameworkMenuText, frameworks[0]); } + + Visible = true; + Enabled = true; } } + } - /// - /// For unit testing to wrap the JTF.Run call. - /// - protected virtual void ExecuteSynchronously(Func asyncFunction) - { + /// + /// For unit testing to wrap the JTF.Run call. + /// + protected virtual void ExecuteSynchronously(Func asyncFunction) + { #pragma warning disable VSTHRD102 // Only wrapped for test purposes #pragma warning disable RS0030 // Do not used banned APIs - ThreadHelper.JoinableTaskFactory.Run(asyncFunction); + ThreadHelper.JoinableTaskFactory.Run(asyncFunction); #pragma warning restore RS0030 // Do not used banned APIs #pragma warning restore VSTHRD102 - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/AbstractDependencyExplorerCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/AbstractDependencyExplorerCommand.cs index df138a635a..9bac3ae301 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/AbstractDependencyExplorerCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/AbstractDependencyExplorerCommand.cs @@ -3,55 +3,54 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Provides the base class for commands +/// that handle Explorer-like commands for dependency +/// nodes. +/// +internal abstract class AbstractDependencyExplorerCommand : AbstractProjectCommand { - /// - /// Provides the base class for commands - /// that handle Explorer-like commands for dependency - /// nodes. - /// - internal abstract class AbstractDependencyExplorerCommand : AbstractProjectCommand + private readonly UnconfiguredProject _project; + + protected AbstractDependencyExplorerCommand(UnconfiguredProject project) { - private readonly UnconfiguredProject _project; + _project = project; + } - protected AbstractDependencyExplorerCommand(UnconfiguredProject project) + protected override Task GetCommandStatusAsync(IImmutableSet nodes, bool focused, string? commandText, CommandStatus progressiveStatus) + { + // Only handle when Solution Explorer has focus so that we don't take over Tab Well handling + if (focused && nodes.All(CanOpen)) { - _project = project; + return GetCommandStatusResult.Handled(commandText, progressiveStatus | CommandStatus.Enabled); } - protected override Task GetCommandStatusAsync(IImmutableSet nodes, bool focused, string? commandText, CommandStatus progressiveStatus) - { - // Only handle when Solution Explorer has focus so that we don't take over Tab Well handling - if (focused && nodes.All(CanOpen)) - { - return GetCommandStatusResult.Handled(commandText, progressiveStatus | CommandStatus.Enabled); - } - - return GetCommandStatusResult.Unhandled; - } + return GetCommandStatusResult.Unhandled; + } - protected override async Task TryHandleCommandAsync(IImmutableSet nodes, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + protected override async Task TryHandleCommandAsync(IImmutableSet nodes, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + // Only handle when Solution Explorer has focus so that we don't take over Tab Well handling + if (focused && nodes.All(CanOpen)) { - // Only handle when Solution Explorer has focus so that we don't take over Tab Well handling - if (focused && nodes.All(CanOpen)) + foreach (IProjectTree node in nodes) { - foreach (IProjectTree node in nodes) - { - string? path = await DependencyServices.GetBrowsePathAsync(_project, node); - if (path is null) - continue; - - Open(path); - } + string? path = await DependencyServices.GetBrowsePathAsync(_project, node); + if (path is null) + continue; - return true; + Open(path); } - return false; + return true; } - protected abstract bool CanOpen(IProjectTree node); - - protected abstract void Open(string path); + return false; } + + protected abstract bool CanOpen(IProjectTree node); + + protected abstract void Open(string path); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/BrowseToDependencyInExplorerCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/BrowseToDependencyInExplorerCommand.cs index 3d5e108dfc..f9c64a0f7c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/BrowseToDependencyInExplorerCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/BrowseToDependencyInExplorerCommand.cs @@ -4,39 +4,38 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Opens the containing folder for references. +/// +/// +/// NOTE: Similar to folders and the project node, this command is supported +/// for container-like references, such as Package References, however, is not +/// placed on the context menu by default, in lieu of +/// VSConstants.VSStd2KCmdID.ExploreFolderInWindows. +/// +[ProjectCommand(VSConstants.CMDSETID.StandardCommandSet2K_string, (long)VSConstants.VSStd2KCmdID.BrowseToFileInExplorer)] +[AppliesTo(ProjectCapability.DependenciesTree)] +[Order(Order.Default)] +internal class BrowseToDependencyInExplorerCommand : AbstractDependencyExplorerCommand { - /// - /// Opens the containing folder for references. - /// - /// - /// NOTE: Similar to folders and the project node, this command is supported - /// for container-like references, such as Package References, however, is not - /// placed on the context menu by default, in lieu of - /// VSConstants.VSStd2KCmdID.ExploreFolderInWindows. - /// - [ProjectCommand(VSConstants.CMDSETID.StandardCommandSet2K_string, (long)VSConstants.VSStd2KCmdID.BrowseToFileInExplorer)] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order.Default)] - internal class BrowseToDependencyInExplorerCommand : AbstractDependencyExplorerCommand - { - private readonly IFileExplorer _fileExplorer; + private readonly IFileExplorer _fileExplorer; - [ImportingConstructor] - public BrowseToDependencyInExplorerCommand(UnconfiguredProject project, IFileExplorer fileExplorer) - : base(project) - { - _fileExplorer = fileExplorer; - } + [ImportingConstructor] + public BrowseToDependencyInExplorerCommand(UnconfiguredProject project, IFileExplorer fileExplorer) + : base(project) + { + _fileExplorer = fileExplorer; + } - protected override bool CanOpen(IProjectTree node) - { - return node.Flags.Contains(DependencyTreeFlags.Dependency | DependencyTreeFlags.SupportsBrowse); - } + protected override bool CanOpen(IProjectTree node) + { + return node.Flags.Contains(DependencyTreeFlags.Dependency | DependencyTreeFlags.SupportsBrowse); + } - protected override void Open(string path) - { - _fileExplorer.OpenContainingFolder(path); - } + protected override void Open(string path) + { + _fileExplorer.OpenContainingFolder(path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/ExploreDependencyFolderInWindowsCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/ExploreDependencyFolderInWindowsCommand.cs index a4223c74af..9f119d5327 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/ExploreDependencyFolderInWindowsCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Dependencies/ExploreDependencyFolderInWindowsCommand.cs @@ -4,34 +4,33 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Opens the folder for references that are backed by folders on disk, including +/// package references, framework references and SDK references. +/// +[ProjectCommand(VSConstants.CMDSETID.StandardCommandSet2K_string, (long)VSConstants.VSStd2KCmdID.ExploreFolderInWindows)] +[AppliesTo(ProjectCapability.DependenciesTree)] +[Order(Order.Default)] +internal class ExploreDependencyFolderInWindowsCommand : AbstractDependencyExplorerCommand { - /// - /// Opens the folder for references that are backed by folders on disk, including - /// package references, framework references and SDK references. - /// - [ProjectCommand(VSConstants.CMDSETID.StandardCommandSet2K_string, (long)VSConstants.VSStd2KCmdID.ExploreFolderInWindows)] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order.Default)] - internal class ExploreDependencyFolderInWindowsCommand : AbstractDependencyExplorerCommand - { - private readonly IFileExplorer _fileExplorer; + private readonly IFileExplorer _fileExplorer; - [ImportingConstructor] - public ExploreDependencyFolderInWindowsCommand(UnconfiguredProject project, IFileExplorer fileExplorer) - : base(project) - { - _fileExplorer = fileExplorer; - } + [ImportingConstructor] + public ExploreDependencyFolderInWindowsCommand(UnconfiguredProject project, IFileExplorer fileExplorer) + : base(project) + { + _fileExplorer = fileExplorer; + } - protected override bool CanOpen(IProjectTree node) - { - return node.Flags.Contains(DependencyTreeFlags.Dependency | DependencyTreeFlags.SupportsFolderBrowse); - } + protected override bool CanOpen(IProjectTree node) + { + return node.Flags.Contains(DependencyTreeFlags.Dependency | DependencyTreeFlags.SupportsFolderBrowse); + } - protected override void Open(string path) - { - _fileExplorer.OpenFolder(path); - } + protected override void Open(string path) + { + _fileExplorer.OpenFolder(path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DynamicMenuCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DynamicMenuCommand.cs index b5122f9742..53b72342fa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DynamicMenuCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/DynamicMenuCommand.cs @@ -3,121 +3,120 @@ using System.ComponentModel.Design; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Implementation of an OleMenuCommand which supports the DynamicStart (like an MRU list) type +/// of commands +/// +internal abstract class DynamicMenuCommand : OleMenuCommand { + public int MaxCount { get; protected set; } + + protected DynamicMenuCommand(CommandID id, int maxCount) + : base(ExecHandler, delegate + { }, QueryStatusHandler, id) + { + MaxCount = maxCount; + } + + /// + /// Derived classes need to implement the following two methods. They must return false + /// if the command is not handled (index is out of range) + /// + public abstract bool ExecCommand(int cmdIndex, EventArgs e); + + public abstract bool QueryStatusCommand(int cmdIndex, EventArgs e); + /// - /// Implementation of an OleMenuCommand which supports the DynamicStart (like an MRU list) type - /// of commands + /// Overridden to set the MatchedCmdId. This is used later by QS\Exec when determining the + /// index of the current selection. /// - internal abstract class DynamicMenuCommand : OleMenuCommand + public override bool DynamicItemMatch(int cmdId) { - public int MaxCount { get; protected set; } + int index = cmdId - CommandID.ID; - protected DynamicMenuCommand(CommandID id, int maxCount) - : base(ExecHandler, delegate - { }, QueryStatusHandler, id) + // Is the index in range? + if (index >= 0 && index < MaxCount) { - MaxCount = maxCount; + MatchedCommandId = cmdId; + return true; } - /// - /// Derived classes need to implement the following two methods. They must return false - /// if the command is not handled (index is out of range) - /// - public abstract bool ExecCommand(int cmdIndex, EventArgs e); - - public abstract bool QueryStatusCommand(int cmdIndex, EventArgs e); + // No match, clear command id and return false + MatchedCommandId = 0; + return false; + } - /// - /// Overridden to set the MatchedCmdId. This is used later by QS\Exec when determining the - /// index of the current selection. - /// - public override bool DynamicItemMatch(int cmdId) + /// + /// Returns the current index (0 based) of the command that is currently selected (set by + /// MatchedCommandId). + /// + public int CurrentIndex + { + get { - int index = cmdId - CommandID.ID; - - // Is the index in range? - if (index >= 0 && index < MaxCount) + // Index is the current matched ID minus the base + if (MatchedCommandId > 0) { - MatchedCommandId = cmdId; - return true; + return MatchedCommandId - CommandID.ID; } - - // No match, clear command id and return false - MatchedCommandId = 0; - return false; + return 0; } + } - /// - /// Returns the current index (0 based) of the command that is currently selected (set by - /// MatchedCommandId). - /// - public int CurrentIndex + /// + /// Exec handler called when one of the menu items is selected. Does some + /// basic validation before calling the commands QueryStatusCommand to update + /// its state + /// + protected static void ExecHandler(object sender, EventArgs e) + { + if (sender is not DynamicMenuCommand command) { - get - { - // Index is the current matched ID minus the base - if (MatchedCommandId > 0) - { - return MatchedCommandId - CommandID.ID; - } - return 0; - } + return; } - /// - /// Exec handler called when one of the menu items is selected. Does some - /// basic validation before calling the commands QueryStatusCommand to update - /// its state - /// - protected static void ExecHandler(object sender, EventArgs e) + int cmdIndex = command.CurrentIndex; + if (cmdIndex >= 0 && cmdIndex < command.MaxCount) { - if (sender is not DynamicMenuCommand command) + // Only return if command was handled + if (command.ExecCommand(cmdIndex, e)) { return; } + } - int cmdIndex = command.CurrentIndex; - if (cmdIndex >= 0 && cmdIndex < command.MaxCount) - { - // Only return if command was handled - if (command.ExecCommand(cmdIndex, e)) - { - return; - } - } + // We want to make sure to clear the matched commandid. + command.MatchedCommandId = 0; + } - // We want to make sure to clear the matched commandid. - command.MatchedCommandId = 0; + /// + /// QueryStatus handler called to update the status of the menu items. Does some + /// basic validation before calling the commands QueryStatusCommand to update + /// its state + /// + protected static void QueryStatusHandler(object sender, EventArgs e) + { + if (sender is not DynamicMenuCommand command) + { + return; } - /// - /// QueryStatus handler called to update the status of the menu items. Does some - /// basic validation before calling the commands QueryStatusCommand to update - /// its state - /// - protected static void QueryStatusHandler(object sender, EventArgs e) + int cmdIndex = command.CurrentIndex; + if (cmdIndex >= 0 && cmdIndex < command.MaxCount) { - if (sender is not DynamicMenuCommand command) + // Only return if command was handled + if (command.QueryStatusCommand(cmdIndex, e)) { + // We want to make sure to clear the matched commandid. + command.MatchedCommandId = 0; return; } - - int cmdIndex = command.CurrentIndex; - if (cmdIndex >= 0 && cmdIndex < command.MaxCount) - { - // Only return if command was handled - if (command.QueryStatusCommand(cmdIndex, e)) - { - // We want to make sure to clear the matched commandid. - command.MatchedCommandId = 0; - return; - } - } - // If we get this far, hide the command - command.Visible = false; - command.Enabled = false; - command.MatchedCommandId = 0; } + // If we get this far, hide the command + command.Visible = false; + command.Enabled = false; + command.MatchedCommandId = 0; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommand.cs index e851c27c05..fbb1e70993 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommand.cs @@ -5,24 +5,23 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +[ProjectCommand(CommandGroup.ManagedProjectSystem, ManagedProjectSystemCommandId.GenerateNuGetPackageProjectContextMenu)] +[AppliesTo(ProjectCapability.Pack)] +internal class GenerateNuGetPackageProjectContextMenuCommand : AbstractGenerateNuGetPackageCommand { - [ProjectCommand(CommandGroup.ManagedProjectSystem, ManagedProjectSystemCommandId.GenerateNuGetPackageProjectContextMenu)] - [AppliesTo(ProjectCapability.Pack)] - internal class GenerateNuGetPackageProjectContextMenuCommand : AbstractGenerateNuGetPackageCommand + [ImportingConstructor] + public GenerateNuGetPackageProjectContextMenuCommand( + UnconfiguredProject project, + IProjectThreadingService threadingService, + ISolutionBuildManager vsSolutionBuildManagerService, + GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) + : base(project, threadingService, vsSolutionBuildManagerService, generatePackageOnBuildPropertyProvider) { - [ImportingConstructor] - public GenerateNuGetPackageProjectContextMenuCommand( - UnconfiguredProject project, - IProjectThreadingService threadingService, - ISolutionBuildManager vsSolutionBuildManagerService, - GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) - : base(project, threadingService, vsSolutionBuildManagerService, generatePackageOnBuildPropertyProvider) - { - } + } - protected override bool ShouldHandle(IProjectTree node) => node.IsRoot(); + protected override bool ShouldHandle(IProjectTree node) => node.IsRoot(); - protected override string GetCommandText() => VSResources.PackCommand; - } + protected override string GetCommandText() => VSResources.PackCommand; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommand.cs index b02b720ed5..b7384df462 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommand.cs @@ -5,25 +5,24 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +[ProjectCommand(CommandGroup.ManagedProjectSystem, ManagedProjectSystemCommandId.GenerateNuGetPackageTopLevelBuild)] +[AppliesTo(ProjectCapability.Pack)] +internal class GenerateNuGetPackageTopLevelBuildMenuCommand : AbstractGenerateNuGetPackageCommand { - [ProjectCommand(CommandGroup.ManagedProjectSystem, ManagedProjectSystemCommandId.GenerateNuGetPackageTopLevelBuild)] - [AppliesTo(ProjectCapability.Pack)] - internal class GenerateNuGetPackageTopLevelBuildMenuCommand : AbstractGenerateNuGetPackageCommand + [ImportingConstructor] + public GenerateNuGetPackageTopLevelBuildMenuCommand( + UnconfiguredProject project, + IProjectThreadingService threadingService, + ISolutionBuildManager vsSolutionBuildManagerService, + GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) + : base(project, threadingService, vsSolutionBuildManagerService, generatePackageOnBuildPropertyProvider) { - [ImportingConstructor] - public GenerateNuGetPackageTopLevelBuildMenuCommand( - UnconfiguredProject project, - IProjectThreadingService threadingService, - ISolutionBuildManager vsSolutionBuildManagerService, - GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) - : base(project, threadingService, vsSolutionBuildManagerService, generatePackageOnBuildPropertyProvider) - { - } + } - protected override bool ShouldHandle(IProjectTree node) => true; + protected override bool ShouldHandle(IProjectTree node) => true; - protected override string GetCommandText() - => string.Format(VSResources.PackSelectedProjectCommand, Path.GetFileNameWithoutExtension(Project.FullPath)); - } + protected override string GetCommandText() + => string.Format(VSResources.PackSelectedProjectCommand, Path.GetFileNameWithoutExtension(Project.FullPath)); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommand.cs index 5bedeb11c5..f7cc46827c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommand.cs @@ -4,18 +4,17 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +// Opens the Project Designer ("Property Pages") when selecting the Open menu item on the AppDesigner folder +[ProjectCommand(CommandGroup.VisualStudioStandard97, VisualStudioStandard97CommandId.Open)] +[AppliesTo(ProjectCapability.AppDesigner)] +[Order(Order.Default)] +internal class OpenProjectDesignerCommand : AbstractOpenProjectDesignerCommand { - // Opens the Project Designer ("Property Pages") when selecting the Open menu item on the AppDesigner folder - [ProjectCommand(CommandGroup.VisualStudioStandard97, VisualStudioStandard97CommandId.Open)] - [AppliesTo(ProjectCapability.AppDesigner)] - [Order(Order.Default)] - internal class OpenProjectDesignerCommand : AbstractOpenProjectDesignerCommand + [ImportingConstructor] + public OpenProjectDesignerCommand(IProjectDesignerService designerService) + : base(designerService) { - [ImportingConstructor] - public OpenProjectDesignerCommand(IProjectDesignerService designerService) - : base(designerService) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommand.cs index b43585a6f9..014b6d9341 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommand.cs @@ -4,18 +4,17 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +// Opens the Project Designer ("Property Pages") when the user double-clicks or presses ENTER on the AppDesigner folder while its selected +[ProjectCommand(CommandGroup.UIHierarchyWindow, UIHierarchyWindowCommandId.DoubleClick, UIHierarchyWindowCommandId.EnterKey)] +[AppliesTo(ProjectCapability.AppDesigner)] +[Order(Order.Default)] +internal class OpenProjectDesignerOnDefaultActionCommand : AbstractOpenProjectDesignerCommand { - // Opens the Project Designer ("Property Pages") when the user double-clicks or presses ENTER on the AppDesigner folder while its selected - [ProjectCommand(CommandGroup.UIHierarchyWindow, UIHierarchyWindowCommandId.DoubleClick, UIHierarchyWindowCommandId.EnterKey)] - [AppliesTo(ProjectCapability.AppDesigner)] - [Order(Order.Default)] - internal class OpenProjectDesignerOnDefaultActionCommand : AbstractOpenProjectDesignerCommand + [ImportingConstructor] + public OpenProjectDesignerOnDefaultActionCommand(IProjectDesignerService designerService) + : base(designerService) { - [ImportingConstructor] - public OpenProjectDesignerOnDefaultActionCommand(IProjectDesignerService designerService) - : base(designerService) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractAddItemCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractAddItemCommand.cs index c91c9e6a68..67687a4b50 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractAddItemCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractAddItemCommand.cs @@ -3,68 +3,67 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +internal abstract class AbstractAddItemCommand : AbstractSingleNodeProjectCommand { - internal abstract class AbstractAddItemCommand : AbstractSingleNodeProjectCommand + private readonly IAddItemDialogService _addItemDialogService; + private readonly OrderAddItemHintReceiver _orderAddItemHintReceiver; + + protected AbstractAddItemCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) { - private readonly IAddItemDialogService _addItemDialogService; - private readonly OrderAddItemHintReceiver _orderAddItemHintReceiver; + Requires.NotNull(addItemDialogService); + Requires.NotNull(orderAddItemHintReceiver); - protected AbstractAddItemCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - { - Requires.NotNull(addItemDialogService); - Requires.NotNull(orderAddItemHintReceiver); + _addItemDialogService = addItemDialogService; + _orderAddItemHintReceiver = orderAddItemHintReceiver; + } - _addItemDialogService = addItemDialogService; - _orderAddItemHintReceiver = orderAddItemHintReceiver; - } + protected abstract bool CanAdd(IProjectTree target); - protected abstract bool CanAdd(IProjectTree target); + protected abstract Task OnAddingNodesAsync(IProjectTree nodeToAddTo); - protected abstract Task OnAddingNodesAsync(IProjectTree nodeToAddTo); + protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + { + IProjectTree? nodeToAddTo = GetNodeToAddTo(node); - protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + if (nodeToAddTo is not null && _addItemDialogService.CanAddNewOrExistingItemTo(nodeToAddTo) && CanAdd(node)) { - IProjectTree? nodeToAddTo = GetNodeToAddTo(node); - - if (nodeToAddTo is not null && _addItemDialogService.CanAddNewOrExistingItemTo(nodeToAddTo) && CanAdd(node)) - { - return GetCommandStatusResult.Handled(commandText, CommandStatus.Enabled); - } - else - { - return GetCommandStatusResult.Unhandled; - } + return GetCommandStatusResult.Handled(commandText, CommandStatus.Enabled); } - - protected virtual OrderingMoveAction Action => OrderingMoveAction.MoveToTop; - - protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + else { - IProjectTree? nodeToAddTo = GetNodeToAddTo(node); + return GetCommandStatusResult.Unhandled; + } + } - if (nodeToAddTo is null) - { - return false; - } + protected virtual OrderingMoveAction Action => OrderingMoveAction.MoveToTop; - // We use a hint receiver that listens for when a file gets added. - // The reason is so we can modify the MSBuild project inside the same write lock of when a file gets added internally in CPS. - // This ensures that we only perform actions on the items that were added as result of a e.g. a add new/existing item dialog. - await _orderAddItemHintReceiver.CaptureAsync(Action, node, () => OnAddingNodesAsync(nodeToAddTo)); + protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + IProjectTree? nodeToAddTo = GetNodeToAddTo(node); - return true; + if (nodeToAddTo is null) + { + return false; } - private IProjectTree? GetNodeToAddTo(IProjectTree node) + // We use a hint receiver that listens for when a file gets added. + // The reason is so we can modify the MSBuild project inside the same write lock of when a file gets added internally in CPS. + // This ensures that we only perform actions on the items that were added as result of a e.g. a add new/existing item dialog. + await _orderAddItemHintReceiver.CaptureAsync(Action, node, () => OnAddingNodesAsync(nodeToAddTo)); + + return true; + } + + private IProjectTree? GetNodeToAddTo(IProjectTree node) + { + return Action switch { - return Action switch - { - OrderingMoveAction.MoveAbove or OrderingMoveAction.MoveBelow => node.Parent, - _ => node, - }; - } + OrderingMoveAction.MoveAbove or OrderingMoveAction.MoveBelow => node.Parent, + _ => node, + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommand.cs index b67fb88bb1..b3f5b44eaf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommand.cs @@ -4,54 +4,53 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +internal abstract class AbstractMoveCommand : AbstractSingleNodeProjectCommand { - internal abstract class AbstractMoveCommand : AbstractSingleNodeProjectCommand - { - private readonly IPhysicalProjectTree _projectTree; - private readonly SVsServiceProvider _serviceProvider; - private readonly ConfiguredProject _configuredProject; - private readonly IProjectAccessor _accessor; + private readonly IPhysicalProjectTree _projectTree; + private readonly SVsServiceProvider _serviceProvider; + private readonly ConfiguredProject _configuredProject; + private readonly IProjectAccessor _accessor; - protected AbstractMoveCommand(IPhysicalProjectTree projectTree, SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) - { - Requires.NotNull(projectTree); - Requires.NotNull(serviceProvider); - Requires.NotNull(configuredProject); - Requires.NotNull(accessor); - - _projectTree = projectTree; - _serviceProvider = serviceProvider; - _configuredProject = configuredProject; - _accessor = accessor; - } + protected AbstractMoveCommand(IPhysicalProjectTree projectTree, SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) + { + Requires.NotNull(projectTree); + Requires.NotNull(serviceProvider); + Requires.NotNull(configuredProject); + Requires.NotNull(accessor); + + _projectTree = projectTree; + _serviceProvider = serviceProvider; + _configuredProject = configuredProject; + _accessor = accessor; + } - protected abstract bool CanMove(IProjectTree node); + protected abstract bool CanMove(IProjectTree node); - protected abstract bool TryMove(Project project, IProjectTree node); + protected abstract bool TryMove(Project project, IProjectTree node); - protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) - { - return GetCommandStatusResult.Handled( - commandText, - CanMove(node) ? CommandStatus.Enabled : CommandStatus.Ninched); - } - - protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - bool didMove = false; + protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + { + return GetCommandStatusResult.Handled( + commandText, + CanMove(node) ? CommandStatus.Enabled : CommandStatus.Ninched); + } - await _accessor.OpenProjectForWriteAsync(_configuredProject, project => didMove = TryMove(project, node)); + protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + bool didMove = false; - if (didMove) - { - // Wait for updating to finish before re-selecting the node that moved. - // We need to re-select the node after it is moved in order to continuously move the node using hotkeys. - await _projectTree.TreeService.PublishLatestTreeAsync(waitForFileSystemUpdates: true); - await NodeHelper.SelectAsync(_configuredProject, _serviceProvider, node); - } + await _accessor.OpenProjectForWriteAsync(_configuredProject, project => didMove = TryMove(project, node)); - return didMove; + if (didMove) + { + // Wait for updating to finish before re-selecting the node that moved. + // We need to re-select the node after it is moved in order to continuously move the node using hotkeys. + await _projectTree.TreeService.PublishLatestTreeAsync(waitForFileSystemUpdates: true); + await NodeHelper.SelectAsync(_configuredProject, _serviceProvider, node); } + + return didMove; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemAboveCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemAboveCommand.cs index f7f0008f56..6c85258963 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemAboveCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemAboveCommand.cs @@ -4,33 +4,32 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering -{ - [ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddExistingItemAbove)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - internal class AddExistingItemAboveCommand : AbstractAddItemCommand - { - private readonly IAddItemDialogService _addItemDialogService; +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; - [ImportingConstructor] - public AddExistingItemAboveCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - : base(addItemDialogService, orderAddItemHintReceiver) - { - _addItemDialogService = addItemDialogService; - } +[ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddExistingItemAbove)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +internal class AddExistingItemAboveCommand : AbstractAddItemCommand +{ + private readonly IAddItemDialogService _addItemDialogService; - protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) - { - return _addItemDialogService.ShowAddExistingItemsDialogAsync(nodeToAddTo); - } + [ImportingConstructor] + public AddExistingItemAboveCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) + : base(addItemDialogService, orderAddItemHintReceiver) + { + _addItemDialogService = addItemDialogService; + } - protected override bool CanAdd(IProjectTree target) - { - return OrderingHelper.HasValidDisplayOrder(target); - } + protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) + { + return _addItemDialogService.ShowAddExistingItemsDialogAsync(nodeToAddTo); + } - protected override OrderingMoveAction Action => OrderingMoveAction.MoveAbove; + protected override bool CanAdd(IProjectTree target) + { + return OrderingHelper.HasValidDisplayOrder(target); } + + protected override OrderingMoveAction Action => OrderingMoveAction.MoveAbove; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemBelowCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemBelowCommand.cs index 89a3e58cda..9be06fa38b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemBelowCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemBelowCommand.cs @@ -4,33 +4,32 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering -{ - [ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddExistingItemBelow)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - internal class AddExistingItemBelowCommand : AbstractAddItemCommand - { - private readonly IAddItemDialogService _addItemDialogService; +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; - [ImportingConstructor] - public AddExistingItemBelowCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - : base(addItemDialogService, orderAddItemHintReceiver) - { - _addItemDialogService = addItemDialogService; - } +[ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddExistingItemBelow)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +internal class AddExistingItemBelowCommand : AbstractAddItemCommand +{ + private readonly IAddItemDialogService _addItemDialogService; - protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) - { - return _addItemDialogService.ShowAddExistingItemsDialogAsync(nodeToAddTo); - } + [ImportingConstructor] + public AddExistingItemBelowCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) + : base(addItemDialogService, orderAddItemHintReceiver) + { + _addItemDialogService = addItemDialogService; + } - protected override bool CanAdd(IProjectTree target) - { - return OrderingHelper.HasValidDisplayOrder(target); - } + protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) + { + return _addItemDialogService.ShowAddExistingItemsDialogAsync(nodeToAddTo); + } - protected override OrderingMoveAction Action => OrderingMoveAction.MoveBelow; + protected override bool CanAdd(IProjectTree target) + { + return OrderingHelper.HasValidDisplayOrder(target); } + + protected override OrderingMoveAction Action => OrderingMoveAction.MoveBelow; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemCommand.cs index 1c02d60e5d..95bf72d547 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddExistingItemCommand.cs @@ -4,32 +4,31 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +[ProjectCommand(CommandGroup.VisualStudioStandard97, (long)VSConstants.VSStd97CmdID.AddExistingItem)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +[Order(5000)] +internal class AddExistingItemCommand : AbstractAddItemCommand { - [ProjectCommand(CommandGroup.VisualStudioStandard97, (long)VSConstants.VSStd97CmdID.AddExistingItem)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - [Order(5000)] - internal class AddExistingItemCommand : AbstractAddItemCommand - { - private readonly IAddItemDialogService _addItemDialogService; + private readonly IAddItemDialogService _addItemDialogService; - [ImportingConstructor] - public AddExistingItemCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - : base(addItemDialogService, orderAddItemHintReceiver) - { - _addItemDialogService = addItemDialogService; - } + [ImportingConstructor] + public AddExistingItemCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) + : base(addItemDialogService, orderAddItemHintReceiver) + { + _addItemDialogService = addItemDialogService; + } - protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) - { - return _addItemDialogService.ShowAddExistingItemsDialogAsync(nodeToAddTo); - } + protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) + { + return _addItemDialogService.ShowAddExistingItemsDialogAsync(nodeToAddTo); + } - protected override bool CanAdd(IProjectTree target) - { - return true; - } + protected override bool CanAdd(IProjectTree target) + { + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemAboveCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemAboveCommand.cs index cffd2e90e2..ef69aa71f8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemAboveCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemAboveCommand.cs @@ -4,33 +4,32 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering -{ - [ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddNewItemAbove)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - internal class AddNewItemAboveCommand : AbstractAddItemCommand - { - private readonly IAddItemDialogService _addItemDialogService; +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; - [ImportingConstructor] - public AddNewItemAboveCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - : base(addItemDialogService, orderAddItemHintReceiver) - { - _addItemDialogService = addItemDialogService; - } +[ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddNewItemAbove)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +internal class AddNewItemAboveCommand : AbstractAddItemCommand +{ + private readonly IAddItemDialogService _addItemDialogService; - protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) - { - return _addItemDialogService.ShowAddNewItemDialogAsync(nodeToAddTo); - } + [ImportingConstructor] + public AddNewItemAboveCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) + : base(addItemDialogService, orderAddItemHintReceiver) + { + _addItemDialogService = addItemDialogService; + } - protected override bool CanAdd(IProjectTree target) - { - return OrderingHelper.HasValidDisplayOrder(target); - } + protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) + { + return _addItemDialogService.ShowAddNewItemDialogAsync(nodeToAddTo); + } - protected override OrderingMoveAction Action => OrderingMoveAction.MoveAbove; + protected override bool CanAdd(IProjectTree target) + { + return OrderingHelper.HasValidDisplayOrder(target); } + + protected override OrderingMoveAction Action => OrderingMoveAction.MoveAbove; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemBelowCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemBelowCommand.cs index eff237395e..f34574c486 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemBelowCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemBelowCommand.cs @@ -4,33 +4,32 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering -{ - [ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddNewItemBelow)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - internal class AddNewItemBelowCommand : AbstractAddItemCommand - { - private readonly IAddItemDialogService _addItemDialogService; +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; - [ImportingConstructor] - public AddNewItemBelowCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - : base(addItemDialogService, orderAddItemHintReceiver) - { - _addItemDialogService = addItemDialogService; - } +[ProjectCommand(CommandGroup.ManagedProjectSystemOrder, ManagedProjectSystemOrderCommandId.AddNewItemBelow)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +internal class AddNewItemBelowCommand : AbstractAddItemCommand +{ + private readonly IAddItemDialogService _addItemDialogService; - protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) - { - return _addItemDialogService.ShowAddNewItemDialogAsync(nodeToAddTo); - } + [ImportingConstructor] + public AddNewItemBelowCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) + : base(addItemDialogService, orderAddItemHintReceiver) + { + _addItemDialogService = addItemDialogService; + } - protected override bool CanAdd(IProjectTree target) - { - return OrderingHelper.HasValidDisplayOrder(target); - } + protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) + { + return _addItemDialogService.ShowAddNewItemDialogAsync(nodeToAddTo); + } - protected override OrderingMoveAction Action => OrderingMoveAction.MoveBelow; + protected override bool CanAdd(IProjectTree target) + { + return OrderingHelper.HasValidDisplayOrder(target); } + + protected override OrderingMoveAction Action => OrderingMoveAction.MoveBelow; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemCommand.cs index bfda267312..32fa75b92a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/AddNewItemCommand.cs @@ -4,32 +4,31 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.ProjectSystem.VS.UI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +[ProjectCommand(CommandGroup.VisualStudioStandard97, (long)VSConstants.VSStd97CmdID.AddNewItem)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +[Order(5000)] +internal class AddNewItemCommand : AbstractAddItemCommand { - [ProjectCommand(CommandGroup.VisualStudioStandard97, (long)VSConstants.VSStd97CmdID.AddNewItem)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - [Order(5000)] - internal class AddNewItemCommand : AbstractAddItemCommand - { - private readonly IAddItemDialogService _addItemDialogService; + private readonly IAddItemDialogService _addItemDialogService; - [ImportingConstructor] - public AddNewItemCommand( - IAddItemDialogService addItemDialogService, - OrderAddItemHintReceiver orderAddItemHintReceiver) - : base(addItemDialogService, orderAddItemHintReceiver) - { - _addItemDialogService = addItemDialogService; - } + [ImportingConstructor] + public AddNewItemCommand( + IAddItemDialogService addItemDialogService, + OrderAddItemHintReceiver orderAddItemHintReceiver) + : base(addItemDialogService, orderAddItemHintReceiver) + { + _addItemDialogService = addItemDialogService; + } - protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) - { - return _addItemDialogService.ShowAddNewItemDialogAsync(nodeToAddTo); - } + protected override Task OnAddingNodesAsync(IProjectTree nodeToAddTo) + { + return _addItemDialogService.ShowAddNewItemDialogAsync(nodeToAddTo); + } - protected override bool CanAdd(IProjectTree target) - { - return true; - } + protected override bool CanAdd(IProjectTree target) + { + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommand.cs index 606efa7429..c98e656cb3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommand.cs @@ -5,25 +5,24 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +[ProjectCommand(CommandGroup.FSharpProject, FSharpProjectCommandId.MoveDown)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +internal class MoveDownCommand : AbstractMoveCommand { - [ProjectCommand(CommandGroup.FSharpProject, FSharpProjectCommandId.MoveDown)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - internal class MoveDownCommand : AbstractMoveCommand + [ImportingConstructor] + public MoveDownCommand(IPhysicalProjectTree projectTree, SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) : base(projectTree, serviceProvider, configuredProject, accessor) { - [ImportingConstructor] - public MoveDownCommand(IPhysicalProjectTree projectTree, SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) : base(projectTree, serviceProvider, configuredProject, accessor) - { - } + } - protected override bool CanMove(IProjectTree node) - { - return OrderingHelper.CanMoveDown(node); - } + protected override bool CanMove(IProjectTree node) + { + return OrderingHelper.CanMoveDown(node); + } - protected override bool TryMove(Project project, IProjectTree node) - { - return OrderingHelper.TryMoveDown(project, node); - } + protected override bool TryMove(Project project, IProjectTree node) + { + return OrderingHelper.TryMoveDown(project, node); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommand.cs index 8814a64100..b8b937fd15 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommand.cs @@ -5,25 +5,24 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +[ProjectCommand(CommandGroup.FSharpProject, FSharpProjectCommandId.MoveUp)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +internal class MoveUpCommand : AbstractMoveCommand { - [ProjectCommand(CommandGroup.FSharpProject, FSharpProjectCommandId.MoveUp)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - internal class MoveUpCommand : AbstractMoveCommand + [ImportingConstructor] + public MoveUpCommand(IPhysicalProjectTree projectTree, SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) : base(projectTree, serviceProvider, configuredProject, accessor) { - [ImportingConstructor] - public MoveUpCommand(IPhysicalProjectTree projectTree, SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) : base(projectTree, serviceProvider, configuredProject, accessor) - { - } + } - protected override bool CanMove(IProjectTree node) - { - return OrderingHelper.CanMoveUp(node); - } + protected override bool CanMove(IProjectTree node) + { + return OrderingHelper.CanMoveUp(node); + } - protected override bool TryMove(Project project, IProjectTree node) - { - return OrderingHelper.TryMoveUp(project, node); - } + protected override bool TryMove(Project project, IProjectTree node) + { + return OrderingHelper.TryMoveUp(project, node); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/NodeHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/NodeHelper.cs index 33d5cb77cf..72b7b14f85 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/NodeHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/NodeHelper.cs @@ -3,98 +3,97 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +/// +/// These are helper functions to select items in a IVsUIHierarchy. +/// This is hack due to CPS not exposing functionality to do this. They have it internally though. +/// Bug filed here: https://devdiv.visualstudio.com/DevDiv/VS%20IDE%20CPS/_workitems/edit/589115 +/// +internal static class NodeHelper { /// - /// These are helper functions to select items in a IVsUIHierarchy. - /// This is hack due to CPS not exposing functionality to do this. They have it internally though. - /// Bug filed here: https://devdiv.visualstudio.com/DevDiv/VS%20IDE%20CPS/_workitems/edit/589115 + /// Select an item in a IVsIHierarchy. + /// Calls on the UI thread. /// - internal static class NodeHelper + public static async Task SelectAsync(ConfiguredProject configuredProject, IServiceProvider serviceProvider, IProjectTree node) { - /// - /// Select an item in a IVsIHierarchy. - /// Calls on the UI thread. - /// - public static async Task SelectAsync(ConfiguredProject configuredProject, IServiceProvider serviceProvider, IProjectTree node) - { - Requires.NotNull(configuredProject); - Requires.NotNull(serviceProvider); - Requires.NotNull(node); + Requires.NotNull(configuredProject); + Requires.NotNull(serviceProvider); + Requires.NotNull(node); - await configuredProject.Services.ProjectService.Services.ThreadingPolicy.SwitchToUIThread(); + await configuredProject.Services.ProjectService.Services.ThreadingPolicy.SwitchToUIThread(); - Select(configuredProject, serviceProvider, (uint)node.Identity.ToInt32()); + Select(configuredProject, serviceProvider, (uint)node.Identity.ToInt32()); - await TaskScheduler.Default; - } + await TaskScheduler.Default; + } - /// - /// Select an item in a IVsIHierarchy. - /// - private static void Select(ConfiguredProject configuredProject, IServiceProvider serviceProvider, uint itemId) + /// + /// Select an item in a IVsIHierarchy. + /// + private static void Select(ConfiguredProject configuredProject, IServiceProvider serviceProvider, uint itemId) + { + UseWindow(configuredProject, serviceProvider, (hierarchy, window) => { - UseWindow(configuredProject, serviceProvider, (hierarchy, window) => + if (window is not null) { - if (window is not null) - { - // We need to unselect the item if it is already selected to re-select it correctly. - window.ExpandItem(hierarchy, itemId, EXPANDFLAGS.EXPF_UnSelectItem); - window.ExpandItem(hierarchy, itemId, EXPANDFLAGS.EXPF_SelectItem); - } - }); - } + // We need to unselect the item if it is already selected to re-select it correctly. + window.ExpandItem(hierarchy, itemId, EXPANDFLAGS.EXPF_UnSelectItem); + window.ExpandItem(hierarchy, itemId, EXPANDFLAGS.EXPF_SelectItem); + } + }); + } - /// - /// Callbacks with a hierarchy and hierarchy window for use. - /// - private static void UseWindow(ConfiguredProject configuredProject, IServiceProvider serviceProvider, Action callback) - { - Assumes.NotNull(configuredProject.UnconfiguredProject.Services.HostObject); - var hierarchy = (IVsUIHierarchy)configuredProject.UnconfiguredProject.Services.HostObject; - callback(hierarchy, GetUIHierarchyWindow(serviceProvider, VSConstants.StandardToolWindows.SolutionExplorer)); - } + /// + /// Callbacks with a hierarchy and hierarchy window for use. + /// + private static void UseWindow(ConfiguredProject configuredProject, IServiceProvider serviceProvider, Action callback) + { + Assumes.NotNull(configuredProject.UnconfiguredProject.Services.HostObject); + var hierarchy = (IVsUIHierarchy)configuredProject.UnconfiguredProject.Services.HostObject; + callback(hierarchy, GetUIHierarchyWindow(serviceProvider, VSConstants.StandardToolWindows.SolutionExplorer)); + } - /// - /// Get reference to IVsUIHierarchyWindow interface from guid persistence slot. - /// - /// The service provider. - /// Unique identifier for a tool window created using IVsUIShell::CreateToolWindow. - /// The caller of this method can use predefined identifiers that map to tool windows if those tool windows - /// are known to the caller. - /// A reference to an IVsUIHierarchyWindow interface, or if the window isn't available, such as command line mode. - private static IVsUIHierarchyWindow? GetUIHierarchyWindow(IServiceProvider serviceProvider, Guid persistenceSlot) - { - Requires.NotNull(serviceProvider); + /// + /// Get reference to IVsUIHierarchyWindow interface from guid persistence slot. + /// + /// The service provider. + /// Unique identifier for a tool window created using IVsUIShell::CreateToolWindow. + /// The caller of this method can use predefined identifiers that map to tool windows if those tool windows + /// are known to the caller. + /// A reference to an IVsUIHierarchyWindow interface, or if the window isn't available, such as command line mode. + private static IVsUIHierarchyWindow? GetUIHierarchyWindow(IServiceProvider serviceProvider, Guid persistenceSlot) + { + Requires.NotNull(serviceProvider); - if (serviceProvider is null) - { - throw new ArgumentNullException(nameof(serviceProvider)); - } + if (serviceProvider is null) + { + throw new ArgumentNullException(nameof(serviceProvider)); + } #pragma warning disable RS0030 // Do not used banned APIs - IVsUIShell shell = serviceProvider.GetService(); + IVsUIShell shell = serviceProvider.GetService(); #pragma warning restore RS0030 // Do not used banned APIs - object? pvar = null; - IVsUIHierarchyWindow? uiHierarchyWindow = null; + object? pvar = null; + IVsUIHierarchyWindow? uiHierarchyWindow = null; - try + try + { + if (ErrorHandler.Succeeded(shell.FindToolWindow(0, ref persistenceSlot, out IVsWindowFrame frame)) && frame is not null) { - if (ErrorHandler.Succeeded(shell.FindToolWindow(0, ref persistenceSlot, out IVsWindowFrame frame)) && frame is not null) - { - ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out pvar)); - } + ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out pvar)); } - finally + } + finally + { + if (pvar is not null) { - if (pvar is not null) - { - uiHierarchyWindow = (IVsUIHierarchyWindow)pvar; - } + uiHierarchyWindow = (IVsUIHierarchyWindow)pvar; } - - return uiHierarchyWindow; } + + return uiHierarchyWindow; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderAddItemHintReceiver.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderAddItemHintReceiver.cs index 7361b8dbb2..6c5180fff0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderAddItemHintReceiver.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderAddItemHintReceiver.cs @@ -1,90 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +[Export(typeof(IProjectChangeHintReceiver))] +[Export(typeof(OrderAddItemHintReceiver))] +[ProjectChangeHintKind(ProjectChangeFileSystemEntityHint.AddedFileAsString)] +[AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private)] +internal class OrderAddItemHintReceiver : IProjectChangeHintReceiver { - [Export(typeof(IProjectChangeHintReceiver))] - [Export(typeof(OrderAddItemHintReceiver))] - [ProjectChangeHintKind(ProjectChangeFileSystemEntityHint.AddedFileAsString)] - [AppliesTo(ProjectCapability.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private)] - internal class OrderAddItemHintReceiver : IProjectChangeHintReceiver - { - private readonly IProjectAccessor _accessor; + private readonly IProjectAccessor _accessor; - private ImmutableHashSet _previousIncludes = ImmutableHashSet.Empty; - private OrderingMoveAction _action = OrderingMoveAction.NoOp; - private IProjectTree? _target; - private bool _isHinting; + private ImmutableHashSet _previousIncludes = ImmutableHashSet.Empty; + private OrderingMoveAction _action = OrderingMoveAction.NoOp; + private IProjectTree? _target; + private bool _isHinting; - [ImportingConstructor] - public OrderAddItemHintReceiver(IProjectAccessor accessor) - { - _accessor = accessor; - } + [ImportingConstructor] + public OrderAddItemHintReceiver(IProjectAccessor accessor) + { + _accessor = accessor; + } - public async Task HintedAsync(IImmutableDictionary> hints) + public async Task HintedAsync(IImmutableDictionary> hints) + { + if (CanMove() && !_previousIncludes.IsEmpty && hints.TryGetValue(ProjectChangeFileSystemEntityHint.AddedFile, out IImmutableSet addedFileHints)) { - if (CanMove() && !_previousIncludes.IsEmpty && hints.TryGetValue(ProjectChangeFileSystemEntityHint.AddedFile, out IImmutableSet addedFileHints)) - { - IProjectChangeHint hint = addedFileHints.First(); - Assumes.Present(hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider); - ConfiguredProject? configuredProject = hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject; - Assumes.NotNull(configuredProject); - await OrderingHelper.MoveAsync(configuredProject, _accessor, _previousIncludes, _target!, _action); - } - - // Reset everything because we are done. - // We need to make sure these are all reset so we can listen for changes again. - Reset(); + IProjectChangeHint hint = addedFileHints.First(); + Assumes.Present(hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider); + ConfiguredProject? configuredProject = hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject; + Assumes.NotNull(configuredProject); + await OrderingHelper.MoveAsync(configuredProject, _accessor, _previousIncludes, _target!, _action); } - public async Task HintingAsync(IProjectChangeHint hint) + // Reset everything because we are done. + // We need to make sure these are all reset so we can listen for changes again. + Reset(); + } + + public async Task HintingAsync(IProjectChangeHint hint) + { + // This will only be called once even if you are adding multiple files from say, e.g. add existing item dialog + // However we check to see if we captured the previous includes for sanity to ensure it only gets set once. + if (CanMove() && _previousIncludes.IsEmpty) { - // This will only be called once even if you are adding multiple files from say, e.g. add existing item dialog - // However we check to see if we captured the previous includes for sanity to ensure it only gets set once. - if (CanMove() && _previousIncludes.IsEmpty) - { - Assumes.Present(hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider); - ConfiguredProject? configuredProject = hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject; - Assumes.NotNull(configuredProject); - _previousIncludes = await OrderingHelper.GetAllEvaluatedIncludesAsync(configuredProject, _accessor); + Assumes.Present(hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider); + ConfiguredProject? configuredProject = hint.UnconfiguredProject.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject; + Assumes.NotNull(configuredProject); + _previousIncludes = await OrderingHelper.GetAllEvaluatedIncludesAsync(configuredProject, _accessor); - _isHinting = true; - } + _isHinting = true; } + } - /// - /// When the task runs, if the receiver picks up that we will be adding an item, it will capture the MSBuild project's includes. - /// If any items were added as a result of the task running, the hint receiver will perform the specified action on those items. - /// - public async Task CaptureAsync(OrderingMoveAction action, IProjectTree target, Func task) - { - Requires.NotNull(target); - Requires.NotNull(task); - - _action = action; - _target = target; - await task(); + /// + /// When the task runs, if the receiver picks up that we will be adding an item, it will capture the MSBuild project's includes. + /// If any items were added as a result of the task running, the hint receiver will perform the specified action on those items. + /// + public async Task CaptureAsync(OrderingMoveAction action, IProjectTree target, Func task) + { + Requires.NotNull(target); + Requires.NotNull(task); - // We need to be sure we are not hinting before we reset, otherwise everything would get reset before HintedAsync gets called. - // This is for sanity. - if (!_isHinting) - { - Reset(); - } - } + _action = action; + _target = target; + await task(); - private void Reset() + // We need to be sure we are not hinting before we reset, otherwise everything would get reset before HintedAsync gets called. + // This is for sanity. + if (!_isHinting) { - _action = OrderingMoveAction.NoOp; - _target = null; - _previousIncludes = ImmutableHashSet.Empty; - _isHinting = false; + Reset(); } + } - private bool CanMove() - { - return _action != OrderingMoveAction.NoOp && _target is not null; - } + private void Reset() + { + _action = OrderingMoveAction.NoOp; + _target = null; + _previousIncludes = ImmutableHashSet.Empty; + _isHinting = false; + } + + private bool CanMove() + { + return _action != OrderingMoveAction.NoOp && _target is not null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelper.cs index 1e42c1f011..ceefe474fd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelper.cs @@ -5,469 +5,468 @@ using Microsoft.Build.Evaluation; using Microsoft.VisualStudio.Buffers.PooledObjects; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +/// +/// Helper methods to interact with a project tree that have items with a valid display order. +/// +internal static class OrderingHelper { /// - /// Helper methods to interact with a project tree that have items with a valid display order. + /// Performs a move on any items that were added based on the previous includes. /// - internal static class OrderingHelper + public static Task MoveAsync(ConfiguredProject configuredProject, IProjectAccessor accessor, ImmutableHashSet previousIncludes, IProjectTree target, OrderingMoveAction action) { - /// - /// Performs a move on any items that were added based on the previous includes. - /// - public static Task MoveAsync(ConfiguredProject configuredProject, IProjectAccessor accessor, ImmutableHashSet previousIncludes, IProjectTree target, OrderingMoveAction action) + Requires.NotNull(configuredProject); + Requires.NotNull(accessor); + Requires.NotNull(previousIncludes); + Requires.NotNull(target); + + return accessor.OpenProjectForWriteAsync(configuredProject, project => { - Requires.NotNull(configuredProject); - Requires.NotNull(accessor); - Requires.NotNull(previousIncludes); - Requires.NotNull(target); + // We do a sanity re-evaluation to absolutely ensure changes were met. + project.ReevaluateIfNecessary(); + ImmutableArray addedElements = GetAddedItemElements(previousIncludes, project); - return accessor.OpenProjectForWriteAsync(configuredProject, project => + // TODO: Should the result (success or failure) be ignored? + _ = action switch { - // We do a sanity re-evaluation to absolutely ensure changes were met. - project.ReevaluateIfNecessary(); - ImmutableArray addedElements = GetAddedItemElements(previousIncludes, project); + OrderingMoveAction.MoveToTop => TryMoveElementsToTop(project, addedElements, target), + OrderingMoveAction.MoveAbove => TryMoveElementsAbove(project, addedElements, target), + OrderingMoveAction.MoveBelow => TryMoveElementsBelow(project, addedElements, target), + _ => false + }; + }); + } - // TODO: Should the result (success or failure) be ignored? - _ = action switch - { - OrderingMoveAction.MoveToTop => TryMoveElementsToTop(project, addedElements, target), - OrderingMoveAction.MoveAbove => TryMoveElementsAbove(project, addedElements, target), - OrderingMoveAction.MoveBelow => TryMoveElementsBelow(project, addedElements, target), - _ => false - }; - }); - } + /// + /// Get all evaluated includes from a project as an immutable hash set. This includes items that aren't for ordering as well. + /// + public static Task> GetAllEvaluatedIncludesAsync(ConfiguredProject configuredProject, IProjectAccessor accessor) + { + Requires.NotNull(configuredProject); + Requires.NotNull(accessor); - /// - /// Get all evaluated includes from a project as an immutable hash set. This includes items that aren't for ordering as well. - /// - public static Task> GetAllEvaluatedIncludesAsync(ConfiguredProject configuredProject, IProjectAccessor accessor) - { - Requires.NotNull(configuredProject); - Requires.NotNull(accessor); + return accessor.OpenProjectForReadAsync(configuredProject, project => + project.AllEvaluatedItems.Select(x => x.EvaluatedInclude).ToImmutableHashSet(StringComparers.ItemNames)); + } - return accessor.OpenProjectForReadAsync(configuredProject, project => - project.AllEvaluatedItems.Select(x => x.EvaluatedInclude).ToImmutableHashSet(StringComparers.ItemNames)); - } + /// + /// Checks to see if the project tree has a valid display order. + /// + public static bool HasValidDisplayOrder([NotNullWhen(returnValue: true)] IProjectTree? projectTree) + { + return IsValidDisplayOrder(GetDisplayOrder(projectTree)); + } - /// - /// Checks to see if the project tree has a valid display order. - /// - public static bool HasValidDisplayOrder([NotNullWhen(returnValue: true)] IProjectTree? projectTree) + /// + /// Gets the display order for a project tree. + /// + public static int GetDisplayOrder(IProjectTree? projectTree) + { + if (projectTree is IProjectTree2 projectTree2) { - return IsValidDisplayOrder(GetDisplayOrder(projectTree)); + return projectTree2.DisplayOrder; } + // It's safe to return zero here. Project trees that do not have a display order are always assumed zero. + return 0; + } - /// - /// Gets the display order for a project tree. - /// - public static int GetDisplayOrder(IProjectTree? projectTree) - { - if (projectTree is IProjectTree2 projectTree2) - { - return projectTree2.DisplayOrder; - } - // It's safe to return zero here. Project trees that do not have a display order are always assumed zero. - return 0; - } + /// + /// Checks if the given project tree can move up over one of its siblings. + /// + public static bool CanMoveUp(IProjectTree projectTree) + { + Requires.NotNull(projectTree); - /// - /// Checks if the given project tree can move up over one of its siblings. - /// - public static bool CanMoveUp(IProjectTree projectTree) - { - Requires.NotNull(projectTree); + return GetSiblingByMoveAction(projectTree, MoveAction.Above) is not null; + } - return GetSiblingByMoveAction(projectTree, MoveAction.Above) is not null; - } + /// + /// Move the project tree up over one of its siblings. + /// + public static bool TryMoveUp(Project project, IProjectTree projectTree) + { + Requires.NotNull(project); + Requires.NotNull(projectTree); - /// - /// Move the project tree up over one of its siblings. - /// - public static bool TryMoveUp(Project project, IProjectTree projectTree) - { - Requires.NotNull(project); - Requires.NotNull(projectTree); + return TryMove(project, projectTree, MoveAction.Above); + } - return TryMove(project, projectTree, MoveAction.Above); - } + /// + /// Checks if the given project tree can move down over one of its siblings. + /// + public static bool CanMoveDown(IProjectTree projectTree) + { + Requires.NotNull(projectTree); - /// - /// Checks if the given project tree can move down over one of its siblings. - /// - public static bool CanMoveDown(IProjectTree projectTree) - { - Requires.NotNull(projectTree); + return GetSiblingByMoveAction(projectTree, MoveAction.Below) is not null; + } - return GetSiblingByMoveAction(projectTree, MoveAction.Below) is not null; - } + /// + /// Move the project tree down over one of its siblings. + /// + public static bool TryMoveDown(Project project, IProjectTree projectTree) + { + Requires.NotNull(project); + Requires.NotNull(projectTree); - /// - /// Move the project tree down over one of its siblings. - /// - public static bool TryMoveDown(Project project, IProjectTree projectTree) - { - Requires.NotNull(project); - Requires.NotNull(projectTree); + return TryMove(project, projectTree, MoveAction.Below); + } - return TryMove(project, projectTree, MoveAction.Below); - } + /// + /// Move the respective item elements above the target. + /// + public static bool TryMoveElementsAbove(Project project, ImmutableArray elements, IProjectTree target) + { + Requires.NotNull(project); + Requires.NotNull(target); - /// - /// Move the respective item elements above the target. - /// - public static bool TryMoveElementsAbove(Project project, ImmutableArray elements, IProjectTree target) + ProjectItemElement? referenceElement = TryGetReferenceElement(project, target, ImmutableArray.Empty, MoveAction.Above); + if (referenceElement is null) { - Requires.NotNull(project); - Requires.NotNull(target); - - ProjectItemElement? referenceElement = TryGetReferenceElement(project, target, ImmutableArray.Empty, MoveAction.Above); - if (referenceElement is null) - { - return false; - } - - return TryMoveElements(elements, referenceElement, MoveAction.Above); + return false; } - /// - /// Move the respective item elements below the target. - /// - public static bool TryMoveElementsBelow(Project project, ImmutableArray elements, IProjectTree target) - { - Requires.NotNull(project); - Requires.NotNull(target); - - ProjectItemElement? referenceElement = TryGetReferenceElement(project, target, ImmutableArray.Empty, MoveAction.Below); - if (referenceElement is null) - { - return false; - } + return TryMoveElements(elements, referenceElement, MoveAction.Above); + } - return TryMoveElements(elements, referenceElement, MoveAction.Below); - } + /// + /// Move the respective item elements below the target. + /// + public static bool TryMoveElementsBelow(Project project, ImmutableArray elements, IProjectTree target) + { + Requires.NotNull(project); + Requires.NotNull(target); - /// - /// Move the respective item elements to the top of the target's children. - /// - public static bool TryMoveElementsToTop(Project project, ImmutableArray elements, IProjectTree target) + ProjectItemElement? referenceElement = TryGetReferenceElement(project, target, ImmutableArray.Empty, MoveAction.Below); + if (referenceElement is null) { - Requires.NotNull(project); - Requires.NotNull(target); + return false; + } - IProjectTree? newTarget = target; + return TryMoveElements(elements, referenceElement, MoveAction.Below); + } - // This is to handle adding files to empty folders since empty folders do not have a valid display order yet. - // We need to find a target up the tree that has a valid display order, because it most likely will have our reference element that we want. - while (!HasValidDisplayOrder(newTarget) && !newTarget!.Flags.Contains(ProjectTreeFlags.ProjectRoot)) - { - newTarget = newTarget.Parent; - } + /// + /// Move the respective item elements to the top of the target's children. + /// + public static bool TryMoveElementsToTop(Project project, ImmutableArray elements, IProjectTree target) + { + Requires.NotNull(project); + Requires.NotNull(target); - var excludeIncludes = elements.Select(x => x.Include).ToImmutableArray(); - ProjectItemElement? referenceElement = GetChildren(newTarget!).Select(x => TryGetReferenceElement(project, x, excludeIncludes, MoveAction.Above)).FirstOrDefault(x => x is not null); - if (referenceElement is null) - { - return false; - } + IProjectTree? newTarget = target; - return TryMoveElements(elements, referenceElement, MoveAction.Above); + // This is to handle adding files to empty folders since empty folders do not have a valid display order yet. + // We need to find a target up the tree that has a valid display order, because it most likely will have our reference element that we want. + while (!HasValidDisplayOrder(newTarget) && !newTarget!.Flags.Contains(ProjectTreeFlags.ProjectRoot)) + { + newTarget = newTarget.Parent; } - /// - /// Get project item elements based on the project tree. - /// Project tree can be a folder or item. - /// - public static ImmutableArray GetItemElements(Project project, IProjectTree projectTree, ImmutableArray excludeIncludes) + var excludeIncludes = elements.Select(x => x.Include).ToImmutableArray(); + ProjectItemElement? referenceElement = GetChildren(newTarget!).Select(x => TryGetReferenceElement(project, x, excludeIncludes, MoveAction.Above)).FirstOrDefault(x => x is not null); + if (referenceElement is null) { - Requires.NotNull(project); - Requires.NotNull(projectTree); - - var includes = GetEvaluatedIncludes(projectTree).Except(excludeIncludes, StringComparers.ItemNames).ToImmutableArray(); - return GetItemElements(project, includes); + return false; } - /// - /// Determines if we are moving up or down files or folders. - /// - private enum MoveAction { Above = 0, Below = 1 } + return TryMoveElements(elements, referenceElement, MoveAction.Above); + } - private static ImmutableArray GetItemElements(Project project, ImmutableArray includes) + /// + /// Get project item elements based on the project tree. + /// Project tree can be a folder or item. + /// + public static ImmutableArray GetItemElements(Project project, IProjectTree projectTree, ImmutableArray excludeIncludes) + { + Requires.NotNull(project); + Requires.NotNull(projectTree); + + var includes = GetEvaluatedIncludes(projectTree).Except(excludeIncludes, StringComparers.ItemNames).ToImmutableArray(); + return GetItemElements(project, includes); + } + + /// + /// Determines if we are moving up or down files or folders. + /// + private enum MoveAction { Above = 0, Below = 1 } + + private static ImmutableArray GetItemElements(Project project, ImmutableArray includes) + { + var elements = PooledArray.GetInstance(); + + foreach (string include in includes) { - var elements = PooledArray.GetInstance(); + // GetItemsByEvaluatedInclude is efficient and uses a MultiDictionary underneath. + // It uses this: new MultiDictionary(StringComparer.OrdinalIgnoreCase); + ProjectItem item = project.GetItemsByEvaluatedInclude(include).FirstOrDefault(); - foreach (string include in includes) + // We only care about adding one item associated with the evaluated include. + if (item?.Xml is ProjectItemElement element && !item.IsImported) { - // GetItemsByEvaluatedInclude is efficient and uses a MultiDictionary underneath. - // It uses this: new MultiDictionary(StringComparer.OrdinalIgnoreCase); - ProjectItem item = project.GetItemsByEvaluatedInclude(include).FirstOrDefault(); - - // We only care about adding one item associated with the evaluated include. - if (item?.Xml is ProjectItemElement element && !item.IsImported) - { - elements.Add(element); - } + elements.Add(element); } - - return elements.ToImmutableAndFree(); } - /// - /// Gets a read-only collection with the evaluated includes associated with a project tree. - /// Evaluated includes will be in order by their display order. - /// - private static IEnumerable GetEvaluatedIncludes(IProjectTree projectTree) - { - var treeQueue = new Queue(); + return elements.ToImmutableAndFree(); + } - var hashSet = new HashSet(StringComparers.ItemNames); - var includes = new SortedList(); + /// + /// Gets a read-only collection with the evaluated includes associated with a project tree. + /// Evaluated includes will be in order by their display order. + /// + private static IEnumerable GetEvaluatedIncludes(IProjectTree projectTree) + { + var treeQueue = new Queue(); - treeQueue.Enqueue(projectTree); + var hashSet = new HashSet(StringComparers.ItemNames); + var includes = new SortedList(); - // The queue is how we process each project tree. - while (treeQueue.Count > 0) - { - IProjectTree tree = treeQueue.Dequeue(); + treeQueue.Enqueue(projectTree); + + // The queue is how we process each project tree. + while (treeQueue.Count > 0) + { + IProjectTree tree = treeQueue.Dequeue(); - if (tree is IProjectItemTree2 tree2 && IsValidDisplayOrder(tree2.DisplayOrder)) + if (tree is IProjectItemTree2 tree2 && IsValidDisplayOrder(tree2.DisplayOrder)) + { + // Technically it is possible to have more than one of the same item names. + // We only want to add one of them. + // Sanity check + if (tree2.Item?.ItemName is not null && hashSet.Add(tree2.Item.ItemName)) { - // Technically it is possible to have more than one of the same item names. - // We only want to add one of them. - // Sanity check - if (tree2.Item?.ItemName is not null && hashSet.Add(tree2.Item.ItemName)) - { - includes.Add(tree2.DisplayOrder, tree2.Item.ItemName); - } + includes.Add(tree2.DisplayOrder, tree2.Item.ItemName); } + } - if (tree.IsFolder || tree.Flags.HasFlag(ProjectTreeFlags.Common.ProjectRoot)) + if (tree.IsFolder || tree.Flags.HasFlag(ProjectTreeFlags.Common.ProjectRoot)) + { + foreach (IProjectTree childTree in tree.Children) { - foreach (IProjectTree childTree in tree.Children) - { - treeQueue.Enqueue(childTree); - } + treeQueue.Enqueue(childTree); } } - - return includes.Select(x => x.Value); } - /// - /// Checks to see if the display order is valid. - /// - private static bool IsValidDisplayOrder(int displayOrder) + return includes.Select(x => x.Value); + } + + /// + /// Checks to see if the display order is valid. + /// + private static bool IsValidDisplayOrder(int displayOrder) + { + return displayOrder > 0 && displayOrder != int.MaxValue; + } + + /// + /// Gets a collection a project tree's children. + /// The children will only have a valid display order, and the collection will be in order by their display order. + /// + private static ImmutableArray GetChildren(IProjectTree projectTree) + { + return projectTree.Children.Where(HasValidDisplayOrder).OrderBy(GetDisplayOrder).ToImmutableArray(); + } + + /// + /// Gets a sibling based on the given project tree. Can return null. + /// + /// the given project tree + /// passes the index of the given project tree from the given ordered sequence, expecting to return a sibling + /// a sibling + private static IProjectTree2? GetSiblingByDisplayOrder(IProjectTree projectTree, Func, IProjectTree2?> returnSibling) + { + IProjectTree? parent = projectTree.Parent; + int displayOrder = GetDisplayOrder(projectTree); + if (!IsValidDisplayOrder(displayOrder) || parent is null) { - return displayOrder > 0 && displayOrder != int.MaxValue; + return null; } - /// - /// Gets a collection a project tree's children. - /// The children will only have a valid display order, and the collection will be in order by their display order. - /// - private static ImmutableArray GetChildren(IProjectTree projectTree) + ImmutableArray orderedChildren = GetChildren(parent); + + for (int i = 0; i < orderedChildren.Length; ++i) { - return projectTree.Children.Where(HasValidDisplayOrder).OrderBy(GetDisplayOrder).ToImmutableArray(); + IProjectTree sibling = orderedChildren[i]; + if (GetDisplayOrder(sibling) == displayOrder) + { + return returnSibling(i, orderedChildren); + } } - /// - /// Gets a sibling based on the given project tree. Can return null. - /// - /// the given project tree - /// passes the index of the given project tree from the given ordered sequence, expecting to return a sibling - /// a sibling - private static IProjectTree2? GetSiblingByDisplayOrder(IProjectTree projectTree, Func, IProjectTree2?> returnSibling) + return null; + } + + /// + /// Gets the previous sibling of the given project tree, if there is any. Can return null. + /// + private static IProjectTree2? GetPreviousSibling(IProjectTree projectTree) + { + return GetSiblingByDisplayOrder(projectTree, (i, orderedChildren) => { - IProjectTree? parent = projectTree.Parent; - int displayOrder = GetDisplayOrder(projectTree); - if (!IsValidDisplayOrder(displayOrder) || parent is null) + if (i == 0) { return null; } - ImmutableArray orderedChildren = GetChildren(parent); + return orderedChildren[i - 1] as IProjectTree2; + }); + } - for (int i = 0; i < orderedChildren.Length; ++i) + /// + /// Gets the next sibling of the given project tree, if there is any. Can return null. + /// + private static IProjectTree2? GetNextSibling(IProjectTree projectTree) + { + return GetSiblingByDisplayOrder(projectTree, (i, orderedChildren) => + { + if (i == (orderedChildren.Length - 1)) { - IProjectTree sibling = orderedChildren[i]; - if (GetDisplayOrder(sibling) == displayOrder) - { - return returnSibling(i, orderedChildren); - } + return null; } - return null; - } + return orderedChildren[i + 1] as IProjectTree2; + }); + } - /// - /// Gets the previous sibling of the given project tree, if there is any. Can return null. - /// - private static IProjectTree2? GetPreviousSibling(IProjectTree projectTree) - { - return GetSiblingByDisplayOrder(projectTree, (i, orderedChildren) => - { - if (i == 0) - { - return null; - } + /// + /// Gets a sibling of the given project tree based on the move action. Can return null. + /// + private static IProjectTree? GetSiblingByMoveAction(IProjectTree projectTree, MoveAction moveAction) + { + return moveAction == MoveAction.Above + ? GetPreviousSibling(projectTree) + : GetNextSibling(projectTree); + } - return orderedChildren[i - 1] as IProjectTree2; - }); - } + /// + /// Gets a reference element based on the given project tree and move action. Can return null. + /// The reference element is the element for which moved items will be above or below it. + /// + private static ProjectItemElement? TryGetReferenceElement(Project project, IProjectTree projectTree, ImmutableArray excludeIncludes, MoveAction moveAction) + { + ImmutableArray items = GetItemElements(project, projectTree, excludeIncludes); - /// - /// Gets the next sibling of the given project tree, if there is any. Can return null. - /// - private static IProjectTree2? GetNextSibling(IProjectTree projectTree) - { - return GetSiblingByDisplayOrder(projectTree, (i, orderedChildren) => - { - if (i == (orderedChildren.Length - 1)) - { - return null; - } + return moveAction == MoveAction.Above + ? items.FirstOrDefault() + : items.LastOrDefault(); + } - return orderedChildren[i + 1] as IProjectTree2; - }); - } + /// + /// Moves child elements based on the reference element and move action. + /// + /// + /// element for which moved items will be above or below it + /// + /// true or false; 'true' if all elements were successfully moved. 'false' if just one element was not moved successfully. + private static bool TryMoveElements(ImmutableArray elements, ProjectItemElement referenceElement, MoveAction moveAction) + { + Requires.NotNull(referenceElement); - /// - /// Gets a sibling of the given project tree based on the move action. Can return null. - /// - private static IProjectTree? GetSiblingByMoveAction(IProjectTree projectTree, MoveAction moveAction) + ProjectElementContainer parent = referenceElement.Parent; + if (parent is null || !elements.Any()) { - return moveAction == MoveAction.Above - ? GetPreviousSibling(projectTree) - : GetNextSibling(projectTree); + return false; } - /// - /// Gets a reference element based on the given project tree and move action. Can return null. - /// The reference element is the element for which moved items will be above or below it. - /// - private static ProjectItemElement? TryGetReferenceElement(Project project, IProjectTree projectTree, ImmutableArray excludeIncludes, MoveAction moveAction) - { - ImmutableArray items = GetItemElements(project, projectTree, excludeIncludes); - - return moveAction == MoveAction.Above - ? items.FirstOrDefault() - : items.LastOrDefault(); - } + // Sanity check + bool didAllElementsMove = true; - /// - /// Moves child elements based on the reference element and move action. - /// - /// - /// element for which moved items will be above or below it - /// - /// true or false; 'true' if all elements were successfully moved. 'false' if just one element was not moved successfully. - private static bool TryMoveElements(ImmutableArray elements, ProjectItemElement referenceElement, MoveAction moveAction) + switch (moveAction) { - Requires.NotNull(referenceElement); - - ProjectElementContainer parent = referenceElement.Parent; - if (parent is null || !elements.Any()) - { - return false; - } + case MoveAction.Above: + foreach (ProjectItemElement element in elements) + { + ProjectElementContainer elementParent = element.Parent; + if (elementParent is not null) + { + elementParent.RemoveChild(element); + parent.InsertBeforeChild(element, referenceElement); + } + else + { + didAllElementsMove = false; + } + } + break; - // Sanity check - bool didAllElementsMove = true; + case MoveAction.Below: + // Iterate in reverse order when we are wanting to move elements down. + // If we didn't do this, the end result would be the moved elements are reversed. + for (int i = elements.Length - 1; i >= 0; --i) + { + ProjectItemElement element = elements[i]; - switch (moveAction) - { - case MoveAction.Above: - foreach (ProjectItemElement element in elements) + ProjectElementContainer elementParent = element.Parent; + if (elementParent is not null) { - ProjectElementContainer elementParent = element.Parent; - if (elementParent is not null) - { - elementParent.RemoveChild(element); - parent.InsertBeforeChild(element, referenceElement); - } - else - { - didAllElementsMove = false; - } + elementParent.RemoveChild(element); + parent.InsertAfterChild(element, referenceElement); } - break; - - case MoveAction.Below: - // Iterate in reverse order when we are wanting to move elements down. - // If we didn't do this, the end result would be the moved elements are reversed. - for (int i = elements.Length - 1; i >= 0; --i) + else { - ProjectItemElement element = elements[i]; - - ProjectElementContainer elementParent = element.Parent; - if (elementParent is not null) - { - elementParent.RemoveChild(element); - parent.InsertAfterChild(element, referenceElement); - } - else - { - didAllElementsMove = false; - } + didAllElementsMove = false; } - break; - } - - return didAllElementsMove; + } + break; } - /// - /// Move project elements based on the given project tree, reference project tree and move action. - /// Will modify the project if successful, but not save; only dirty. - /// - private static bool TryMove(Project project, IProjectTree projectTree, IProjectTree? referenceProjectTree, MoveAction moveAction) - { - if (!HasValidDisplayOrder(projectTree) || !HasValidDisplayOrder(referenceProjectTree)) - { - return false; - } - - if (projectTree == referenceProjectTree) - { - return false; - } - - if (referenceProjectTree is not null) - { - // The reference element is the element for which moved items will be above or below it. - ProjectItemElement? referenceElement = TryGetReferenceElement(project, referenceProjectTree, ImmutableArray.Empty, moveAction); - - if (referenceElement is not null) - { - ImmutableArray elements = GetItemElements(project, projectTree, ImmutableArray.Empty); - return TryMoveElements(elements, referenceElement, moveAction); - } - } + return didAllElementsMove; + } + /// + /// Move project elements based on the given project tree, reference project tree and move action. + /// Will modify the project if successful, but not save; only dirty. + /// + private static bool TryMove(Project project, IProjectTree projectTree, IProjectTree? referenceProjectTree, MoveAction moveAction) + { + if (!HasValidDisplayOrder(projectTree) || !HasValidDisplayOrder(referenceProjectTree)) + { return false; } - /// - /// Move project elements based on the given project tree and move action. - /// Will modify the project if successful, but not save; only dirty. - /// - private static bool TryMove(Project project, IProjectTree projectTree, MoveAction moveAction) + if (projectTree == referenceProjectTree) { - // Determine what sibling we want to look at based on if we are moving up or down. - IProjectTree? sibling = GetSiblingByMoveAction(projectTree, moveAction); - return TryMove(project, projectTree, sibling, moveAction); + return false; } - private static ImmutableArray GetAddedItemElements(ImmutableHashSet previousIncludes, Project project) + if (referenceProjectTree is not null) { - return project.AllEvaluatedItems - // We are excluding folder elements until CPS allows empty folders to be part of the order; when they do, we can omit checking the item type for "Folder". - // Related changes will also need to happen in TryMoveElementsToTop when CPS allows empty folders in ordering. - // Don't choose items that were imported. Most likely won't happen on added elements, but just in case for sanity. - .Where(x => !previousIncludes.Contains(x.EvaluatedInclude, StringComparers.ItemNames) && !x.ItemType.Equals("Folder", StringComparisons.ItemTypes) && !x.IsImported) - .Select(x => x.Xml) - .ToImmutableArray(); + // The reference element is the element for which moved items will be above or below it. + ProjectItemElement? referenceElement = TryGetReferenceElement(project, referenceProjectTree, ImmutableArray.Empty, moveAction); + + if (referenceElement is not null) + { + ImmutableArray elements = GetItemElements(project, projectTree, ImmutableArray.Empty); + return TryMoveElements(elements, referenceElement, moveAction); + } } + + return false; + } + + /// + /// Move project elements based on the given project tree and move action. + /// Will modify the project if successful, but not save; only dirty. + /// + private static bool TryMove(Project project, IProjectTree projectTree, MoveAction moveAction) + { + // Determine what sibling we want to look at based on if we are moving up or down. + IProjectTree? sibling = GetSiblingByMoveAction(projectTree, moveAction); + return TryMove(project, projectTree, sibling, moveAction); + } + + private static ImmutableArray GetAddedItemElements(ImmutableHashSet previousIncludes, Project project) + { + return project.AllEvaluatedItems + // We are excluding folder elements until CPS allows empty folders to be part of the order; when they do, we can omit checking the item type for "Folder". + // Related changes will also need to happen in TryMoveElementsToTop when CPS allows empty folders in ordering. + // Don't choose items that were imported. Most likely won't happen on added elements, but just in case for sanity. + .Where(x => !previousIncludes.Contains(x.EvaluatedInclude, StringComparers.ItemNames) && !x.ItemType.Equals("Folder", StringComparisons.ItemTypes) && !x.IsImported) + .Select(x => x.Xml) + .ToImmutableArray(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingMoveAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingMoveAction.cs index 5da612b6a6..8fda93be09 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingMoveAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/OrderingMoveAction.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +internal enum OrderingMoveAction { - internal enum OrderingMoveAction - { - NoOp = 0, - MoveToTop = 1, - MoveAbove = 2, - MoveBelow = 3 - } + NoOp = 0, + MoveToTop = 1, + MoveAbove = 2, + MoveBelow = 3 } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/PasteOrdering.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/PasteOrdering.cs index 74a7e6f611..e7b3fca7d5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/PasteOrdering.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/Ordering/PasteOrdering.cs @@ -1,117 +1,116 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +/// +/// This does proper file ordering for pasting or dropping items into a folder or project. +/// +[Export(typeof(IPasteDataObjectProcessor))] +[Export(typeof(IPasteHandler))] +[AppliesTo(ProjectCapabilities.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] +[Order(OrderPrecedence)] +internal class PasteOrdering : IPasteHandler, IPasteDataObjectProcessor { - /// - /// This does proper file ordering for pasting or dropping items into a folder or project. - /// - [Export(typeof(IPasteDataObjectProcessor))] - [Export(typeof(IPasteHandler))] - [AppliesTo(ProjectCapabilities.SortByDisplayOrder + " & " + ProjectCapability.EditableDisplayOrder)] - [Order(OrderPrecedence)] - internal class PasteOrdering : IPasteHandler, IPasteDataObjectProcessor - { - public const int OrderPrecedence = 10000; + public const int OrderPrecedence = 10000; - private readonly IActiveConfiguredValue _configuredProject; - private readonly IProjectAccessor _accessor; + private readonly IActiveConfiguredValue _configuredProject; + private readonly IProjectAccessor _accessor; - private IProjectTree? _dropTarget; + private IProjectTree? _dropTarget; - [ImportingConstructor] - public PasteOrdering(UnconfiguredProject unconfiguredProject, IActiveConfiguredValue configuredProject, IProjectAccessor accessor) - { - _configuredProject = configuredProject; - _accessor = accessor; + [ImportingConstructor] + public PasteOrdering(UnconfiguredProject unconfiguredProject, IActiveConfiguredValue configuredProject, IProjectAccessor accessor) + { + _configuredProject = configuredProject; + _accessor = accessor; - PasteHandlers = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: unconfiguredProject); - PasteProcessors = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: unconfiguredProject); - } + PasteHandlers = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: unconfiguredProject); + PasteProcessors = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: unconfiguredProject); + } - [ImportMany] - private OrderPrecedenceImportCollection PasteHandlers { get; } + [ImportMany] + private OrderPrecedenceImportCollection PasteHandlers { get; } - [ImportMany] - private OrderPrecedenceImportCollection PasteProcessors { get; } + [ImportMany] + private OrderPrecedenceImportCollection PasteProcessors { get; } - private IPasteHandler PasteHandler + private IPasteHandler PasteHandler + { + get { - get - { - // Grab the paste handler that has the highest order precedence that is below PasteOrdering's order precedence. - IPasteHandler pasteHandler = - PasteHandlers.Where(x => x.Metadata.OrderPrecedence < OrderPrecedence) - .OrderByDescending(x => x.Metadata.OrderPrecedence).First().Value; - - return pasteHandler; - } - } + // Grab the paste handler that has the highest order precedence that is below PasteOrdering's order precedence. + IPasteHandler pasteHandler = + PasteHandlers.Where(x => x.Metadata.OrderPrecedence < OrderPrecedence) + .OrderByDescending(x => x.Metadata.OrderPrecedence).First().Value; - private IPasteDataObjectProcessor PasteProcessor - { - get - { - // Grab the paste processor that has the highest order precedence that is below PasteOrdering's order precedence. - IPasteDataObjectProcessor pasteProcessor = - PasteProcessors.Where(x => x.Metadata.OrderPrecedence < OrderPrecedence) - .OrderByDescending(x => x.Metadata.OrderPrecedence).First().Value; - - return pasteProcessor; - } + return pasteHandler; } + } - public bool CanHandleDataObject(object dataObject, IProjectTree dropTarget, IProjectTreeProvider currentProvider) + private IPasteDataObjectProcessor PasteProcessor + { + get { - _dropTarget = dropTarget; - return PasteProcessor.CanHandleDataObject(dataObject, dropTarget, currentProvider); - } + // Grab the paste processor that has the highest order precedence that is below PasteOrdering's order precedence. + IPasteDataObjectProcessor pasteProcessor = + PasteProcessors.Where(x => x.Metadata.OrderPrecedence < OrderPrecedence) + .OrderByDescending(x => x.Metadata.OrderPrecedence).First().Value; - public Task?> ProcessDataObjectAsync(object dataObject, IProjectTree dropTarget, IProjectTreeProvider currentProvider, DropEffects effect) - { - _dropTarget = dropTarget; - return PasteProcessor.ProcessDataObjectAsync(dataObject, dropTarget, currentProvider, effect); + return pasteProcessor; } + } - public DropEffects? QueryDropEffect(object dataObject, int grfKeyState, bool draggedFromThisProject) - { - return PasteProcessor.QueryDropEffect(dataObject, grfKeyState, draggedFromThisProject); - } + public bool CanHandleDataObject(object dataObject, IProjectTree dropTarget, IProjectTreeProvider currentProvider) + { + _dropTarget = dropTarget; + return PasteProcessor.CanHandleDataObject(dataObject, dropTarget, currentProvider); + } - public Task ProcessPostFilterAsync(IEnumerable items) - { - return PasteProcessor.ProcessPostFilterAsync(items); - } + public Task?> ProcessDataObjectAsync(object dataObject, IProjectTree dropTarget, IProjectTreeProvider currentProvider, DropEffects effect) + { + _dropTarget = dropTarget; + return PasteProcessor.ProcessDataObjectAsync(dataObject, dropTarget, currentProvider, effect); + } - public bool CanHandleItem(Type itemType) - { - return PasteHandler.CanHandleItem(itemType); - } + public DropEffects? QueryDropEffect(object dataObject, int grfKeyState, bool draggedFromThisProject) + { + return PasteProcessor.QueryDropEffect(dataObject, grfKeyState, draggedFromThisProject); + } - public void FilterItemList(IEnumerable items, DropEffects effect) - { - PasteHandler.FilterItemList(items, effect); - } + public Task ProcessPostFilterAsync(IEnumerable items) + { + return PasteProcessor.ProcessPostFilterAsync(items); + } - public async Task PasteItemsAsync(IEnumerable items, DropEffects effect) - { - Assumes.NotNull(_dropTarget); + public bool CanHandleItem(Type itemType) + { + return PasteHandler.CanHandleItem(itemType); + } - ImmutableHashSet previousIncludes = await OrderingHelper.GetAllEvaluatedIncludesAsync(_configuredProject.Value, _accessor); - PasteItemsResult result = await PasteHandler.PasteItemsAsync(items, effect); + public void FilterItemList(IEnumerable items, DropEffects effect) + { + PasteHandler.FilterItemList(items, effect); + } - await OrderingHelper.MoveAsync(_configuredProject.Value, _accessor, previousIncludes, _dropTarget, OrderingMoveAction.MoveToTop); + public async Task PasteItemsAsync(IEnumerable items, DropEffects effect) + { + Assumes.NotNull(_dropTarget); - return result; - } + ImmutableHashSet previousIncludes = await OrderingHelper.GetAllEvaluatedIncludesAsync(_configuredProject.Value, _accessor); + PasteItemsResult result = await PasteHandler.PasteItemsAsync(items, effect); - public bool PromptForAnyOverwrites(IEnumerable items, ref DropEffects effect) - { - return PasteHandler.PromptForAnyOverwrites(items, ref effect); - } + await OrderingHelper.MoveAsync(_configuredProject.Value, _accessor, previousIncludes, _dropTarget, OrderingMoveAction.MoveToTop); - public Task> ValidateItemListAsync(IEnumerable items, DropEffects effect) - { - return PasteHandler.ValidateItemListAsync(items, effect); - } + return result; + } + + public bool PromptForAnyOverwrites(IEnumerable items, ref DropEffects effect) + { + return PasteHandler.PromptForAnyOverwrites(items, ref effect); + } + + public Task> ValidateItemListAsync(IEnumerable items, DropEffects effect) + { + return PasteHandler.ValidateItemListAsync(items, effect); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/PackageCommandRegistrationService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/PackageCommandRegistrationService.cs index c9d77dfb4d..dad78ba820 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/PackageCommandRegistrationService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/PackageCommandRegistrationService.cs @@ -4,40 +4,39 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +/// +/// Registers VS menu commands provided by the managed language project system package in. +/// +[Export(typeof(IPackageService))] +internal sealed class PackageCommandRegistrationService : IPackageService { /// - /// Registers VS menu commands provided by the managed language project system package in. + /// implementations may export themselves with this contract name + /// to be automatically added when the managed-language project system package initializes. /// - [Export(typeof(IPackageService))] - internal sealed class PackageCommandRegistrationService : IPackageService - { - /// - /// implementations may export themselves with this contract name - /// to be automatically added when the managed-language project system package initializes. - /// - public const string PackageCommandContract = "ManagedPackageCommand"; + public const string PackageCommandContract = "ManagedPackageCommand"; - private readonly JoinableTaskContext _context; - private readonly IEnumerable _commands; + private readonly JoinableTaskContext _context; + private readonly IEnumerable _commands; - [ImportingConstructor] - public PackageCommandRegistrationService([ImportMany(PackageCommandContract)] IEnumerable commands, JoinableTaskContext context) - { - _commands = commands; - _context = context; - } + [ImportingConstructor] + public PackageCommandRegistrationService([ImportMany(PackageCommandContract)] IEnumerable commands, JoinableTaskContext context) + { + _commands = commands; + _context = context; + } - public async Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) - { - _context.VerifyIsOnMainThread(); + public async Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) + { + _context.VerifyIsOnMainThread(); - IMenuCommandService menuCommandService = await asyncServiceProvider.GetServiceAsync(); + IMenuCommandService menuCommandService = await asyncServiceProvider.GetServiceAsync(); - foreach (MenuCommand menuCommand in _commands) - { - menuCommandService.AddCommand(menuCommand); - } + foreach (MenuCommand menuCommand in _commands) + { + menuCommandService.AddCommand(menuCommand); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/TemplateDetailsExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/TemplateDetailsExtensions.cs index 7d2cc04c28..48a8a3a431 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/TemplateDetailsExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/TemplateDetailsExtensions.cs @@ -2,47 +2,46 @@ using static Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.AbstractAddItemCommandHandler; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +internal static class TemplateDetailsExtensions { - internal static class TemplateDetailsExtensions + /// + /// Creates a TemplateDetails record where both resources come from a single package + /// + public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, Guid resourcePackage, Enum dirNameId, Enum templateNameId) { - /// - /// Creates a TemplateDetails record where both resources come from a single package - /// - public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, Guid resourcePackage, Enum dirNameId, Enum templateNameId) - { - return CreateTemplateDetails(map, commandId, capability, resourcePackage, dirNameId, resourcePackage, templateNameId); - } + return CreateTemplateDetails(map, commandId, capability, resourcePackage, dirNameId, resourcePackage, templateNameId); + } - /// - /// Creates a TemplateDetails record where both resources come from a single package, and combines the two capabilities with & before checking. - /// - public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, string extraCapability, Guid resourcePackage, Enum dirNameId, Enum templateNameId) - { - return CreateTemplateDetails(map, commandId, capability + " & " + extraCapability, resourcePackage, dirNameId, resourcePackage, templateNameId); - } + /// + /// Creates a TemplateDetails record where both resources come from a single package, and combines the two capabilities with & before checking. + /// + public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, string extraCapability, Guid resourcePackage, Enum dirNameId, Enum templateNameId) + { + return CreateTemplateDetails(map, commandId, capability + " & " + extraCapability, resourcePackage, dirNameId, resourcePackage, templateNameId); + } - /// - /// Creates a TemplateDetails record by combining the two capabilities with & before checking. - /// - public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, string extraCapability, Guid dirNamePackage, Enum dirNameId, Guid templateNamePackage, Enum templateNameId) - { - return CreateTemplateDetails(map, commandId, capability + " & " + extraCapability, dirNamePackage, dirNameId, templateNamePackage, templateNameId); - } + /// + /// Creates a TemplateDetails record by combining the two capabilities with & before checking. + /// + public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, string extraCapability, Guid dirNamePackage, Enum dirNameId, Guid templateNamePackage, Enum templateNameId) + { + return CreateTemplateDetails(map, commandId, capability + " & " + extraCapability, dirNamePackage, dirNameId, templateNamePackage, templateNameId); + } - /// - /// Creates a new TemplateDetails record - /// - public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, Guid dirNamePackage, Enum dirNameId, Guid templateNamePackage, Enum templateNameId) + /// + /// Creates a new TemplateDetails record + /// + public static ImmutableDictionary> CreateTemplateDetails(this ImmutableDictionary> map, long commandId, string capability, Guid dirNamePackage, Enum dirNameId, Guid templateNamePackage, Enum templateNameId) + { + if (!map.TryGetValue(commandId, out ImmutableArray list)) { - if (!map.TryGetValue(commandId, out ImmutableArray list)) - { - list = ImmutableArray.Empty; - } + list = ImmutableArray.Empty; + } - list = list.Add(new TemplateDetails(capability, dirNamePackage, Convert.ToUInt32(dirNameId), templateNamePackage, Convert.ToUInt32(templateNameId))); + list = list.Add(new TemplateDetails(capability, dirNamePackage, Convert.ToUInt32(dirNameId), templateNamePackage, Convert.ToUInt32(templateNameId))); - return map.SetItem(commandId, list); - } + return map.SetItem(commandId, list); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS2kAddItemCommandHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS2kAddItemCommandHandler.cs index 81f38e6290..cdc6d28e2c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS2kAddItemCommandHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS2kAddItemCommandHandler.cs @@ -4,30 +4,29 @@ using Microsoft.VisualStudio.ProjectSystem.VS.UI; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +[ExportCommandGroup(CommandGroup.VisualStudioStandard2k)] +[AppliesTo(ProjectCapability.DotNet)] +internal class VS2kAddItemCommandHandler : AbstractAddItemCommandHandler { - [ExportCommandGroup(CommandGroup.VisualStudioStandard2k)] - [AppliesTo(ProjectCapability.DotNet)] - internal class VS2kAddItemCommandHandler : AbstractAddItemCommandHandler - { - private static readonly ImmutableDictionary> s_templateDetails = ImmutableDictionary>.Empty - // Command Id Capabilities DirNamePackageGuid DirNameResourceId TemplateNameResourceId - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddForm, ProjectCapability.CSharp, ProjectCapability.WindowsForms, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWWFCWIN32FORM ) - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddUserControl, ProjectCapability.CSharp, ProjectCapability.WindowsForms, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWUSERCONTROL ) - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddComponent, ProjectCapability.CSharp, ProjectCapability.WindowsForms, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWWFCCOMPONENT ) + private static readonly ImmutableDictionary> s_templateDetails = ImmutableDictionary>.Empty + // Command Id Capabilities DirNamePackageGuid DirNameResourceId TemplateNameResourceId + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddForm, ProjectCapability.CSharp, ProjectCapability.WindowsForms, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWWFCWIN32FORM ) + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddUserControl, ProjectCapability.CSharp, ProjectCapability.WindowsForms, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWUSERCONTROL ) + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddComponent, ProjectCapability.CSharp, ProjectCapability.WindowsForms, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWWFCCOMPONENT ) - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddForm, ProjectCapability.VisualBasic, ProjectCapability.WindowsForms, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_WINFORM ) - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddUserControl, ProjectCapability.VisualBasic, ProjectCapability.WindowsForms, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_USERCTRL ) - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddComponent, ProjectCapability.VisualBasic, ProjectCapability.WindowsForms, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_COMPONENT ) + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddForm, ProjectCapability.VisualBasic, ProjectCapability.WindowsForms, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_WINFORM ) + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddUserControl, ProjectCapability.VisualBasic, ProjectCapability.WindowsForms, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_USERCTRL ) + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddComponent, ProjectCapability.VisualBasic, ProjectCapability.WindowsForms, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_COMPONENT ) - .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddModule, ProjectCapability.VisualBasic, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_MODULE ); + .CreateTemplateDetails(VisualStudioStandard2KCommandId.AddModule, ProjectCapability.VisualBasic, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_MODULE ); - protected override ImmutableDictionary> TemplatesByCommandId => s_templateDetails; + protected override ImmutableDictionary> TemplatesByCommandId => s_templateDetails; - [ImportingConstructor] - public VS2kAddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) - : base(configuredProject, addItemDialogService, vsShell) - { - } + [ImportingConstructor] + public VS2kAddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) + : base(configuredProject, addItemDialogService, vsShell) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS97AddItemCommandHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS97AddItemCommandHandler.cs index abd9fbbe4a..682b20266f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS97AddItemCommandHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/VS97AddItemCommandHandler.cs @@ -4,24 +4,23 @@ using Microsoft.VisualStudio.ProjectSystem.VS.UI; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +[ExportCommandGroup(CommandGroup.VisualStudioStandard97)] +[AppliesTo(ProjectCapability.DotNet)] +internal class VS97AddItemCommandHandler : AbstractAddItemCommandHandler { - [ExportCommandGroup(CommandGroup.VisualStudioStandard97)] - [AppliesTo(ProjectCapability.DotNet)] - internal class VS97AddItemCommandHandler : AbstractAddItemCommandHandler - { - private static readonly ImmutableDictionary> s_templateDetails = ImmutableDictionary>.Empty - // Command Id Capability DirNamePackageGuid DirNameResourceId TemplateNameResourceId - .CreateTemplateDetails(VisualStudioStandard97CommandId.AddClass, ProjectCapability.CSharp, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWCSharpCLASS ) + private static readonly ImmutableDictionary> s_templateDetails = ImmutableDictionary>.Empty + // Command Id Capability DirNamePackageGuid DirNameResourceId TemplateNameResourceId + .CreateTemplateDetails(VisualStudioStandard97CommandId.AddClass, ProjectCapability.CSharp, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_PROJECTITEMTYPE_STR, LegacyCSharpStringResourceIds.IDS_TEMPLATE_NEWCSharpCLASS ) - .CreateTemplateDetails(VisualStudioStandard97CommandId.AddClass, ProjectCapability.VisualBasic, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_CLASS ); + .CreateTemplateDetails(VisualStudioStandard97CommandId.AddClass, ProjectCapability.VisualBasic, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_VBPROJECTFILES, LegacyVBStringResourceIds.IDS_VSDIR_ITEM_CLASS ); - protected override ImmutableDictionary> TemplatesByCommandId => s_templateDetails; + protected override ImmutableDictionary> TemplatesByCommandId => s_templateDetails; - [ImportingConstructor] - public VS97AddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) - : base(configuredProject, addItemDialogService, vsShell) - { - } + [ImportingConstructor] + public VS97AddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) + : base(configuredProject, addItemDialogService, vsShell) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/WPFAddItemCommandHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/WPFAddItemCommandHandler.cs index 0a71241200..645af6a7eb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/WPFAddItemCommandHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Input/Commands/WPFAddItemCommandHandler.cs @@ -4,42 +4,41 @@ using Microsoft.VisualStudio.ProjectSystem.VS.UI; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +[ExportCommandGroup(CommandGroup.WPF)] +[AppliesTo(ProjectCapability.DotNet)] +internal class WPFAddItemCommandHandler : AbstractAddItemCommandHandler { - [ExportCommandGroup(CommandGroup.WPF)] - [AppliesTo(ProjectCapability.DotNet)] - internal class WPFAddItemCommandHandler : AbstractAddItemCommandHandler - { - private static readonly Guid s_wpfPackage = new("{b3bae735-386c-4030-8329-ef48eeda4036}"); + private static readonly Guid s_wpfPackage = new("{b3bae735-386c-4030-8329-ef48eeda4036}"); - private static readonly ImmutableDictionary> s_templateDetails = ImmutableDictionary>.Empty - // Command Id Capabilities DirNamePackageGuid DirNameResourceId TemplateName TemplateNameResourceId - // PackageGuid - .CreateTemplateDetails(WPFCommandId.AddWPFWindow, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) - .CreateTemplateDetails(WPFCommandId.AddWPFWindow, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) - .CreateTemplateDetails(WPFCommandId.WPFWindow, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) - .CreateTemplateDetails(WPFCommandId.AddWPFPage, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) - .CreateTemplateDetails(WPFCommandId.WPFPage, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) - .CreateTemplateDetails(WPFCommandId.AddWPFUserControl, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) - .CreateTemplateDetails(WPFCommandId.WPFUserControl, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) - .CreateTemplateDetails(WPFCommandId.AddWPFResourceDictionary, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ) - .CreateTemplateDetails(WPFCommandId.WPFResourceDictionary, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ) + private static readonly ImmutableDictionary> s_templateDetails = ImmutableDictionary>.Empty + // Command Id Capabilities DirNamePackageGuid DirNameResourceId TemplateName TemplateNameResourceId + // PackageGuid + .CreateTemplateDetails(WPFCommandId.AddWPFWindow, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) + .CreateTemplateDetails(WPFCommandId.AddWPFWindow, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) + .CreateTemplateDetails(WPFCommandId.WPFWindow, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) + .CreateTemplateDetails(WPFCommandId.AddWPFPage, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) + .CreateTemplateDetails(WPFCommandId.WPFPage, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) + .CreateTemplateDetails(WPFCommandId.AddWPFUserControl, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) + .CreateTemplateDetails(WPFCommandId.WPFUserControl, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) + .CreateTemplateDetails(WPFCommandId.AddWPFResourceDictionary, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ) + .CreateTemplateDetails(WPFCommandId.WPFResourceDictionary, ProjectCapability.CSharp, ProjectCapability.WPF, LegacyCSharpPackageGuid, LegacyCSharpStringResourceIds.IDS_TEMPLATE_DIRLOCALITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ) - .CreateTemplateDetails(WPFCommandId.AddWPFWindow, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) - .CreateTemplateDetails(WPFCommandId.WPFWindow, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) - .CreateTemplateDetails(WPFCommandId.AddWPFPage, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) - .CreateTemplateDetails(WPFCommandId.WPFPage, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) - .CreateTemplateDetails(WPFCommandId.AddWPFUserControl, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) - .CreateTemplateDetails(WPFCommandId.WPFUserControl, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) - .CreateTemplateDetails(WPFCommandId.AddWPFResourceDictionary, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ) - .CreateTemplateDetails(WPFCommandId.WPFResourceDictionary, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ); + .CreateTemplateDetails(WPFCommandId.AddWPFWindow, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) + .CreateTemplateDetails(WPFCommandId.WPFWindow, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFWindow ) + .CreateTemplateDetails(WPFCommandId.AddWPFPage, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) + .CreateTemplateDetails(WPFCommandId.WPFPage, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFPage ) + .CreateTemplateDetails(WPFCommandId.AddWPFUserControl, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) + .CreateTemplateDetails(WPFCommandId.WPFUserControl, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFUserControl ) + .CreateTemplateDetails(WPFCommandId.AddWPFResourceDictionary, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ) + .CreateTemplateDetails(WPFCommandId.WPFResourceDictionary, ProjectCapability.VisualBasic, ProjectCapability.WPF, LegacyVBPackageGuid, LegacyVBStringResourceIds.IDS_VSDIR_CLIENTPROJECTITEMS, s_wpfPackage, WPFTemplateNames.WPFResourceDictionary ); - protected override ImmutableDictionary> TemplatesByCommandId => s_templateDetails; + protected override ImmutableDictionary> TemplatesByCommandId => s_templateDetails; - [ImportingConstructor] - public WPFAddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) - : base(configuredProject, addItemDialogService, vsShell) - { - } + [ImportingConstructor] + public WPFAddItemCommandHandler(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) + : base(configuredProject, addItemDialogService, vsShell) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Interop/IVsAppId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Interop/IVsAppId.cs index 82ddb616b3..3b068a42c7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Interop/IVsAppId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Interop/IVsAppId.cs @@ -3,229 +3,228 @@ using System.Runtime.InteropServices; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Interop +namespace Microsoft.VisualStudio.ProjectSystem.VS.Interop; + +[Guid("1EAA526A-0898-11d3-B868-00C04F79F802"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface IVsAppId { - [Guid("1EAA526A-0898-11d3-B868-00C04F79F802"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IVsAppId - { - [PreserveSig] - int SetSite(IOleServiceProvider pSP); + [PreserveSig] + int SetSite(IOleServiceProvider pSP); - [PreserveSig] - int GetProperty(int propid, // VSAPROPID - [MarshalAs(UnmanagedType.Struct)] out object pvar); + [PreserveSig] + int GetProperty(int propid, // VSAPROPID + [MarshalAs(UnmanagedType.Struct)] out object pvar); - [PreserveSig] - int SetProperty(int propid, //[in] VSAPROPID - [MarshalAs(UnmanagedType.Struct)] object var); + [PreserveSig] + int SetProperty(int propid, //[in] VSAPROPID + [MarshalAs(UnmanagedType.Struct)] object var); - [PreserveSig] - int GetGuidProperty(int propid, // VSAPROPID - out Guid guid); + [PreserveSig] + int GetGuidProperty(int propid, // VSAPROPID + out Guid guid); - [PreserveSig] - int SetGuidProperty(int propid, // [in] VSAPROPID - ref Guid rguid); + [PreserveSig] + int SetGuidProperty(int propid, // [in] VSAPROPID + ref Guid rguid); - [PreserveSig] - int Initialize(); // called after main initialization and before command executing and entering main loop - } + [PreserveSig] + int Initialize(); // called after main initialization and before command executing and entering main loop +} - internal enum VSAPropID - { - NIL = -1, - LAST = -8500, // !!!! NOTE !!!! THIS MUST BE THE SAME AS THE FIRST PROP DEFINED - GuidAppIDPackage = -8501, // GUID of the Application ID Package; e.g. this is used to load resource strings. - AppName = -8502, // BSTR or I4 localize name of the App used in title bar. - // (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) - CmdLineOptDialog = -8503, // I4 - Command Line Options dialog resource id in appid package satellite dll - HideSolutionConcept = -8504, // BOOL - default FALSE. TRUE if appid uses the solution, but does show it to the user - // !!! can be called before main initialization happen - ShowStartupDialogs = -8505, // BOOL - default TRUE - ShowIDE = -8506, // BOOL - default TRUE - // !!! can be called before main initialization happen - ShowHierarchyRootInTitle = -8507, // BOOL - default TRUE - SolutionFileExt = -8508, // BSTR - solution file extension (default - ".sln"); - UserOptsFileExt = -8509, // BSTR - solution options file extension (default - ".suo"); - AltMSODLL = -8510, // BSTR - path/filename for alternate MSOx DLL (default - ask MSI), exactly as passed to LoadLibrary - CreateProjShortcuts = -8511, // BOOL - default TRUE should shortcuts to solutions/projects be added to 'recent' folder? - AppIcon = -8512, // I4 - HICON for 32x32 icon - AppSmallIcon = -8513, // I4 - HICON for 16x16 icon - DefaultHomePage = -8514, // BSTR - default Home page URL (for Web browser) - DefaultSearchPage = -8515, // BSTR - default Search page URL (for Web browser) - WBExternalObject = -8516, // IDispatch * (for IDocHostUIHandler::GetExternal), default SApplicationObject - AppShortName = -8517, // BSTR or I4 localize name of the short version of the App name, less than 32 chars. - ClsidAppIdServer = -8518, // CLSID under which we're registered as a JIT Debug or Attach Server - GuidGeneralOutput = -8519, // GUID of the "General" output window for the shell. First request for this creates it. - UseDebugLaunchService = -8520, // default FALSE. TRUE if debugger should use SVsDebugLaunch launch service - GuidDefaultDebugEngine = -8521, // GUID of the default debug engine for this appid - CmdLineOptStrFirst = -8522, // I4 - beginning of res id range of the Command Line Options string resource(s) in appid package - // satellite dll. used instead of CmdLineOptDialog when output piped to console - CmdLineOptStrLast = -8523, // I4 - end of res id range of the Command Line Options string resource(s) in appid package - // satellite dll. used instead of CmdLineOptDialog when output piped to console - IsRegisteredAsRuntimeJITDebugger = -8524, // used to register as a runtime JIT Debugger - PersistProjExplorerState = -8525, // BOOL - default is TRUE. Persists expansion state of the project explorer - PredefinedAliasesID = -8526, // Deprecated -- use PredefinedAliasesString instead - // (was I4 - resource id in appid package satellite dll of predefined aliases text) - DisableDynamicHelp = -8527, // BOOL - default is FALSE. Should the Dynamic Help window be shown on F1 - UsesMRUCommandsOnFileMenu = -8528, // BOOL - default is TRUE. Are the MRU commands on the File menu used? - AllowsDroppedFilesOnMainWindow = -8529, // BOOL - default is TRUE. Should the main window accept dropped files (i.e., WS_EX_ACCEPTFILES)? - DisableAnswerWizardControl = -8530, // BOOL - default is TRUE. Should the AnswerWizard menubar control be disabled? - DisableAnsiCodePageCheck = -8531, // BOOL - default is FALSE. Should the Ansi codepage check be used when loading UI libraries? - DisableInstructionUnitStepping = -8532, // Set to TRUE to disable debugger's support for source-instruction stepping. - UseVisualStudioDialogShortcuts = -8533, // BOOL - default is TRUE. Should the VS shortcuts be used in the Open/Save/Browse dialogs? - SKUEdition = -8534, // Either a VSASKUEdition or a string. VSASKUEdition if it is a standard version, or a BSTR if a custom version. - Logo = -8535, // BSTR - logo for command line - DDEApplication = -8536, // BSTR - application supported in DDE (expected in WM_DDE_INITIATE). Required for DDE support. - DDETopic = -8537, // BSTR - topic supported in DDE (expected in WM_DDE_INITIATE) Required for DDE support. - VSIPLicenseRequired = -8538, // BOOL - default is FALSE. If TRUE, about box puts up stuff about VSIP license required - DropFilesOnMainWindowHandler = -8539, // GUID - package GUID, which implements IVSDropFilesHandler to override default behaviour - CmdLineError = -8540, // BSTR or I4 - error message for invalid cmd line to show before cmd line options help - // (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) - AllowCurrentUserSafeDomains = -8541, // BOOL - default is FALSE. Should security manager add safe domains from HKCU\\VsProtocol\SafeDomains - TechSupportLink = -8542, // BSTR - should be link to tech support for this appid. - HideMiscellaneousFilesByDefault = -8543, // BOOL - default is FALSE. Should the Miscellaneous Files project be hidden by default? - PredefinedAliasesString = -8544, // BSTR - predefined aliases for the appid - ShowRuntimeInAboutBox = -8545, // BOOL - default is FALSE. Should runtime (and runtime ver) show up at the top of about box? - SubSKUEdition = -8546, // I4 - some combination of the bits defined in VSASubSKUEdition or zero (if none). - StatusBarClientText = -8547, // BSTR global (application) scoped text for Client Text field of status bar. - NewProjDlgSlnTreeNodeTitle = -8548, // BSTR or I4 localized replacement name for the 'Visual Studio Solutions' node in the 'Project Types' tree in - // the New Project dialog. (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) - DefaultProjectsLocation = -8549, // BSTR full path to the projects location (overrides the 'Visual Studio Projects' location) - SolutionFileCreatorIdentifier = -8550, // BSTR string used as the second line in the solution file (used for determining SLN double-click behavior) - HideSolutionExplorerToolbar = -8551, // BOOL - default is FALSE. Should the Solution Explorer tool window hide its toolbar? - DefaultUserFilesFolderRoot = -8552, // BSTR name of folder at the end of the default my documents location, e.g. 'Visual Studio' in the default case: '%USERPROFILE%\My Documents\Visual Studio' - UserFilesSubFolderName = -8553, // BSTR name of folder used for appid-specific subfolders under '%USERPROFILE%\My Documents\Visual Studio', e.g. 'Visual Basic Express' for '%USERPROFILE%\My Documents\Visual Studio\Settings\Visual Basic Express' - NewProjDlgInstalledTemplatesHdr = -8554, // BSTR or I4 localized replacement name for the 'Visual Studio installed templates' header in the 'Templates' list - // in the New Project dialog. (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) - IncludeAddNewOnlineTemplateIcon = -8555, // BOOL - default is TRUE. Should the "Add New Online Template" icon be added to the New Project/Item dialogs? - // Note: if this icon is not added then the "My Templates" group only shows up if other user templates are added. - AddinsAllowed = -8556, // VARIANT_BOOL indicating wether Add-ins can be loaded or not. If not implemented, then VARIANT_TRUE is assumed. - App64Icon = -8557, // I4 - HICON for 64x64 icon - UseAutoRecovery = -8558, // BOOL - default is TRUE. In order to turn off AutoRecovery, an AppID should implement this - // propid and set its value to FALSE. - DisableOutputWindow = -8559, // VARIANT_BOOL indicating whether shell should treat the output window as disabled. Returning VARIANT_TRUE means that - // solution build manager will not try to output anything into the output window and 'Show Output window when build starts' will be hidden - // in the Options dialog. Default value is VARIANT_FALSE. - DisableStartPage = -8560, // VARIANT_BOOL indicating whether we should disable the start page in the shell - StartPageTheme = -8561, // INT_PTR pointing to the memory containing the VSSTARTPAGETHEME struct. The memory should be allocated and de-allocated at the - // appid implementation level. - LicenseGUID = -8562, // Returns the highest applicable license GUID, if licenses are required. - // If no licenses are required, returns E_NOTIMPL. - RegistrationDlgBanner = -8563, // The banner on top of the Registration and Trial dialogs. - AutoRecoveryTookPlace = -8564, // VARIANT_BOOL indicating if an AutoRecovery took place. The default value is VARIANT_FALSE, and it is set to VARAINT_TRUE - // iff an AutoRecovery happened, before the DTEEvents::OnStartupComplete event is fired. An AppID should query this value - // if they need to know if a Recovery took place to change their startup action, for example - AboutBoxTheme = -8565, // INT_PTR pointing to the memory containing the VSABOUTBOXTHEME struct. The memory should be allocated and de-allocated at the - // appid implementation level. - SQMTitle = -8566, // BSTR Title for the SQM optin dialog. - AutoSaveNewUnsavedFiles = -8567, // VARIANT_BOOL indicating whether the disaster recovery mechanism should save previously unsaved files. The default is FALSE. - Preview = -8568, // I4 Enumeration indicating whether this is: - // 0: full release - // 1: CTP - // 2: Beta - // 3: RC - DaysUntilExpiration = -8569, // I4 Days until expiration: - // days - // -1 if already expired. - // if this is a full release, ie. not expiring, always returns 0 - ReleaseString = -8570, // BSTR what this release is branded as, e.g. November CTP, Beta 2, etc. - ReleaseString_Short = -8571, // BSTR what this release is branded as, e.g. November CTP, Beta 2, etc. - RegistryRoots = -8572, // SafeArray of BSTRs, in order from earliest to latest - DisableUACSupport = -8573, - RunAsNormalUser = -8574, // VT_BOOL. TRUE if machine-wide registry values should be moved under HKEY_CURRENT_USER - // and common app-data files are written under per-user app-data with a - // "Configuration" or "UserSettings" subkey/subfolder - ConfigurationRoot = -8575, // VT_BSTR Alternative registry root to use when user settings and machine configuration - // need to be different. If not implemented, then use the default registry root. - DontWriteToUserAppData = -8576, // VT_BOOL. TRUE if we should not write to the user's appdata folder - // This might be TRUE in a "kiosk" application where the application leaves no - // trace of the user behind on the machine. - SQMLogFile = -8577, // BSTR full name of the SQM log created for the current session. - SupportRestartManager = -8578, // VT_BOOL (default is TRUE). In order to turn off support for Restart Manager, an AppID should implement this - SamplesURL = -8579, // BSTR URL to show in the internal web browser for Help - Samples command - AppDataDir = -8580, // BSTR (Remote) application data directory - LocalAppDataDir = -8581, // BSTR Local application data directory - CommonAppDataDir = -8582, // BSTR common (all users) application data directory - ConfigurationTimestampUtc = -8583, // VT_DATE value that represents the last time the configuration cache was built - // of Visual Studio was initializing - CommonExtensionSearchPath = -8584, // SafeArray of BSTRs. APPID specific list of folders where to look for Common (shared by all users) VS extensions. - // VS Extension Manager looks under these locations for VSIX manifest files. - UserExtensionsRootFolder = -8585, // BSTR. APPID specific folder path for User extensions. VS Extension Manager - // looks under this location for VSIX manifest files. - LoadUserExtensions = -8586, // VT_BOOL. Tells PkgDef management and Extension Manager API whether to load User extensions. - // This property is calculated based on the security logic of extension management and user preferrences. - LoadedUserExtensions = -8587, // SafeArray of BSTRs. List of folders that were searched for enabled user extensions. - // These are the essentially the user extensions that were enabled when the appid initialized. - AllowLoadingAllPackages = -8588, // VT_BOOL. Each APPID specifies through this property if it allows loading ALL Visual Studio Packages - // without PLK checking. Default is FALSE. +internal enum VSAPropID +{ + NIL = -1, + LAST = -8500, // !!!! NOTE !!!! THIS MUST BE THE SAME AS THE FIRST PROP DEFINED + GuidAppIDPackage = -8501, // GUID of the Application ID Package; e.g. this is used to load resource strings. + AppName = -8502, // BSTR or I4 localize name of the App used in title bar. + // (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) + CmdLineOptDialog = -8503, // I4 - Command Line Options dialog resource id in appid package satellite dll + HideSolutionConcept = -8504, // BOOL - default FALSE. TRUE if appid uses the solution, but does show it to the user + // !!! can be called before main initialization happen + ShowStartupDialogs = -8505, // BOOL - default TRUE + ShowIDE = -8506, // BOOL - default TRUE + // !!! can be called before main initialization happen + ShowHierarchyRootInTitle = -8507, // BOOL - default TRUE + SolutionFileExt = -8508, // BSTR - solution file extension (default - ".sln"); + UserOptsFileExt = -8509, // BSTR - solution options file extension (default - ".suo"); + AltMSODLL = -8510, // BSTR - path/filename for alternate MSOx DLL (default - ask MSI), exactly as passed to LoadLibrary + CreateProjShortcuts = -8511, // BOOL - default TRUE should shortcuts to solutions/projects be added to 'recent' folder? + AppIcon = -8512, // I4 - HICON for 32x32 icon + AppSmallIcon = -8513, // I4 - HICON for 16x16 icon + DefaultHomePage = -8514, // BSTR - default Home page URL (for Web browser) + DefaultSearchPage = -8515, // BSTR - default Search page URL (for Web browser) + WBExternalObject = -8516, // IDispatch * (for IDocHostUIHandler::GetExternal), default SApplicationObject + AppShortName = -8517, // BSTR or I4 localize name of the short version of the App name, less than 32 chars. + ClsidAppIdServer = -8518, // CLSID under which we're registered as a JIT Debug or Attach Server + GuidGeneralOutput = -8519, // GUID of the "General" output window for the shell. First request for this creates it. + UseDebugLaunchService = -8520, // default FALSE. TRUE if debugger should use SVsDebugLaunch launch service + GuidDefaultDebugEngine = -8521, // GUID of the default debug engine for this appid + CmdLineOptStrFirst = -8522, // I4 - beginning of res id range of the Command Line Options string resource(s) in appid package + // satellite dll. used instead of CmdLineOptDialog when output piped to console + CmdLineOptStrLast = -8523, // I4 - end of res id range of the Command Line Options string resource(s) in appid package + // satellite dll. used instead of CmdLineOptDialog when output piped to console + IsRegisteredAsRuntimeJITDebugger = -8524, // used to register as a runtime JIT Debugger + PersistProjExplorerState = -8525, // BOOL - default is TRUE. Persists expansion state of the project explorer + PredefinedAliasesID = -8526, // Deprecated -- use PredefinedAliasesString instead + // (was I4 - resource id in appid package satellite dll of predefined aliases text) + DisableDynamicHelp = -8527, // BOOL - default is FALSE. Should the Dynamic Help window be shown on F1 + UsesMRUCommandsOnFileMenu = -8528, // BOOL - default is TRUE. Are the MRU commands on the File menu used? + AllowsDroppedFilesOnMainWindow = -8529, // BOOL - default is TRUE. Should the main window accept dropped files (i.e., WS_EX_ACCEPTFILES)? + DisableAnswerWizardControl = -8530, // BOOL - default is TRUE. Should the AnswerWizard menubar control be disabled? + DisableAnsiCodePageCheck = -8531, // BOOL - default is FALSE. Should the Ansi codepage check be used when loading UI libraries? + DisableInstructionUnitStepping = -8532, // Set to TRUE to disable debugger's support for source-instruction stepping. + UseVisualStudioDialogShortcuts = -8533, // BOOL - default is TRUE. Should the VS shortcuts be used in the Open/Save/Browse dialogs? + SKUEdition = -8534, // Either a VSASKUEdition or a string. VSASKUEdition if it is a standard version, or a BSTR if a custom version. + Logo = -8535, // BSTR - logo for command line + DDEApplication = -8536, // BSTR - application supported in DDE (expected in WM_DDE_INITIATE). Required for DDE support. + DDETopic = -8537, // BSTR - topic supported in DDE (expected in WM_DDE_INITIATE) Required for DDE support. + VSIPLicenseRequired = -8538, // BOOL - default is FALSE. If TRUE, about box puts up stuff about VSIP license required + DropFilesOnMainWindowHandler = -8539, // GUID - package GUID, which implements IVSDropFilesHandler to override default behaviour + CmdLineError = -8540, // BSTR or I4 - error message for invalid cmd line to show before cmd line options help + // (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) + AllowCurrentUserSafeDomains = -8541, // BOOL - default is FALSE. Should security manager add safe domains from HKCU\\VsProtocol\SafeDomains + TechSupportLink = -8542, // BSTR - should be link to tech support for this appid. + HideMiscellaneousFilesByDefault = -8543, // BOOL - default is FALSE. Should the Miscellaneous Files project be hidden by default? + PredefinedAliasesString = -8544, // BSTR - predefined aliases for the appid + ShowRuntimeInAboutBox = -8545, // BOOL - default is FALSE. Should runtime (and runtime ver) show up at the top of about box? + SubSKUEdition = -8546, // I4 - some combination of the bits defined in VSASubSKUEdition or zero (if none). + StatusBarClientText = -8547, // BSTR global (application) scoped text for Client Text field of status bar. + NewProjDlgSlnTreeNodeTitle = -8548, // BSTR or I4 localized replacement name for the 'Visual Studio Solutions' node in the 'Project Types' tree in + // the New Project dialog. (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) + DefaultProjectsLocation = -8549, // BSTR full path to the projects location (overrides the 'Visual Studio Projects' location) + SolutionFileCreatorIdentifier = -8550, // BSTR string used as the second line in the solution file (used for determining SLN double-click behavior) + HideSolutionExplorerToolbar = -8551, // BOOL - default is FALSE. Should the Solution Explorer tool window hide its toolbar? + DefaultUserFilesFolderRoot = -8552, // BSTR name of folder at the end of the default my documents location, e.g. 'Visual Studio' in the default case: '%USERPROFILE%\My Documents\Visual Studio' + UserFilesSubFolderName = -8553, // BSTR name of folder used for appid-specific subfolders under '%USERPROFILE%\My Documents\Visual Studio', e.g. 'Visual Basic Express' for '%USERPROFILE%\My Documents\Visual Studio\Settings\Visual Basic Express' + NewProjDlgInstalledTemplatesHdr = -8554, // BSTR or I4 localized replacement name for the 'Visual Studio installed templates' header in the 'Templates' list + // in the New Project dialog. (either a string or a resource id that is loaded from GuidAppIDPackage UILibrary) + IncludeAddNewOnlineTemplateIcon = -8555, // BOOL - default is TRUE. Should the "Add New Online Template" icon be added to the New Project/Item dialogs? + // Note: if this icon is not added then the "My Templates" group only shows up if other user templates are added. + AddinsAllowed = -8556, // VARIANT_BOOL indicating wether Add-ins can be loaded or not. If not implemented, then VARIANT_TRUE is assumed. + App64Icon = -8557, // I4 - HICON for 64x64 icon + UseAutoRecovery = -8558, // BOOL - default is TRUE. In order to turn off AutoRecovery, an AppID should implement this + // propid and set its value to FALSE. + DisableOutputWindow = -8559, // VARIANT_BOOL indicating whether shell should treat the output window as disabled. Returning VARIANT_TRUE means that + // solution build manager will not try to output anything into the output window and 'Show Output window when build starts' will be hidden + // in the Options dialog. Default value is VARIANT_FALSE. + DisableStartPage = -8560, // VARIANT_BOOL indicating whether we should disable the start page in the shell + StartPageTheme = -8561, // INT_PTR pointing to the memory containing the VSSTARTPAGETHEME struct. The memory should be allocated and de-allocated at the + // appid implementation level. + LicenseGUID = -8562, // Returns the highest applicable license GUID, if licenses are required. + // If no licenses are required, returns E_NOTIMPL. + RegistrationDlgBanner = -8563, // The banner on top of the Registration and Trial dialogs. + AutoRecoveryTookPlace = -8564, // VARIANT_BOOL indicating if an AutoRecovery took place. The default value is VARIANT_FALSE, and it is set to VARAINT_TRUE + // iff an AutoRecovery happened, before the DTEEvents::OnStartupComplete event is fired. An AppID should query this value + // if they need to know if a Recovery took place to change their startup action, for example + AboutBoxTheme = -8565, // INT_PTR pointing to the memory containing the VSABOUTBOXTHEME struct. The memory should be allocated and de-allocated at the + // appid implementation level. + SQMTitle = -8566, // BSTR Title for the SQM optin dialog. + AutoSaveNewUnsavedFiles = -8567, // VARIANT_BOOL indicating whether the disaster recovery mechanism should save previously unsaved files. The default is FALSE. + Preview = -8568, // I4 Enumeration indicating whether this is: + // 0: full release + // 1: CTP + // 2: Beta + // 3: RC + DaysUntilExpiration = -8569, // I4 Days until expiration: + // days + // -1 if already expired. + // if this is a full release, ie. not expiring, always returns 0 + ReleaseString = -8570, // BSTR what this release is branded as, e.g. November CTP, Beta 2, etc. + ReleaseString_Short = -8571, // BSTR what this release is branded as, e.g. November CTP, Beta 2, etc. + RegistryRoots = -8572, // SafeArray of BSTRs, in order from earliest to latest + DisableUACSupport = -8573, + RunAsNormalUser = -8574, // VT_BOOL. TRUE if machine-wide registry values should be moved under HKEY_CURRENT_USER + // and common app-data files are written under per-user app-data with a + // "Configuration" or "UserSettings" subkey/subfolder + ConfigurationRoot = -8575, // VT_BSTR Alternative registry root to use when user settings and machine configuration + // need to be different. If not implemented, then use the default registry root. + DontWriteToUserAppData = -8576, // VT_BOOL. TRUE if we should not write to the user's appdata folder + // This might be TRUE in a "kiosk" application where the application leaves no + // trace of the user behind on the machine. + SQMLogFile = -8577, // BSTR full name of the SQM log created for the current session. + SupportRestartManager = -8578, // VT_BOOL (default is TRUE). In order to turn off support for Restart Manager, an AppID should implement this + SamplesURL = -8579, // BSTR URL to show in the internal web browser for Help - Samples command + AppDataDir = -8580, // BSTR (Remote) application data directory + LocalAppDataDir = -8581, // BSTR Local application data directory + CommonAppDataDir = -8582, // BSTR common (all users) application data directory + ConfigurationTimestampUtc = -8583, // VT_DATE value that represents the last time the configuration cache was built + // of Visual Studio was initializing + CommonExtensionSearchPath = -8584, // SafeArray of BSTRs. APPID specific list of folders where to look for Common (shared by all users) VS extensions. + // VS Extension Manager looks under these locations for VSIX manifest files. + UserExtensionsRootFolder = -8585, // BSTR. APPID specific folder path for User extensions. VS Extension Manager + // looks under this location for VSIX manifest files. + LoadUserExtensions = -8586, // VT_BOOL. Tells PkgDef management and Extension Manager API whether to load User extensions. + // This property is calculated based on the security logic of extension management and user preferrences. + LoadedUserExtensions = -8587, // SafeArray of BSTRs. List of folders that were searched for enabled user extensions. + // These are the essentially the user extensions that were enabled when the appid initialized. + AllowLoadingAllPackages = -8588, // VT_BOOL. Each APPID specifies through this property if it allows loading ALL Visual Studio Packages + // without PLK checking. Default is FALSE. - RunningInSafeMode = -8589, // VT_BOOL. Specifies whether the AppID is running in safe mode. + RunningInSafeMode = -8589, // VT_BOOL. Specifies whether the AppID is running in safe mode. - VSAPROPID_ProductFamily = -8590, // I4. See PIDFamily enum in DDConfig.h for list of valid values. - VSAPROPID_SplashScreenTheme = -8591, // INT_PTR pointing to the memory containing the VSSPLASHSCREENTHEME struct. The memory should be allocated and - // de-allocated at the appid implementation level. - VSAPROPID_RequiresElevation = -8592, // VT_BOOL. True means the appid always requires elevation - // False means the appid never requires elevation - // Default means the appid doesn't care, allow msenv to make the decision based on other factors (command line switches, etc.) - VSAPROPID_ApplicationRootFolder = -8593, // BSTR Full path of root location of installation (e.g. drive>:\Program Files\Microsoft Visual Studio \) - VSAPROPID_ApplicationExtensionsFolder = -8594, // BSTR Full path of folder for installing per-machine Extensions (e.g. Example: C:\Program Files\Microsoft Visual Studio \Common7\IDE\Extensions) - VSAPROPID_GenericTheme = -8595, // INT_PTR pointing to the memory containing the VSGENERICTHEME struct. The memory should be allocated and - // de-allocated at the appid implementation level. - VSAPROPID_ActivityLogPath = -8596, // VT_BSTR, Read-Only. Path to ActivityLog file. - VSAPROPID_ReleaseVersion = -8597, // VT_BSTR, Read-Only. The build version of the release and the branch/machine/user information used to build it (e.g. "10.0.30319.01 RTMRel" or "10.0.30128.1 BRANCHNAME(COMPUTERNAME-USERNAME)"). This is the same as the release string shown in Help/About. - VSAPROPID_EnableSamples = -8598, // VT_BOOL. Specifies whether samples are enabled. Defaults to false if not specified for Isolated Shell appids. - VSAPROPID_EnableMicrosoftGalleries = -8599, // VT_BOOL. Specifies whether Microsoft-owned extension galleries are enabled. Defaults to false if not specified for Isolated Shell appids. - VSAPROPID_EnablePrivateGalleries = -8600, // VT_BOOL. Specifies whether private extension galleries are enabled. Defaults to false if not specified for Isolated Shell appids. - VSAPROPID_AppVectorIcon = -8601, // VT_BSTR. Gets a vector path for an icon. This vector path must conform to the path markup syntax used by System.Windows.Media.Geometry. - VSAPROPID_AppBrandName = -8602, // VT_BSTR. The localized full brand name of the application, including SKU information. E.g. "Microsoft Visual Studio Professional 2012 RC" or "Microsoft Visual Studio Express 2012 RC for Windows 8" - VSAPROPID_AppShortBrandName = -8603, // VT_BSTR. A short version of VSAPROPID_AppBrandName, less than 32 chars. E.g. "VS Pro 2012 RC" or "VS Express 2012 RC for Win8" - VSAPROPID_SKUInfo = -8604, // VT_BSTR. A localized text describing the current SKU (name, year, release type, etc). E.g. "Ultimate 2012 RC" or "Express 2012 RC for Web" - VSAPROPID_GuidDefaultColorTheme = -8605, // GUID representing the color theme that should be used by default for the appid. If unimplemented by the appid, or if the theme does not exist when the appid is launched, the default light theme is chosen. - VSAPROPID_ActivityLogServiceObject = -8606, // VT_UNKNOWN. IUnknown the free thread activity log service object. - VSAPROPID_AppUpdateIcon = -8607, // VT_INT_PTR - HICON for SM_CXICON x SM_CYICON app update icon. - VSAPROPID_AppUpdateSmallIcon = -8608, // VT_INT_PTR - HICON for SM_CXSMICON x SM_CYSMICON app update icon. - VSAPROPID_AppUpdate64Icon = -8609, // VT_INT_PTR - HICON for 64 x 64 app update icon. - VSAPROPID_IsSubscriptionAware = -8610, // VT_BOOL. Specifies whether the application supports subscription license from VS Online - VSAPROPID_SubscriptionLicenseId = -8611, // GUID unique LicenseID that application specifies under $RootFolder$\Licenses for coordinating its VS Online subscription tokens. - VSAPROPID_SubscriptionRightsName = -8612, // VT_BSTR. Unique Name that identifies this application with the VS Online Licensing Service. - VSAPROPID_SupportsConnectedUser = -8613, // VT_BOOL. Specifies whether the application supports Connected User UI (e.g. Connected User sign-in, ID Card, roaming settings, first launch sign-in invitation, etc.) - VSAPROPID_SettingsRegistryRoots = -8614, // SafeArray of BSTRs, in order from earliest to latest, including current version, of registry roots checked during settings migration - VSAPROPID_EnableOfflineHelpNotification = -8615, // VT_BOOL. Specifies whether the help notification should be published to the notification hub on first launch - VSAPROPID_DefaultProfile = -8616, // VT_BSTR (optional). Specifies the default profile for the appid (e.g. "General") - VSAPROPID_ThemeThumbnailProvider = -8617, // VT_UNKNOWN (optional). Specifies an IUnknown from which the IVsThemeThumbnailProvider interface for the appid can be queried. - VSAPROPID_CommunityEdition = -8618, // VT_BOOL. Specifies whether VS is community edition. Only applicable to VS Professional. - VSAPROPID_LicenseURL = -8619, // BSTR URL to show in the About box for the license terms - VSAPROPID_EditionName = -8620, // BSTR Name to be used for the APPID in return value of DTE.Edition property - VSAPROPID_AppQueryLoadServiceObject = -8621, // VT_UNKNOWN. IUnknown the free threaded app query load service object. - VSAPROPID_IsVSTelemetryEnabled = -8622, // VT_BOOL. Specifies whether the VS Telemetry API is enabled in the SKU or not. - VSAPROPID_WorkingFodersRootExt = -8623, // BSTR - solution working folders extension(default - ""); - VSAPROPID_UnlocalizedReleaseString_Short = -8624, // BSTR what this release is branded as, e.g. November CTP, Beta 2, etc. (unlocalized) - // For the localized version of this string, use VSAPROPID_ReleaseString_Short - VSAPROPID_EnableNoToolWinMode = -8625, // VT_BOOL. Specifies whether the AppId enables NoToolWin mode. - VSAPROPID_InIsolationMode = -8626, // VT_BOOL. Specifies whether the AppId is running in isolation. - VSAPROPID_IsolationInstallationName = -8627, // VT_BSTR. The AppId's isolation installation name. - VSAPROPID_IsolationInstallationId = -8628, // VT_BSTR. The AppId's isolation installation id. - VSAPROPID_IsolationInstallationVersion = -8629, // VT_BSTR. The AppId's isolation installation version. - VSAPROPID_IsolationInstallationWorkloads = -8630, // VT_BSTR. The AppId's isolation installation workloads. - VSAPROPID_IsolationInstallationPackages = -8631, // VT_BSTR. The AppId's isolation installation packages. - VSAPROPID_IsolationInstallationUserDataFilePath = -8632, // VT_BSTR. The AppId's isolation installation userdata file path. - VSAPROPID_IsolationInstallationLogsDirectory = -8633, // VT_BSTR. The AppId's isolation installation logs directory. - VSAPROPID_SetupEngineFilePath = -8634, // VT_BSTR. The Setup Engine file path;. - VSAPROPID_LegacyCompatDirectory = -8635, // VT_BSTR. The root legacy compat directory that MSIs that are not isolation aware may install things to - VSAPROPID_CommonExtensionExclusionList = -8636, // SafeArray of BSTRs. A list of directories to exclude from extension processing (pkgdef, MEF, etc..) - VSAPROPID_SetupIsValid = -8637, // VT_BOOL. Specifies whether Setup finished correctly. - VSAPROPID_ChannelId = -8638, // VT_BSTR. The AppId's installation channel ID, for example VisualStudio.15.Release - VSAPROPID_ChannelManifestId = -8639, // VT_BSTR. The AppId's installation channel manifest unique ID, for example VisualStudio.15.Release/public.d15rel/15.0.26020.0 - VSAPROPID_InstallationNickname = -8640, // VT_BSTR. The AppId's installation nickname to disambiguate between SxS installations. - VSAPROPID_ProductDisplayVersion = -8641, // VT_BSTR. The AppId's product display version. - VSAPROPID_ProductSemanticVersion = -8642, // VT_BSTR. The AppId's product semantic version. - VSAPROPID_ChannelTitle = -8643, // VT_BSTR. The AppId's installation channel title. - VSAPROPID_ChannelSuffix = -8644, // VT_BSTR. The AppId's installation channel suffix. - VSAPROPID_AlphaPacksCount = -8645, // VT_BSTR. The number of alpha-packs this installation has. - VSAPROPID_CampaignId = -8646, // VT_BSTR. The campaign id associated with this install. - VSAPROPID_AppHostVersion = -8647, // VT_BSTR. The AppId's host version, preferred by _DTE.Version property. - VSAPROPID_SKUName = -8648, // VT_BSTR. The SkuName, unlocalized and sent with Telemetry. - VSAPROPID_BranchName = -8649 // VT_BSTR. The branch name of the build. - } + VSAPROPID_ProductFamily = -8590, // I4. See PIDFamily enum in DDConfig.h for list of valid values. + VSAPROPID_SplashScreenTheme = -8591, // INT_PTR pointing to the memory containing the VSSPLASHSCREENTHEME struct. The memory should be allocated and + // de-allocated at the appid implementation level. + VSAPROPID_RequiresElevation = -8592, // VT_BOOL. True means the appid always requires elevation + // False means the appid never requires elevation + // Default means the appid doesn't care, allow msenv to make the decision based on other factors (command line switches, etc.) + VSAPROPID_ApplicationRootFolder = -8593, // BSTR Full path of root location of installation (e.g. drive>:\Program Files\Microsoft Visual Studio \) + VSAPROPID_ApplicationExtensionsFolder = -8594, // BSTR Full path of folder for installing per-machine Extensions (e.g. Example: C:\Program Files\Microsoft Visual Studio \Common7\IDE\Extensions) + VSAPROPID_GenericTheme = -8595, // INT_PTR pointing to the memory containing the VSGENERICTHEME struct. The memory should be allocated and + // de-allocated at the appid implementation level. + VSAPROPID_ActivityLogPath = -8596, // VT_BSTR, Read-Only. Path to ActivityLog file. + VSAPROPID_ReleaseVersion = -8597, // VT_BSTR, Read-Only. The build version of the release and the branch/machine/user information used to build it (e.g. "10.0.30319.01 RTMRel" or "10.0.30128.1 BRANCHNAME(COMPUTERNAME-USERNAME)"). This is the same as the release string shown in Help/About. + VSAPROPID_EnableSamples = -8598, // VT_BOOL. Specifies whether samples are enabled. Defaults to false if not specified for Isolated Shell appids. + VSAPROPID_EnableMicrosoftGalleries = -8599, // VT_BOOL. Specifies whether Microsoft-owned extension galleries are enabled. Defaults to false if not specified for Isolated Shell appids. + VSAPROPID_EnablePrivateGalleries = -8600, // VT_BOOL. Specifies whether private extension galleries are enabled. Defaults to false if not specified for Isolated Shell appids. + VSAPROPID_AppVectorIcon = -8601, // VT_BSTR. Gets a vector path for an icon. This vector path must conform to the path markup syntax used by System.Windows.Media.Geometry. + VSAPROPID_AppBrandName = -8602, // VT_BSTR. The localized full brand name of the application, including SKU information. E.g. "Microsoft Visual Studio Professional 2012 RC" or "Microsoft Visual Studio Express 2012 RC for Windows 8" + VSAPROPID_AppShortBrandName = -8603, // VT_BSTR. A short version of VSAPROPID_AppBrandName, less than 32 chars. E.g. "VS Pro 2012 RC" or "VS Express 2012 RC for Win8" + VSAPROPID_SKUInfo = -8604, // VT_BSTR. A localized text describing the current SKU (name, year, release type, etc). E.g. "Ultimate 2012 RC" or "Express 2012 RC for Web" + VSAPROPID_GuidDefaultColorTheme = -8605, // GUID representing the color theme that should be used by default for the appid. If unimplemented by the appid, or if the theme does not exist when the appid is launched, the default light theme is chosen. + VSAPROPID_ActivityLogServiceObject = -8606, // VT_UNKNOWN. IUnknown the free thread activity log service object. + VSAPROPID_AppUpdateIcon = -8607, // VT_INT_PTR - HICON for SM_CXICON x SM_CYICON app update icon. + VSAPROPID_AppUpdateSmallIcon = -8608, // VT_INT_PTR - HICON for SM_CXSMICON x SM_CYSMICON app update icon. + VSAPROPID_AppUpdate64Icon = -8609, // VT_INT_PTR - HICON for 64 x 64 app update icon. + VSAPROPID_IsSubscriptionAware = -8610, // VT_BOOL. Specifies whether the application supports subscription license from VS Online + VSAPROPID_SubscriptionLicenseId = -8611, // GUID unique LicenseID that application specifies under $RootFolder$\Licenses for coordinating its VS Online subscription tokens. + VSAPROPID_SubscriptionRightsName = -8612, // VT_BSTR. Unique Name that identifies this application with the VS Online Licensing Service. + VSAPROPID_SupportsConnectedUser = -8613, // VT_BOOL. Specifies whether the application supports Connected User UI (e.g. Connected User sign-in, ID Card, roaming settings, first launch sign-in invitation, etc.) + VSAPROPID_SettingsRegistryRoots = -8614, // SafeArray of BSTRs, in order from earliest to latest, including current version, of registry roots checked during settings migration + VSAPROPID_EnableOfflineHelpNotification = -8615, // VT_BOOL. Specifies whether the help notification should be published to the notification hub on first launch + VSAPROPID_DefaultProfile = -8616, // VT_BSTR (optional). Specifies the default profile for the appid (e.g. "General") + VSAPROPID_ThemeThumbnailProvider = -8617, // VT_UNKNOWN (optional). Specifies an IUnknown from which the IVsThemeThumbnailProvider interface for the appid can be queried. + VSAPROPID_CommunityEdition = -8618, // VT_BOOL. Specifies whether VS is community edition. Only applicable to VS Professional. + VSAPROPID_LicenseURL = -8619, // BSTR URL to show in the About box for the license terms + VSAPROPID_EditionName = -8620, // BSTR Name to be used for the APPID in return value of DTE.Edition property + VSAPROPID_AppQueryLoadServiceObject = -8621, // VT_UNKNOWN. IUnknown the free threaded app query load service object. + VSAPROPID_IsVSTelemetryEnabled = -8622, // VT_BOOL. Specifies whether the VS Telemetry API is enabled in the SKU or not. + VSAPROPID_WorkingFodersRootExt = -8623, // BSTR - solution working folders extension(default - ""); + VSAPROPID_UnlocalizedReleaseString_Short = -8624, // BSTR what this release is branded as, e.g. November CTP, Beta 2, etc. (unlocalized) + // For the localized version of this string, use VSAPROPID_ReleaseString_Short + VSAPROPID_EnableNoToolWinMode = -8625, // VT_BOOL. Specifies whether the AppId enables NoToolWin mode. + VSAPROPID_InIsolationMode = -8626, // VT_BOOL. Specifies whether the AppId is running in isolation. + VSAPROPID_IsolationInstallationName = -8627, // VT_BSTR. The AppId's isolation installation name. + VSAPROPID_IsolationInstallationId = -8628, // VT_BSTR. The AppId's isolation installation id. + VSAPROPID_IsolationInstallationVersion = -8629, // VT_BSTR. The AppId's isolation installation version. + VSAPROPID_IsolationInstallationWorkloads = -8630, // VT_BSTR. The AppId's isolation installation workloads. + VSAPROPID_IsolationInstallationPackages = -8631, // VT_BSTR. The AppId's isolation installation packages. + VSAPROPID_IsolationInstallationUserDataFilePath = -8632, // VT_BSTR. The AppId's isolation installation userdata file path. + VSAPROPID_IsolationInstallationLogsDirectory = -8633, // VT_BSTR. The AppId's isolation installation logs directory. + VSAPROPID_SetupEngineFilePath = -8634, // VT_BSTR. The Setup Engine file path;. + VSAPROPID_LegacyCompatDirectory = -8635, // VT_BSTR. The root legacy compat directory that MSIs that are not isolation aware may install things to + VSAPROPID_CommonExtensionExclusionList = -8636, // SafeArray of BSTRs. A list of directories to exclude from extension processing (pkgdef, MEF, etc..) + VSAPROPID_SetupIsValid = -8637, // VT_BOOL. Specifies whether Setup finished correctly. + VSAPROPID_ChannelId = -8638, // VT_BSTR. The AppId's installation channel ID, for example VisualStudio.15.Release + VSAPROPID_ChannelManifestId = -8639, // VT_BSTR. The AppId's installation channel manifest unique ID, for example VisualStudio.15.Release/public.d15rel/15.0.26020.0 + VSAPROPID_InstallationNickname = -8640, // VT_BSTR. The AppId's installation nickname to disambiguate between SxS installations. + VSAPROPID_ProductDisplayVersion = -8641, // VT_BSTR. The AppId's product display version. + VSAPROPID_ProductSemanticVersion = -8642, // VT_BSTR. The AppId's product semantic version. + VSAPROPID_ChannelTitle = -8643, // VT_BSTR. The AppId's installation channel title. + VSAPROPID_ChannelSuffix = -8644, // VT_BSTR. The AppId's installation channel suffix. + VSAPROPID_AlphaPacksCount = -8645, // VT_BSTR. The number of alpha-packs this installation has. + VSAPROPID_CampaignId = -8646, // VT_BSTR. The campaign id associated with this install. + VSAPROPID_AppHostVersion = -8647, // VT_BSTR. The AppId's host version, preferred by _DTE.Version property. + VSAPROPID_SKUName = -8648, // VT_BSTR. The SkuName, unlocalized and sent with Telemetry. + VSAPROPID_BranchName = -8649 // VT_BSTR. The branch name of the build. } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProvider.cs index 9f7879a31e..e33a210146 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProvider.cs @@ -4,23 +4,22 @@ using Microsoft.CSharp; using Microsoft.VisualStudio.Designer.Interfaces; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp; + +/// +/// Provides the C# for use by designers and code generators. +/// +/// +/// This service is requested by and +/// returned by . +/// +[ExportVsProfferedProjectService(typeof(CodeDomProvider))] +[AppliesTo(ProjectCapability.CSharp)] +[PartCreationPolicy(CreationPolicy.NonShared)] +internal class CSharpCodeDomProvider : CSharpCodeProvider { - /// - /// Provides the C# for use by designers and code generators. - /// - /// - /// This service is requested by and - /// returned by . - /// - [ExportVsProfferedProjectService(typeof(CodeDomProvider))] - [AppliesTo(ProjectCapability.CSharp)] - [PartCreationPolicy(CreationPolicy.NonShared)] - internal class CSharpCodeDomProvider : CSharpCodeProvider + [ImportingConstructor] + public CSharpCodeDomProvider() { - [ImportingConstructor] - public CSharpCodeDomProvider() - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProvider.cs index 02e5a1cc4e..9a8fdf5ffc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProvider.cs @@ -3,127 +3,126 @@ using System.Globalization; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp; + +/// +/// An implementation of to provider C# language-specific features. +/// +[Export(typeof(ILanguageFeaturesProvider))] +[AppliesTo(ProjectCapabilities.CSharp)] +internal class CSharpLanguageFeaturesProvider : ILanguageFeaturesProvider { + private static readonly ImmutableHashSet s_identifierCharCategories = ImmutableHashSet.Empty + .Add(UnicodeCategory.UppercaseLetter) + .Add(UnicodeCategory.LowercaseLetter) + .Add(UnicodeCategory.TitlecaseLetter) + .Add(UnicodeCategory.ModifierLetter) + .Add(UnicodeCategory.OtherLetter) + .Add(UnicodeCategory.DecimalDigitNumber) + .Add(UnicodeCategory.ConnectorPunctuation) + .Add(UnicodeCategory.EnclosingMark) + .Add(UnicodeCategory.NonSpacingMark); + + private static readonly ImmutableHashSet s_firstIdentifierCharCategories = ImmutableHashSet.Empty + .Add(UnicodeCategory.UppercaseLetter) + .Add(UnicodeCategory.LowercaseLetter) + .Add(UnicodeCategory.TitlecaseLetter) + .Add(UnicodeCategory.ModifierLetter) + .Add(UnicodeCategory.OtherLetter) + .Add(UnicodeCategory.ConnectorPunctuation); + + [ImportingConstructor] + public CSharpLanguageFeaturesProvider() + { + } + /// - /// An implementation of to provider C# language-specific features. + /// Makes a proper language identifier from the specified name. /// - [Export(typeof(ILanguageFeaturesProvider))] - [AppliesTo(ProjectCapabilities.CSharp)] - internal class CSharpLanguageFeaturesProvider : ILanguageFeaturesProvider + /// + /// A containing the name. + /// + /// + /// A proper identifier which meets the C# language specification. + /// + /// + /// is . + /// + /// + /// is an empty string (""). + /// + public string MakeProperIdentifier(string name) { - private static readonly ImmutableHashSet s_identifierCharCategories = ImmutableHashSet.Empty - .Add(UnicodeCategory.UppercaseLetter) - .Add(UnicodeCategory.LowercaseLetter) - .Add(UnicodeCategory.TitlecaseLetter) - .Add(UnicodeCategory.ModifierLetter) - .Add(UnicodeCategory.OtherLetter) - .Add(UnicodeCategory.DecimalDigitNumber) - .Add(UnicodeCategory.ConnectorPunctuation) - .Add(UnicodeCategory.EnclosingMark) - .Add(UnicodeCategory.NonSpacingMark); + Requires.NotNullOrEmpty(name); - private static readonly ImmutableHashSet s_firstIdentifierCharCategories = ImmutableHashSet.Empty - .Add(UnicodeCategory.UppercaseLetter) - .Add(UnicodeCategory.LowercaseLetter) - .Add(UnicodeCategory.TitlecaseLetter) - .Add(UnicodeCategory.ModifierLetter) - .Add(UnicodeCategory.OtherLetter) - .Add(UnicodeCategory.ConnectorPunctuation); - - [ImportingConstructor] - public CSharpLanguageFeaturesProvider() + string identifier = string.Concat(name.Select(c => IsValidIdentifierChar(c) ? c : '_')); + if (!IsValidFirstIdentifierChar(identifier[0])) { + identifier = '_' + identifier; } - /// - /// Makes a proper language identifier from the specified name. - /// - /// - /// A containing the name. - /// - /// - /// A proper identifier which meets the C# language specification. - /// - /// - /// is . - /// - /// - /// is an empty string (""). - /// - public string MakeProperIdentifier(string name) - { - Requires.NotNullOrEmpty(name); - - string identifier = string.Concat(name.Select(c => IsValidIdentifierChar(c) ? c : '_')); - if (!IsValidFirstIdentifierChar(identifier[0])) - { - identifier = '_' + identifier; - } - - return identifier; - } + return identifier; + } - /// - /// Makes a proper namespace from the specified name. - /// - /// - /// A containing the name. - /// - /// - /// A proper namespace which meets the C# language specification. - /// - /// - /// is . - /// - /// - /// is an empty string (""). - /// - public string MakeProperNamespace(string name) - { - Requires.NotNullOrEmpty(name); + /// + /// Makes a proper namespace from the specified name. + /// + /// + /// A containing the name. + /// + /// + /// A proper namespace which meets the C# language specification. + /// + /// + /// is . + /// + /// + /// is an empty string (""). + /// + public string MakeProperNamespace(string name) + { + Requires.NotNullOrEmpty(name); - IEnumerable namespaceNames = new LazyStringSplit(name, '.').Select(MakeProperIdentifier); + IEnumerable namespaceNames = new LazyStringSplit(name, '.').Select(MakeProperIdentifier); - return string.Join(".", namespaceNames); - } + return string.Join(".", namespaceNames); + } - /// - /// Concatenates the specified namespace names. - /// - /// - /// A array containing the namespace names to be concatenated. - /// - /// - /// A concatenated namespace name. - /// - /// - /// is . - /// - /// - /// contains no elements. - /// - /// -or- - /// - /// contains an element that is . - /// - public string ConcatNamespaces(params string[] namespaceNames) - { - Requires.NotNullEmptyOrNullElements(namespaceNames); + /// + /// Concatenates the specified namespace names. + /// + /// + /// A array containing the namespace names to be concatenated. + /// + /// + /// A concatenated namespace name. + /// + /// + /// is . + /// + /// + /// contains no elements. + /// + /// -or- + /// + /// contains an element that is . + /// + public string ConcatNamespaces(params string[] namespaceNames) + { + Requires.NotNullEmptyOrNullElements(namespaceNames); - return string.Join(".", namespaceNames.Where(name => name.Length > 0)); - } + return string.Join(".", namespaceNames.Where(name => name.Length > 0)); + } - private static bool IsValidIdentifierChar(char c) - { - UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c); - return s_identifierCharCategories.Contains(category); - } + private static bool IsValidIdentifierChar(char c) + { + UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c); + return s_identifierCharCategories.Contains(category); + } - private static bool IsValidFirstIdentifierChar(char c) - { - UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c); - return s_firstIdentifierCharCategories.Contains(category); - } + private static bool IsValidFirstIdentifierChar(char c) + { + UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c); + return s_firstIdentifierCharCategories.Contains(category); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.VersionedProjectChangeDiff.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.VersionedProjectChangeDiff.cs index 607d1e7970..964dfb30f4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.VersionedProjectChangeDiff.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.VersionedProjectChangeDiff.cs @@ -2,27 +2,26 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +internal partial class AbstractEvaluationCommandLineHandler { - internal partial class AbstractEvaluationCommandLineHandler + /// + /// Represents a set of differences made to a project along with a version. + /// + [DebuggerDisplay("{Version}")] + private readonly struct VersionedProjectChangeDiff { - /// - /// Represents a set of differences made to a project along with a version. - /// - [DebuggerDisplay("{Version}")] - private readonly struct VersionedProjectChangeDiff - { - public readonly IComparable Version; - public readonly IProjectChangeDiff Difference; + public readonly IComparable Version; + public readonly IProjectChangeDiff Difference; - public VersionedProjectChangeDiff(IComparable version, IProjectChangeDiff difference) - { - Assumes.NotNull(version); - Assumes.NotNull(difference); + public VersionedProjectChangeDiff(IComparable version, IProjectChangeDiff difference) + { + Assumes.NotNull(version); + Assumes.NotNull(difference); - Version = version; - Difference = difference; - } + Version = version; + Difference = difference; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.cs index 5bc608f03a..3fafc83452 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandler.cs @@ -3,267 +3,266 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +/// +/// Responsible for coordinating changes and conflicts between evaluation and design-time builds, and pushing those changes +/// onto Roslyn via a . +/// +internal abstract partial class AbstractEvaluationCommandLineHandler(UnconfiguredProject project) { + // This class is not thread-safe, and the assumption is that the caller will make sure that project evaluations and builds (design-time) + // do not overlap inside the class at the same time. + // + // In the ideal world, we would simply wait for a design-time build to get the command-line arguments that would have been passed + // to Csc/Vbc and push these onto Roslyn. This is exactly what the legacy project system did; when a user added or removed a file + // or changed the project, it performed a blocking wait on the design-time build before returning control to the user. In CPS, + // however, design-time builds are not UI blocking, so control can be returned to the user before Roslyn has been told about the + // file. This leads to the user observable behavior where the source file for a period of time lives in the "Misc" project and is + // without "project" IntelliSense. To counteract that, we push changes both in design-time builds *and* during evaluations, which + // gives the user results a lot faster than if we just pushed during design-time builds only. Evaluations are guaranteed to have + // occurred before a file is seen by components outside of the project system. + // + // Typically, adds and removes of files found at evaluation time are also found during a design-time build, with the latter also + // including generated files. This forces us to remember what files we've already sent to Roslyn to avoid sending duplicate adds + // or removes of the same file. Due to design-time builds being significantly slower than evaluations, there are also times where + // many evaluations have occurred by the time a design-time build based on a past version of the ConfiguredProject has completed. + // This can lead to conflicts. + // + // A conflict occurs when evaluation or design-time build adds an item that the other removed, or vice versa. + // + // Examples of conflicts include: + // + // - A user removes an item before a design-time build that contains the addition of that item has finished + // - A user adds an item before a design-time build that contains the removal of that item has finished + // - A user adds an item that was previously generated by a target (but stopped generating it) + // - A user removes an item and in the same version it starts getting generated via a target during design-time build + // + // Examples of changes that are not conflicts include: + // + // - A user adds an item and it appears as an addition in both evaluation and design-time build (the item is always added) + // - A user removes an item and it appears as a removal in both evaluation and design-time build (the item is always removed) + // - A target during design-time build generates an item that did not appear during evaluation (the item is always added) + // - A target, new since the last design-time build, removes an item that appeared during evaluation (the item is always removed) + // + // TODO: These are also not conflicts, but we're currently handling differently to a normal build, which we should fix: + // + // - A target from the very first design-time build, removed an item that appeared during evaluation. Currently, the item is "added" + // but command-line builds do not see the source file. This is because a design-time build IProjectChangeDescription is only a + // diff between itself and the previous build, not between itself and evaluation, which means that design-time build diff never + // knows that the item was removed, it was just never present. + // + // Algorithm for resolving conflicts is as follows: + // + // 1. Walk every evaluation since the last design-time build, discarding those from conflict resolution that have a version less + // than or equal to the current design-time build. + // 2. Walk every design-time build addition, if there's an associated removal in a later evaluation - we throw away the addition + // 3. Walk every design-time build removal, if there's an associated addition in a later evaluation - we throw away the removal + // + // We don't resolve conflicts between changed items, because the design-time build doesn't produce them due to the way we represent + // command-line arguments as individual item includes, such as , without any + // metadata. + // + private readonly HashSet _paths = new(StringComparers.Paths); + private readonly Queue _projectEvaluations = new(); + + protected UnconfiguredProject Project { get; } = Requires.NotNull(project); + /// - /// Responsible for coordinating changes and conflicts between evaluation and design-time builds, and pushing those changes - /// onto Roslyn via a . + /// Applies the specified version of the project evaluation and metadata to the underlying + /// , indicating if the context is the currently active one. /// - internal abstract partial class AbstractEvaluationCommandLineHandler(UnconfiguredProject project) + public void ApplyProjectEvaluation(IWorkspaceProjectContext context, IComparable version, IProjectChangeDiff difference, IImmutableDictionary> previousMetadata, IImmutableDictionary> currentMetadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) { - // This class is not thread-safe, and the assumption is that the caller will make sure that project evaluations and builds (design-time) - // do not overlap inside the class at the same time. - // - // In the ideal world, we would simply wait for a design-time build to get the command-line arguments that would have been passed - // to Csc/Vbc and push these onto Roslyn. This is exactly what the legacy project system did; when a user added or removed a file - // or changed the project, it performed a blocking wait on the design-time build before returning control to the user. In CPS, - // however, design-time builds are not UI blocking, so control can be returned to the user before Roslyn has been told about the - // file. This leads to the user observable behavior where the source file for a period of time lives in the "Misc" project and is - // without "project" IntelliSense. To counteract that, we push changes both in design-time builds *and* during evaluations, which - // gives the user results a lot faster than if we just pushed during design-time builds only. Evaluations are guaranteed to have - // occurred before a file is seen by components outside of the project system. - // - // Typically, adds and removes of files found at evaluation time are also found during a design-time build, with the latter also - // including generated files. This forces us to remember what files we've already sent to Roslyn to avoid sending duplicate adds - // or removes of the same file. Due to design-time builds being significantly slower than evaluations, there are also times where - // many evaluations have occurred by the time a design-time build based on a past version of the ConfiguredProject has completed. - // This can lead to conflicts. - // - // A conflict occurs when evaluation or design-time build adds an item that the other removed, or vice versa. - // - // Examples of conflicts include: - // - // - A user removes an item before a design-time build that contains the addition of that item has finished - // - A user adds an item before a design-time build that contains the removal of that item has finished - // - A user adds an item that was previously generated by a target (but stopped generating it) - // - A user removes an item and in the same version it starts getting generated via a target during design-time build - // - // Examples of changes that are not conflicts include: - // - // - A user adds an item and it appears as an addition in both evaluation and design-time build (the item is always added) - // - A user removes an item and it appears as a removal in both evaluation and design-time build (the item is always removed) - // - A target during design-time build generates an item that did not appear during evaluation (the item is always added) - // - A target, new since the last design-time build, removes an item that appeared during evaluation (the item is always removed) - // - // TODO: These are also not conflicts, but we're currently handling differently to a normal build, which we should fix: - // - // - A target from the very first design-time build, removed an item that appeared during evaluation. Currently, the item is "added" - // but command-line builds do not see the source file. This is because a design-time build IProjectChangeDescription is only a - // diff between itself and the previous build, not between itself and evaluation, which means that design-time build diff never - // knows that the item was removed, it was just never present. - // - // Algorithm for resolving conflicts is as follows: - // - // 1. Walk every evaluation since the last design-time build, discarding those from conflict resolution that have a version less - // than or equal to the current design-time build. - // 2. Walk every design-time build addition, if there's an associated removal in a later evaluation - we throw away the addition - // 3. Walk every design-time build removal, if there's an associated addition in a later evaluation - we throw away the removal - // - // We don't resolve conflicts between changed items, because the design-time build doesn't produce them due to the way we represent - // command-line arguments as individual item includes, such as , without any - // metadata. - // - private readonly HashSet _paths = new(StringComparers.Paths); - private readonly Queue _projectEvaluations = new(); - - protected UnconfiguredProject Project { get; } = Requires.NotNull(project); - - /// - /// Applies the specified version of the project evaluation and metadata to the underlying - /// , indicating if the context is the currently active one. - /// - public void ApplyProjectEvaluation(IWorkspaceProjectContext context, IComparable version, IProjectChangeDiff difference, IImmutableDictionary> previousMetadata, IImmutableDictionary> currentMetadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) - { - if (!difference.AnyChanges) - return; + if (!difference.AnyChanges) + return; - difference = difference.NormalizeRenames(); + difference = difference.NormalizeRenames(); - EnqueueProjectEvaluation(); + EnqueueProjectEvaluation(); - ApplyChangesToContext(context, difference, previousMetadata, currentMetadata, isActiveContext, logger, evaluation: true); + ApplyChangesToContext(context, difference, previousMetadata, currentMetadata, isActiveContext, logger, evaluation: true); - void EnqueueProjectEvaluation() - { - Assumes.False(_projectEvaluations.Count > 0 && version.IsEarlierThan(_projectEvaluations.Peek().Version), "Attempted to push a project evaluation that regressed in version."); + void EnqueueProjectEvaluation() + { + Assumes.False(_projectEvaluations.Count > 0 && version.IsEarlierThan(_projectEvaluations.Peek().Version), "Attempted to push a project evaluation that regressed in version."); - _projectEvaluations.Enqueue(new VersionedProjectChangeDiff(version, difference)); - } + _projectEvaluations.Enqueue(new VersionedProjectChangeDiff(version, difference)); } + } - /// - /// Applies the specified version of the project build to the underlying - /// , indicating if the context is the currently active one. - /// - public void ApplyProjectBuild(IWorkspaceProjectContext context, IComparable version, IProjectChangeDiff difference, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) - { - if (!difference.AnyChanges) - return; + /// + /// Applies the specified version of the project build to the underlying + /// , indicating if the context is the currently active one. + /// + public void ApplyProjectBuild(IWorkspaceProjectContext context, IComparable version, IProjectChangeDiff difference, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) + { + if (!difference.AnyChanges) + return; - difference = difference.NormalizeRenames(); - difference = ResolveProjectBuildConflicts(version, difference); + difference = difference.NormalizeRenames(); + difference = ResolveProjectBuildConflicts(version, difference); - ApplyChangesToContext(context, difference, ImmutableStringDictionary>.EmptyOrdinal, ImmutableStringDictionary>.EmptyOrdinal, isActiveContext, logger, evaluation: false); + ApplyChangesToContext(context, difference, ImmutableStringDictionary>.EmptyOrdinal, ImmutableStringDictionary>.EmptyOrdinal, isActiveContext, logger, evaluation: false); + + IProjectChangeDiff ResolveProjectBuildConflicts(IComparable projectBuildVersion, IProjectChangeDiff projectBuildDifference) + { + DiscardOutOfDateProjectEvaluations(); - IProjectChangeDiff ResolveProjectBuildConflicts(IComparable projectBuildVersion, IProjectChangeDiff projectBuildDifference) + // Walk all evaluations (if any) that occurred since we launched and resolve the conflicts + foreach (VersionedProjectChangeDiff evaluation in _projectEvaluations) { - DiscardOutOfDateProjectEvaluations(); + Assumes.True(evaluation.Version.IsLaterThan(projectBuildVersion), "Attempted to resolve a conflict between a project build and an earlier project evaluation."); - // Walk all evaluations (if any) that occurred since we launched and resolve the conflicts - foreach (VersionedProjectChangeDiff evaluation in _projectEvaluations) - { - Assumes.True(evaluation.Version.IsLaterThan(projectBuildVersion), "Attempted to resolve a conflict between a project build and an earlier project evaluation."); + projectBuildDifference = ResolveConflicts(evaluation.Difference, projectBuildDifference); + } - projectBuildDifference = ResolveConflicts(evaluation.Difference, projectBuildDifference); - } + return projectBuildDifference; - return projectBuildDifference; + void DiscardOutOfDateProjectEvaluations() + { + // Throw away evaluations that are the same version or earlier than the design-time build + // version as it has more up-to-date information on the the current state of the project - void DiscardOutOfDateProjectEvaluations() + // Note, evaluations could be empty if previous evaluations resulted in no new changes + while (_projectEvaluations.Count > 0) { - // Throw away evaluations that are the same version or earlier than the design-time build - // version as it has more up-to-date information on the the current state of the project - - // Note, evaluations could be empty if previous evaluations resulted in no new changes - while (_projectEvaluations.Count > 0) - { - VersionedProjectChangeDiff projectEvaluation = _projectEvaluations.Peek(); - if (!projectEvaluation.Version.IsEarlierThanOrEqualTo(projectBuildVersion)) - break; - - _projectEvaluations.Dequeue(); - } + VersionedProjectChangeDiff projectEvaluation = _projectEvaluations.Peek(); + if (!projectEvaluation.Version.IsEarlierThanOrEqualTo(projectBuildVersion)) + break; + + _projectEvaluations.Dequeue(); } + } - static IProjectChangeDiff ResolveConflicts(IProjectChangeDiff evaluationDifferences, IProjectChangeDiff projectBuildDifferences) - { - // Remove added items that were removed by later evaluations, and vice versa - IImmutableSet added = projectBuildDifferences.AddedItems.Except(evaluationDifferences.RemovedItems); - IImmutableSet removed = projectBuildDifferences.RemovedItems.Except(evaluationDifferences.AddedItems); + static IProjectChangeDiff ResolveConflicts(IProjectChangeDiff evaluationDifferences, IProjectChangeDiff projectBuildDifferences) + { + // Remove added items that were removed by later evaluations, and vice versa + IImmutableSet added = projectBuildDifferences.AddedItems.Except(evaluationDifferences.RemovedItems); + IImmutableSet removed = projectBuildDifferences.RemovedItems.Except(evaluationDifferences.AddedItems); - Assumes.True(projectBuildDifferences.ChangedItems.Count == 0, "We should never see ChangedItems during project builds."); + Assumes.True(projectBuildDifferences.ChangedItems.Count == 0, "We should never see ChangedItems during project builds."); - return new ProjectChangeDiff(added, removed, projectBuildDifferences.ChangedItems); - } + return new ProjectChangeDiff(added, removed, projectBuildDifferences.ChangedItems); } } + } - protected abstract void AddToContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger); + protected abstract void AddToContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger); - protected abstract void RemoveFromContext(IWorkspaceProjectContext context, string fullPath, IManagedProjectDiagnosticOutputService logger); + protected abstract void RemoveFromContext(IWorkspaceProjectContext context, string fullPath, IManagedProjectDiagnosticOutputService logger); - protected abstract void UpdateInContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary previousMetadata, IImmutableDictionary currentMetadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger); + protected abstract void UpdateInContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary previousMetadata, IImmutableDictionary currentMetadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger); - private static bool IsItemInCurrentConfiguration(string includePath, IImmutableDictionary> metadata) + private static bool IsItemInCurrentConfiguration(string includePath, IImmutableDictionary> metadata) + { + if (metadata.TryGetValue(includePath, out IImmutableDictionary? itemMetadata) + && itemMetadata.GetBoolProperty(Compile.ExcludeFromCurrentConfigurationProperty) is true) { - if (metadata.TryGetValue(includePath, out IImmutableDictionary? itemMetadata) - && itemMetadata.GetBoolProperty(Compile.ExcludeFromCurrentConfigurationProperty) is true) - { - return false; - } + return false; + } + + return true; + } - return true; + private void ApplyChangesToContext( + IWorkspaceProjectContext context, IProjectChangeDiff difference, + IImmutableDictionary> previousMetadata, + IImmutableDictionary> currentMetadata, bool isActiveContext, + IManagedProjectDiagnosticOutputService logger, bool evaluation) + { + foreach (string includePath in difference.RemovedItems) + { + RemoveFromContextIfPresent(context, includePath, logger); } - private void ApplyChangesToContext( - IWorkspaceProjectContext context, IProjectChangeDiff difference, - IImmutableDictionary> previousMetadata, - IImmutableDictionary> currentMetadata, bool isActiveContext, - IManagedProjectDiagnosticOutputService logger, bool evaluation) + foreach (string includePath in difference.AddedItems) { - foreach (string includePath in difference.RemovedItems) + if (evaluation && !IsItemInCurrentConfiguration(includePath, currentMetadata)) { - RemoveFromContextIfPresent(context, includePath, logger); + // The item is present in evaluation but contains metadata indicating it should be + // ignored. + continue; } - foreach (string includePath in difference.AddedItems) - { - if (evaluation && !IsItemInCurrentConfiguration(includePath, currentMetadata)) - { - // The item is present in evaluation but contains metadata indicating it should be - // ignored. - continue; - } + AddToContextIfNotPresent(context, includePath, currentMetadata, isActiveContext, logger); + } - AddToContextIfNotPresent(context, includePath, currentMetadata, isActiveContext, logger); + if (evaluation) + { // No need to look at metadata for design-time builds, the items that come from + // that aren't traditional items, but are just command-line args we took from + // the compiler and converted them to look like items. + + foreach (string includePath in difference.ChangedItems) + { + HandleEvaluationMetadataChange(includePath); } + } - if (evaluation) - { // No need to look at metadata for design-time builds, the items that come from - // that aren't traditional items, but are just command-line args we took from - // the compiler and converted them to look like items. + Assumes.True(difference.RenamedItems.Count == 0, "We should have normalized renames."); - foreach (string includePath in difference.ChangedItems) - { - HandleEvaluationMetadataChange(includePath); - } - } + void HandleEvaluationMetadataChange(string includePath) + { + // This should only be called for evaluation changes. The items we get from design-time builds represent + // command line arguments and won't have metadata. - Assumes.True(difference.RenamedItems.Count == 0, "We should have normalized renames."); + // A change in ExcludeFromCurrentConfiguration metadata needs to be processed as an add or remove rather + // than an update, so check for that first. + bool previouslyIncluded = IsItemInCurrentConfiguration(includePath, previousMetadata); + bool currentlyIncluded = IsItemInCurrentConfiguration(includePath, currentMetadata); - void HandleEvaluationMetadataChange(string includePath) + if (previouslyIncluded && !currentlyIncluded) + { + RemoveFromContextIfPresent(context, includePath, logger); + } + else if (!previouslyIncluded && currentlyIncluded) { - // This should only be called for evaluation changes. The items we get from design-time builds represent - // command line arguments and won't have metadata. + AddToContextIfNotPresent(context, includePath, currentMetadata, isActiveContext, logger); + } + else + { + // No change to ExcludeFromCurrentConfiguration; handle as an update. - // A change in ExcludeFromCurrentConfiguration metadata needs to be processed as an add or remove rather - // than an update, so check for that first. - bool previouslyIncluded = IsItemInCurrentConfiguration(includePath, previousMetadata); - bool currentlyIncluded = IsItemInCurrentConfiguration(includePath, currentMetadata); + string fullPath = Project.MakeRooted(includePath); - if (previouslyIncluded && !currentlyIncluded) - { - RemoveFromContextIfPresent(context, includePath, logger); - } - else if (!previouslyIncluded && currentlyIncluded) - { - AddToContextIfNotPresent(context, includePath, currentMetadata, isActiveContext, logger); - } - else + if (_paths.Contains(fullPath)) { - // No change to ExcludeFromCurrentConfiguration; handle as an update. - - string fullPath = Project.MakeRooted(includePath); - - if (_paths.Contains(fullPath)) - { - IImmutableDictionary previousItemMetadata = previousMetadata.GetValueOrDefault(includePath, ImmutableStringDictionary.EmptyOrdinal); - IImmutableDictionary currentItemMetadata = currentMetadata.GetValueOrDefault(includePath, ImmutableStringDictionary.EmptyOrdinal); + IImmutableDictionary previousItemMetadata = previousMetadata.GetValueOrDefault(includePath, ImmutableStringDictionary.EmptyOrdinal); + IImmutableDictionary currentItemMetadata = currentMetadata.GetValueOrDefault(includePath, ImmutableStringDictionary.EmptyOrdinal); - UpdateInContext(context, fullPath, previousItemMetadata, currentItemMetadata, isActiveContext, logger); - } + UpdateInContext(context, fullPath, previousItemMetadata, currentItemMetadata, isActiveContext, logger); } } } + } - private void RemoveFromContextIfPresent(IWorkspaceProjectContext context, string includePath, IManagedProjectDiagnosticOutputService logger) - { - string fullPath = Project.MakeRooted(includePath); + private void RemoveFromContextIfPresent(IWorkspaceProjectContext context, string includePath, IManagedProjectDiagnosticOutputService logger) + { + string fullPath = Project.MakeRooted(includePath); - if (_paths.Contains(fullPath)) - { - // Remove from the context first so if Roslyn throws due to a bug - // or other reason, that our state of the world remains consistent - RemoveFromContext(context, fullPath, logger); - bool removed = _paths.Remove(fullPath); - Assumes.True(removed); - } + if (_paths.Contains(fullPath)) + { + // Remove from the context first so if Roslyn throws due to a bug + // or other reason, that our state of the world remains consistent + RemoveFromContext(context, fullPath, logger); + bool removed = _paths.Remove(fullPath); + Assumes.True(removed); } + } - private void AddToContextIfNotPresent(IWorkspaceProjectContext context, string includePath, IImmutableDictionary> metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) - { - string fullPath = Project.MakeRooted(includePath); + private void AddToContextIfNotPresent(IWorkspaceProjectContext context, string includePath, IImmutableDictionary> metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) + { + string fullPath = Project.MakeRooted(includePath); - if (!_paths.Contains(fullPath)) - { - IImmutableDictionary itemMetadata = metadata.GetValueOrDefault(includePath, ImmutableStringDictionary.EmptyOrdinal); + if (!_paths.Contains(fullPath)) + { + IImmutableDictionary itemMetadata = metadata.GetValueOrDefault(includePath, ImmutableStringDictionary.EmptyOrdinal); - // Add to the context first so if Roslyn throws due to a bug or - // other reason, that our state of the world remains consistent - AddToContext(context, fullPath, itemMetadata, isActiveContext, logger); - bool added = _paths.Add(fullPath); - Assumes.True(added); - } + // Add to the context first so if Roslyn throws due to a bug or + // other reason, that our state of the world remains consistent + AddToContext(context, fullPath, itemMetadata, isActiveContext, logger); + bool added = _paths.Add(fullPath); + Assumes.True(added); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ICommandLineHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ICommandLineHandler.cs index 2728e54625..7a11a76e5e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ICommandLineHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ICommandLineHandler.cs @@ -2,37 +2,36 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Handles changes within the command-line, and applies them to a +/// instance. +/// +internal interface ICommandLineHandler : IWorkspaceUpdateHandler { /// - /// Handles changes within the command-line, and applies them to a - /// instance. + /// Handles the specified added and removed command-line arguments, and applies + /// them to the underlying . /// - internal interface ICommandLineHandler : IWorkspaceUpdateHandler - { - /// - /// Handles the specified added and removed command-line arguments, and applies - /// them to the underlying . - /// - /// - /// The to update. - /// - /// - /// An representing the at - /// the time the were produced. - /// - /// - /// A representing the added arguments. - /// - /// - /// A representing the removed arguments. - /// - /// - /// A describing the state of the . - /// - /// - /// The for logging to the log. - /// - void Handle(IWorkspaceProjectContext context, IComparable version, BuildOptions added, BuildOptions removed, ContextState state, IManagedProjectDiagnosticOutputService logger); - } + /// + /// The to update. + /// + /// + /// An representing the at + /// the time the were produced. + /// + /// + /// A representing the added arguments. + /// + /// + /// A representing the removed arguments. + /// + /// + /// A describing the state of the . + /// + /// + /// The for logging to the log. + /// + void Handle(IWorkspaceProjectContext context, IComparable version, BuildOptions added, BuildOptions removed, ContextState state, IManagedProjectDiagnosticOutputService logger); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IProjectEvaluationHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IProjectEvaluationHandler.cs index 6ffaa4e61a..d2ce0c4565 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IProjectEvaluationHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IProjectEvaluationHandler.cs @@ -2,49 +2,48 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Handles changes to project evaluation rule and applies them to a +/// instance. +/// +internal interface IProjectEvaluationHandler : IWorkspaceUpdateHandler { /// - /// Handles changes to project evaluation rule and applies them to a - /// instance. + /// Gets the project evaluation rule that the handles. /// - internal interface IProjectEvaluationHandler : IWorkspaceUpdateHandler - { - /// - /// Gets the project evaluation rule that the handles. - /// - string ProjectEvaluationRule { get; } + string ProjectEvaluationRule { get; } - /// - /// Handles the specified set of changes to the project evaluation rule, and applies them - /// to the underlying . - /// - /// - /// The to update. - /// - /// - /// The configuration of the project being updated. Needed as we are in unconfigured scope here. - /// - /// - /// An representing the at - /// the time the was produced. - /// - /// - /// A representing the set of - /// changes made to the project. - /// - /// - /// A describing the state of the . - /// - /// - /// The for logging to the log. - /// - void Handle( - IWorkspaceProjectContext context, - ProjectConfiguration projectConfiguration, - IComparable version, - IProjectChangeDescription projectChange, - ContextState state, - IManagedProjectDiagnosticOutputService logger); - } + /// + /// Handles the specified set of changes to the project evaluation rule, and applies them + /// to the underlying . + /// + /// + /// The to update. + /// + /// + /// The configuration of the project being updated. Needed as we are in unconfigured scope here. + /// + /// + /// An representing the at + /// the time the was produced. + /// + /// + /// A representing the set of + /// changes made to the project. + /// + /// + /// A describing the state of the . + /// + /// + /// The for logging to the log. + /// + void Handle( + IWorkspaceProjectContext context, + ProjectConfiguration projectConfiguration, + IComparable version, + IProjectChangeDescription projectChange, + ContextState state, + IManagedProjectDiagnosticOutputService logger); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ISourceItemsHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ISourceItemsHandler.cs index 3339171590..75b27cf697 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ISourceItemsHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ISourceItemsHandler.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Handles changes to source items and applies them to a +/// instance. +/// +internal interface ISourceItemsHandler : IWorkspaceUpdateHandler { /// - /// Handles changes to source items and applies them to a - /// instance. + /// Handles the specified set of changes to source items, and applies them + /// to the underlying . /// - internal interface ISourceItemsHandler : IWorkspaceUpdateHandler - { - /// - /// Handles the specified set of changes to source items, and applies them - /// to the underlying . - /// - /// - /// The to update. - /// - /// - /// A dictionary of representing the set of - /// changes made to the project, keyed by their item type. - /// - /// - /// A describing the state of the . - /// - /// - /// The for logging to the log. - /// - void Handle(IWorkspaceProjectContext context, IImmutableDictionary projectChanges, ContextState state, IManagedProjectDiagnosticOutputService logger); - } + /// + /// The to update. + /// + /// + /// A dictionary of representing the set of + /// changes made to the project, keyed by their item type. + /// + /// + /// A describing the state of the . + /// + /// + /// The for logging to the log. + /// + void Handle(IWorkspaceProjectContext context, IImmutableDictionary projectChanges, ContextState state, IManagedProjectDiagnosticOutputService logger); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IVsContainedLanguageComponentsFactory.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IVsContainedLanguageComponentsFactory.cs index fb068fc337..12c1fad1c1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IVsContainedLanguageComponentsFactory.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IVsContainedLanguageComponentsFactory.cs @@ -3,28 +3,27 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TextManager.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; + +/// +/// Exposes a VS specific API related to +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface IVsContainedLanguageComponentsFactory { /// - /// Exposes a VS specific API related to + /// Gets an object that represents a host-specific IVsContainedLanguageFactory implementation. + /// Note: currently we have only one target framework and IVsHierarchy and itemId is returned as + /// they are from the unconfigured project. Later when combined intellisense is implemented, depending + /// on implementation we might need to have a logic that returns IVsHierarchy and itemId specific to + /// currently active target framework (that's how it was in Dev14's dnx/dotnet project system) /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface IVsContainedLanguageComponentsFactory - { - /// - /// Gets an object that represents a host-specific IVsContainedLanguageFactory implementation. - /// Note: currently we have only one target framework and IVsHierarchy and itemId is returned as - /// they are from the unconfigured project. Later when combined intellisense is implemented, depending - /// on implementation we might need to have a logic that returns IVsHierarchy and itemId specific to - /// currently active target framework (that's how it was in Dev14's dnx/dotnet project system) - /// - /// Path to a file - /// Project hierarchy containing given file for current language service - /// item id of the given file - /// an instance of IVsContainedLanguageFactory specific for current language service - int GetContainedLanguageFactoryForFile(string filePath, - out IVsHierarchy? hierarchy, - out uint itemid, - out IVsContainedLanguageFactory? containedLanguageFactory); - } + /// Path to a file + /// Project hierarchy containing given file for current language service + /// item id of the given file + /// an instance of IVsContainedLanguageFactory specific for current language service + int GetContainedLanguageFactoryForFile(string filePath, + out IVsHierarchy? hierarchy, + out uint itemid, + out IVsContainedLanguageFactory? containedLanguageFactory); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspace.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspace.cs index bbc9839fa5..9207b4a336 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspace.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspace.cs @@ -3,26 +3,25 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Provides access to a and associated services. +/// +internal interface IWorkspace { /// - /// Provides access to a and associated services. + /// Gets an identifier that uniquely identifies the across a solution. /// - internal interface IWorkspace - { - /// - /// Gets an identifier that uniquely identifies the across a solution. - /// - string ContextId { get; } + string ContextId { get; } - /// - /// Gets the that provides access to the language service. - /// - IWorkspaceProjectContext Context { get; } + /// + /// Gets the that provides access to the language service. + /// + IWorkspaceProjectContext Context { get; } - /// - /// Gets the language service build error reporter object. - /// - IVsLanguageServiceBuildErrorReporter2 ErrorReporter { get; } - } + /// + /// Gets the language service build error reporter object. + /// + IVsLanguageServiceBuildErrorReporter2 ErrorReporter { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspaceUpdateHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspaceUpdateHandler.cs index c564bc1209..dc73b3a388 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspaceUpdateHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/IWorkspaceUpdateHandler.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// A marker interface for types that apply changes to instances. +/// +/// +/// Valid types are , +/// and . Implementations of this interface are only invoked +/// when implementing one of these specific subtypes. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface IWorkspaceUpdateHandler { - /// - /// A marker interface for types that apply changes to instances. - /// - /// - /// Valid types are , - /// and . Implementations of this interface are only invoked - /// when implementing one of these specific subtypes. - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface IWorkspaceUpdateHandler - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ProjectContextCodeModelProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ProjectContextCodeModelProvider.cs index 47a99f3f12..0175e594c0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ProjectContextCodeModelProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/ProjectContextCodeModelProvider.cs @@ -6,90 +6,89 @@ using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; + +/// +/// Adapts CPS's and to Roslyn's implementation. +/// +[Export(typeof(ICodeModelProvider))] +[Export(typeof(IProjectCodeModelProvider))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal class ProjectContextCodeModelProvider : ICodeModelProvider, IProjectCodeModelProvider { - /// - /// Adapts CPS's and to Roslyn's implementation. - /// - [Export(typeof(ICodeModelProvider))] - [Export(typeof(IProjectCodeModelProvider))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal class ProjectContextCodeModelProvider : ICodeModelProvider, IProjectCodeModelProvider - { - private readonly IProjectThreadingService _threadingService; - private readonly ICodeModelFactory _codeModelFactory; - private readonly IWorkspaceWriter _workspaceWriter; - private readonly ITelemetryService _telemetryService; - private int _telemetrySent; - - [ImportingConstructor] - public ProjectContextCodeModelProvider(IProjectThreadingService threadingService, ICodeModelFactory codeModelFactory, IWorkspaceWriter workspaceWriter, ITelemetryService telemetryService) - { - _threadingService = threadingService; - _codeModelFactory = codeModelFactory; - _workspaceWriter = workspaceWriter; - _telemetryService = telemetryService; - } + private readonly IProjectThreadingService _threadingService; + private readonly ICodeModelFactory _codeModelFactory; + private readonly IWorkspaceWriter _workspaceWriter; + private readonly ITelemetryService _telemetryService; + private int _telemetrySent; - public CodeModel? GetCodeModel(Project project) - { - Requires.NotNull(project); + [ImportingConstructor] + public ProjectContextCodeModelProvider(IProjectThreadingService threadingService, ICodeModelFactory codeModelFactory, IWorkspaceWriter workspaceWriter, ITelemetryService telemetryService) + { + _threadingService = threadingService; + _codeModelFactory = codeModelFactory; + _workspaceWriter = workspaceWriter; + _telemetryService = telemetryService; + } - SendTelemetry(); + public CodeModel? GetCodeModel(Project project) + { + Requires.NotNull(project); - return _threadingService.ExecuteSynchronously(() => - { - return GetCodeModelAsync(project); - }); - } + SendTelemetry(); - public FileCodeModel? GetFileCodeModel(ProjectItem fileItem) + return _threadingService.ExecuteSynchronously(() => { - Requires.NotNull(fileItem); + return GetCodeModelAsync(project); + }); + } - SendTelemetry(); + public FileCodeModel? GetFileCodeModel(ProjectItem fileItem) + { + Requires.NotNull(fileItem); - return _threadingService.ExecuteSynchronously(() => - { - return GetFileCodeModelAsync(fileItem); - }); - } + SendTelemetry(); - private async Task GetCodeModelAsync(Project project) + return _threadingService.ExecuteSynchronously(() => { - await _threadingService.SwitchToUIThread(); + return GetFileCodeModelAsync(fileItem); + }); + } - return await _workspaceWriter.WriteAsync(workspace => - { - return Task.FromResult(_codeModelFactory.GetCodeModel(workspace.Context, project)); - }); - } + private async Task GetCodeModelAsync(Project project) + { + await _threadingService.SwitchToUIThread(); - private async Task GetFileCodeModelAsync(ProjectItem fileItem) + return await _workspaceWriter.WriteAsync(workspace => { - await _threadingService.SwitchToUIThread(); + return Task.FromResult(_codeModelFactory.GetCodeModel(workspace.Context, project)); + }); + } - return await _workspaceWriter.WriteAsync(workspace => - { - try - { - return Task.FromResult(_codeModelFactory.GetFileCodeModel(workspace.Context, fileItem)); - } - catch (NotImplementedException) - { // Isn't a file that Roslyn knows about - } - - return TaskResult.Null(); - }); - } + private async Task GetFileCodeModelAsync(ProjectItem fileItem) + { + await _threadingService.SwitchToUIThread(); - private void SendTelemetry() + return await _workspaceWriter.WriteAsync(workspace => { - // Send a telemetry event once per unconfigured project - if (Interlocked.CompareExchange(ref _telemetrySent, value: 1, comparand: 0) == 0) + try { - _telemetryService.PostEvent(TelemetryEventName.CodeModelRequested); + return Task.FromResult(_codeModelFactory.GetFileCodeModel(workspace.Context, fileItem)); + } + catch (NotImplementedException) + { // Isn't a file that Roslyn knows about } + + return TaskResult.Null(); + }); + } + + private void SendTelemetry() + { + // Send a telemetry event once per unconfigured project + if (Interlocked.CompareExchange(ref _telemetrySent, value: 1, comparand: 0) == 0) + { + _telemetryService.PostEvent(TelemetryEventName.CodeModelRequested); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProvider.cs index fc180f31f7..80a8a0e989 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProvider.cs @@ -4,23 +4,22 @@ using Microsoft.VisualBasic; using Microsoft.VisualStudio.Designer.Interfaces; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.VisualBasic; + +/// +/// Provides the Visual Basic for use by designers and code generators. +/// +/// +/// This service is requested by and +/// returned by . +/// +[ExportVsProfferedProjectService(typeof(CodeDomProvider))] +[AppliesTo(ProjectCapability.VisualBasic)] +[PartCreationPolicy(CreationPolicy.NonShared)] +internal class VisualBasicCodeDomProvider : VBCodeProvider { - /// - /// Provides the Visual Basic for use by designers and code generators. - /// - /// - /// This service is requested by and - /// returned by . - /// - [ExportVsProfferedProjectService(typeof(CodeDomProvider))] - [AppliesTo(ProjectCapability.VisualBasic)] - [PartCreationPolicy(CreationPolicy.NonShared)] - internal class VisualBasicCodeDomProvider : VBCodeProvider + [ImportingConstructor] + public VisualBasicCodeDomProvider() { - [ImportingConstructor] - public VisualBasicCodeDomProvider() - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsActiveEditorContextTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsActiveEditorContextTracker.cs index 26158c962e..d8d200858f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsActiveEditorContextTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsActiveEditorContextTracker.cs @@ -4,48 +4,47 @@ using Microsoft.VisualStudio.TextManager.Interop; using HierarchyId = Microsoft.VisualStudio.Shell.HierarchyId; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; + +/// +/// Tracks the "active" context for the editor by handling the VSHPROPID_ActiveIntellisenseProjectContext hierarchy property. +/// +[Export(typeof(IActiveIntellisenseProjectProvider))] +[ExportProjectNodeComService(typeof(IVsContainedLanguageProjectNameProvider))] +[AppliesTo(ProjectCapability.DotNetLanguageService)] +internal class VsActiveEditorContextTracker : IActiveIntellisenseProjectProvider, IVsContainedLanguageProjectNameProvider { - /// - /// Tracks the "active" context for the editor by handling the VSHPROPID_ActiveIntellisenseProjectContext hierarchy property. - /// - [Export(typeof(IActiveIntellisenseProjectProvider))] - [ExportProjectNodeComService(typeof(IVsContainedLanguageProjectNameProvider))] - [AppliesTo(ProjectCapability.DotNetLanguageService)] - internal class VsActiveEditorContextTracker : IActiveIntellisenseProjectProvider, IVsContainedLanguageProjectNameProvider + private readonly IActiveEditorContextTracker _activeEditorContextTracker; + + // UnconfiguredProject is only included for scoping reasons. + [ImportingConstructor] + public VsActiveEditorContextTracker(UnconfiguredProject? project, IActiveEditorContextTracker activeEditorContextTracker) { - private readonly IActiveEditorContextTracker _activeEditorContextTracker; + _activeEditorContextTracker = activeEditorContextTracker; + } - // UnconfiguredProject is only included for scoping reasons. - [ImportingConstructor] - public VsActiveEditorContextTracker(UnconfiguredProject? project, IActiveEditorContextTracker activeEditorContextTracker) - { - _activeEditorContextTracker = activeEditorContextTracker; - } + public string? ActiveIntellisenseProjectContext + { + get { return _activeEditorContextTracker.ActiveIntellisenseProjectContext; } + set { _activeEditorContextTracker.ActiveIntellisenseProjectContext = value; } + } - public string? ActiveIntellisenseProjectContext + public int GetProjectName(uint itemid, out string? pbstrProjectName) + { + if (itemid == HierarchyId.Nil || itemid == HierarchyId.Selection || itemid == HierarchyId.Empty) { - get { return _activeEditorContextTracker.ActiveIntellisenseProjectContext; } - set { _activeEditorContextTracker.ActiveIntellisenseProjectContext = value; } + pbstrProjectName = null; + return HResult.InvalidArg; } - public int GetProjectName(uint itemid, out string? pbstrProjectName) - { - if (itemid == HierarchyId.Nil || itemid == HierarchyId.Selection || itemid == HierarchyId.Empty) - { - pbstrProjectName = null; - return HResult.InvalidArg; - } - - pbstrProjectName = ActiveIntellisenseProjectContext; + pbstrProjectName = ActiveIntellisenseProjectContext; - return HResult.OK; - } + return HResult.OK; + } - // Pass-through for unit testing. - internal bool IsActiveEditorContext(string contextId) => _activeEditorContextTracker.IsActiveEditorContext(contextId); + // Pass-through for unit testing. + internal bool IsActiveEditorContext(string contextId) => _activeEditorContextTracker.IsActiveEditorContext(contextId); - // Pass-through for unit testing. - internal IDisposable RegisterContext(string contextId) => _activeEditorContextTracker.RegisterContext(contextId); - } + // Pass-through for unit testing. + internal IDisposable RegisterContext(string contextId) => _activeEditorContextTracker.RegisterContext(contextId); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactory.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactory.cs index a50454d2e6..0664b1adb4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactory.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactory.cs @@ -8,86 +8,85 @@ using Microsoft.VisualStudio.Threading; using IOleAsyncServiceProvider = Microsoft.VisualStudio.Shell.Interop.COMAsyncServiceProvider.IAsyncServiceProvider; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; + +[Export(typeof(IVsContainedLanguageComponentsFactory))] +[AppliesTo(ProjectCapability.DotNet)] +internal class VsContainedLanguageComponentsFactory : IVsContainedLanguageComponentsFactory { - [Export(typeof(IVsContainedLanguageComponentsFactory))] - [AppliesTo(ProjectCapability.DotNet)] - internal class VsContainedLanguageComponentsFactory : IVsContainedLanguageComponentsFactory + private readonly IVsService _serviceProvider; + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IWorkspaceWriter _workspaceWriter; + private readonly AsyncLazy _containedLanguageFactory; + + [ImportingConstructor] + public VsContainedLanguageComponentsFactory(IVsService serviceProvider, + IUnconfiguredProjectVsServices projectVsServices, + IWorkspaceWriter workspaceWriter) { - private readonly IVsService _serviceProvider; - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IWorkspaceWriter _workspaceWriter; - private readonly AsyncLazy _containedLanguageFactory; - - [ImportingConstructor] - public VsContainedLanguageComponentsFactory(IVsService serviceProvider, - IUnconfiguredProjectVsServices projectVsServices, - IWorkspaceWriter workspaceWriter) - { - _serviceProvider = serviceProvider; - _projectVsServices = projectVsServices; - _workspaceWriter = workspaceWriter; + _serviceProvider = serviceProvider; + _projectVsServices = projectVsServices; + _workspaceWriter = workspaceWriter; - _containedLanguageFactory = new AsyncLazy(GetContainedLanguageFactoryAsync, projectVsServices.ThreadingService.JoinableTaskFactory); - } + _containedLanguageFactory = new AsyncLazy(GetContainedLanguageFactoryAsync, projectVsServices.ThreadingService.JoinableTaskFactory); + } - public int GetContainedLanguageFactoryForFile(string filePath, - out IVsHierarchy? hierarchy, - out uint itemid, - out IVsContainedLanguageFactory? containedLanguageFactory) + public int GetContainedLanguageFactoryForFile(string filePath, + out IVsHierarchy? hierarchy, + out uint itemid, + out IVsContainedLanguageFactory? containedLanguageFactory) + { + (itemid, hierarchy, containedLanguageFactory) = _projectVsServices.ThreadingService.ExecuteSynchronously(() => { - (itemid, hierarchy, containedLanguageFactory) = _projectVsServices.ThreadingService.ExecuteSynchronously(() => - { - return GetContainedLanguageFactoryForFileAsync(filePath); - }); + return GetContainedLanguageFactoryForFileAsync(filePath); + }); - return (hierarchy is null || containedLanguageFactory is null) ? HResult.Fail : HResult.OK; - } + return (hierarchy is null || containedLanguageFactory is null) ? HResult.Fail : HResult.OK; + } - private async Task<(HierarchyId itemid, IVsHierarchy? hierarchy, IVsContainedLanguageFactory? containedLanguageFactory)> GetContainedLanguageFactoryForFileAsync(string filePath) - { - await _workspaceWriter.WhenInitialized(); + private async Task<(HierarchyId itemid, IVsHierarchy? hierarchy, IVsContainedLanguageFactory? containedLanguageFactory)> GetContainedLanguageFactoryForFileAsync(string filePath) + { + await _workspaceWriter.WhenInitialized(); - await _projectVsServices.ThreadingService.SwitchToUIThread(); + await _projectVsServices.ThreadingService.SwitchToUIThread(); - var priority = new VSDOCUMENTPRIORITY[1]; - HResult result = _projectVsServices.VsProject.IsDocumentInProject(filePath, out int isFound, priority, out uint itemid); - if (result.Failed || isFound == 0) - return (HierarchyId.Nil, null, null); + var priority = new VSDOCUMENTPRIORITY[1]; + HResult result = _projectVsServices.VsProject.IsDocumentInProject(filePath, out int isFound, priority, out uint itemid); + if (result.Failed || isFound == 0) + return (HierarchyId.Nil, null, null); - Assumes.False(itemid == HierarchyId.Nil); + Assumes.False(itemid == HierarchyId.Nil); - IVsContainedLanguageFactory? containedLanguageFactory = await _containedLanguageFactory.GetValueAsync(); + IVsContainedLanguageFactory? containedLanguageFactory = await _containedLanguageFactory.GetValueAsync(); - if (containedLanguageFactory is null) - return (HierarchyId.Nil, null, null); + if (containedLanguageFactory is null) + return (HierarchyId.Nil, null, null); - return (itemid, _projectVsServices.VsHierarchy, containedLanguageFactory); - } + return (itemid, _projectVsServices.VsHierarchy, containedLanguageFactory); + } - private async Task GetContainedLanguageFactoryAsync() - { - Guid languageServiceId = await GetLanguageServiceIdAsync(); - if (languageServiceId == Guid.Empty) - return null; + private async Task GetContainedLanguageFactoryAsync() + { + Guid languageServiceId = await GetLanguageServiceIdAsync(); + if (languageServiceId == Guid.Empty) + return null; - IOleAsyncServiceProvider serviceProvider = await _serviceProvider.GetValueAsync(); + IOleAsyncServiceProvider serviceProvider = await _serviceProvider.GetValueAsync(); - object? service = await serviceProvider.QueryServiceAsync(languageServiceId); + object? service = await serviceProvider.QueryServiceAsync(languageServiceId); - // NOTE: While this type is implemented in Roslyn, we force the cast on - // the UI thread because they are free to change this to an STA object - // which would result in an RPC call from a background thread. - await _projectVsServices.ThreadingService.SwitchToUIThread(); + // NOTE: While this type is implemented in Roslyn, we force the cast on + // the UI thread because they are free to change this to an STA object + // which would result in an RPC call from a background thread. + await _projectVsServices.ThreadingService.SwitchToUIThread(); - return service as IVsContainedLanguageFactory; - } + return service as IVsContainedLanguageFactory; + } - private async Task GetLanguageServiceIdAsync() - { - ConfigurationGeneral properties = await _projectVsServices.ActiveConfiguredProjectProperties.GetConfigurationGeneralPropertiesAsync(); + private async Task GetLanguageServiceIdAsync() + { + ConfigurationGeneral properties = await _projectVsServices.ActiveConfiguredProjectProperties.GetConfigurationGeneralPropertiesAsync(); - return await properties.LanguageServiceId.GetValueAsGuidAsync(); - } + return await properties.LanguageServiceId.GetValueAsGuidAsync(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.ProjectType.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.ProjectType.cs index ba6d0d3c7f..710a5e32c5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.ProjectType.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.ProjectType.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal partial class ProjectCapabilitiesMissingVetoProjectLoad { - internal partial class ProjectCapabilitiesMissingVetoProjectLoad + private class ProjectType { - private class ProjectType + public ProjectType(string extension, IEnumerable capabilities) { - public ProjectType(string extension, IEnumerable capabilities) - { - Extension = extension; - Capabilities = capabilities.ToImmutableArray(); - } - - public string Extension; - public ImmutableArray Capabilities; + Extension = extension; + Capabilities = capabilities.ToImmutableArray(); } + + public string Extension; + public ImmutableArray Capabilities; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.cs index defc8c24ce..fc94a91ca2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoad.cs @@ -5,72 +5,71 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Verifies that the project was loaded with the expected capabilities to catch +/// when the project type in the solution does not match the project itself, for +/// example when the user has renamed csproj -> vbproj without updating the project +/// type in the solution. +/// +[Export(typeof(IVetoProjectPreLoad))] +[AppliesTo(ProjectCapability.DotNet)] +internal partial class ProjectCapabilitiesMissingVetoProjectLoad : IVetoProjectPreLoad { - /// - /// Verifies that the project was loaded with the expected capabilities to catch - /// when the project type in the solution does not match the project itself, for - /// example when the user has renamed csproj -> vbproj without updating the project - /// type in the solution. - /// - [Export(typeof(IVetoProjectPreLoad))] - [AppliesTo(ProjectCapability.DotNet)] - internal partial class ProjectCapabilitiesMissingVetoProjectLoad : IVetoProjectPreLoad + private static readonly ImmutableArray s_projectTypes = GetProjectTypes(); + private readonly UnconfiguredProject _project; + private readonly IProjectCapabilitiesService _projectCapabilitiesService; + + [ImportingConstructor] + public ProjectCapabilitiesMissingVetoProjectLoad(UnconfiguredProject project, IProjectCapabilitiesService projectCapabilitiesService) { - private static readonly ImmutableArray s_projectTypes = GetProjectTypes(); - private readonly UnconfiguredProject _project; - private readonly IProjectCapabilitiesService _projectCapabilitiesService; + _project = project; + _projectCapabilitiesService = projectCapabilitiesService; + } - [ImportingConstructor] - public ProjectCapabilitiesMissingVetoProjectLoad(UnconfiguredProject project, IProjectCapabilitiesService projectCapabilitiesService) - { - _project = project; - _projectCapabilitiesService = projectCapabilitiesService; - } + public Task AllowProjectLoadAsync(bool isNewProject, ProjectConfiguration activeConfiguration, CancellationToken cancellationToken = default) + { + ProjectType? projectType = GetCurrentProjectType(); + if (projectType is null) // Unrecognized, probably a Shared Project + return TaskResult.True; - public Task AllowProjectLoadAsync(bool isNewProject, ProjectConfiguration activeConfiguration, CancellationToken cancellationToken = default) + foreach (string capability in projectType.Capabilities) { - ProjectType? projectType = GetCurrentProjectType(); - if (projectType is null) // Unrecognized, probably a Shared Project - return TaskResult.True; - - foreach (string capability in projectType.Capabilities) + if (!_projectCapabilitiesService.Contains(capability)) { - if (!_projectCapabilitiesService.Contains(capability)) - { - // Throw instead of returning false so that we can control message and the HRESULT - throw new COMException(string.Format(CultureInfo.CurrentCulture, Resources.ProjectLoadedWithWrongProjectType, _project.FullPath), - HResult.Fail); - } + // Throw instead of returning false so that we can control message and the HRESULT + throw new COMException(string.Format(CultureInfo.CurrentCulture, Resources.ProjectLoadedWithWrongProjectType, _project.FullPath), + HResult.Fail); } - - return TaskResult.True; } - private ProjectType? GetCurrentProjectType() - { - Assumes.True(!s_projectTypes.IsEmpty); + return TaskResult.True; + } + + private ProjectType? GetCurrentProjectType() + { + Assumes.True(!s_projectTypes.IsEmpty); - string extension = Path.GetExtension(_project.FullPath); + string extension = Path.GetExtension(_project.FullPath); - foreach (ProjectType projectType in s_projectTypes) + foreach (ProjectType projectType in s_projectTypes) + { + if (StringComparers.Paths.Equals(projectType.Extension, extension)) { - if (StringComparers.Paths.Equals(projectType.Extension, extension)) - { - return projectType; - } + return projectType; } - - return null; } - private static ImmutableArray GetProjectTypes() - { - Assembly assembly = typeof(ProjectCapabilitiesMissingVetoProjectLoad).Assembly; + return null; + } - return assembly.GetCustomAttributes() - .Select(a => new ProjectType('.' + a.DefaultProjectExtension, a.Capabilities!.Split(new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries))) - .ToImmutableArray(); - } + private static ImmutableArray GetProjectTypes() + { + Assembly assembly = typeof(ProjectCapabilitiesMissingVetoProjectLoad).Assembly; + + return assembly.GetCustomAttributes() + .Select(a => new ProjectType('.' + a.DefaultProjectExtension, a.Capabilities!.Split(new[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries))) + .ToImmutableArray(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs index 6d2d7d04fa..244173bacb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectSystemOptions.cs @@ -3,102 +3,101 @@ using Microsoft.Internal.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Settings; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[Export(typeof(IProjectSystemOptions))] +internal class ProjectSystemOptions : IProjectSystemOptions { - [Export(typeof(IProjectSystemOptions))] - internal class ProjectSystemOptions : IProjectSystemOptions + private const string FastUpToDateEnabledSettingKey = @"ManagedProjectSystem\FastUpToDateCheckEnabled"; + private const string FastUpToDateLogLevelSettingKey = @"ManagedProjectSystem\FastUpToDateLogLevel"; + private const string UseDesignerByDefaultSettingKey = @"ManagedProjectSystem\UseDesignerByDefault"; + private const string PreferSingleTargetBuildsForStartupProjects = @"ManagedProjectSystem\PreferSingleTargetBuilds"; + + // This setting exists as an option in Roslyn repo: 'FeatureOnOffOptions.SkipAnalyzersForImplicitlyTriggeredBuilds'. + // Do not change this setting key unless the Roslyn option name is changed. + internal const string SkipAnalyzersForImplicitlyTriggeredBuildSettingKey = "TextEditor.SkipAnalyzersForImplicitlyTriggeredBuilds"; + + private readonly IVsService _settingsManager; + private readonly IVsService _featureFlagsService; + + [ImportingConstructor] + public ProjectSystemOptions( + IVsService settingsManager, + IVsService featureFlagsService, + IProjectThreadingService threadingService) + { + _settingsManager = settingsManager; + _featureFlagsService = featureFlagsService; + } + + public Task GetIsFastUpToDateCheckEnabledAsync(CancellationToken cancellationToken) + { + return GetSettingValueOrDefaultAsync(FastUpToDateEnabledSettingKey, defaultValue: true, cancellationToken); + } + + public Task GetFastUpToDateLoggingLevelAsync(CancellationToken cancellationToken) + { + return GetSettingValueOrDefaultAsync(FastUpToDateLogLevelSettingKey, defaultValue: LogLevel.None, cancellationToken); + } + + public Task GetUseDesignerByDefaultAsync(string designerCategory, bool defaultValue, CancellationToken cancellationToken) + { + return GetSettingValueOrDefaultAsync(UseDesignerByDefaultSettingKey + "\\" + designerCategory, defaultValue, cancellationToken); + } + + public Task SetUseDesignerByDefaultAsync(string designerCategory, bool value, CancellationToken cancellationToken) + { + return SetSettingValueAsync(UseDesignerByDefaultSettingKey + "\\" + designerCategory, value, cancellationToken); + } + + public Task GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(CancellationToken cancellationToken) + { + return GetSettingValueOrDefaultAsync(SkipAnalyzersForImplicitlyTriggeredBuildSettingKey, defaultValue: true, cancellationToken); + } + + public Task GetPreferSingleTargetBuildsForStartupProjectsAsync(CancellationToken cancellationToken) + { + return GetSettingValueOrDefaultAsync(PreferSingleTargetBuildsForStartupProjects, defaultValue: true, cancellationToken); + } + + private async Task GetSettingValueOrDefaultAsync(string name, T defaultValue, CancellationToken cancellationToken) { - private const string FastUpToDateEnabledSettingKey = @"ManagedProjectSystem\FastUpToDateCheckEnabled"; - private const string FastUpToDateLogLevelSettingKey = @"ManagedProjectSystem\FastUpToDateLogLevel"; - private const string UseDesignerByDefaultSettingKey = @"ManagedProjectSystem\UseDesignerByDefault"; - private const string PreferSingleTargetBuildsForStartupProjects = @"ManagedProjectSystem\PreferSingleTargetBuilds"; - - // This setting exists as an option in Roslyn repo: 'FeatureOnOffOptions.SkipAnalyzersForImplicitlyTriggeredBuilds'. - // Do not change this setting key unless the Roslyn option name is changed. - internal const string SkipAnalyzersForImplicitlyTriggeredBuildSettingKey = "TextEditor.SkipAnalyzersForImplicitlyTriggeredBuilds"; - - private readonly IVsService _settingsManager; - private readonly IVsService _featureFlagsService; - - [ImportingConstructor] - public ProjectSystemOptions( - IVsService settingsManager, - IVsService featureFlagsService, - IProjectThreadingService threadingService) - { - _settingsManager = settingsManager; - _featureFlagsService = featureFlagsService; - } - - public Task GetIsFastUpToDateCheckEnabledAsync(CancellationToken cancellationToken) - { - return GetSettingValueOrDefaultAsync(FastUpToDateEnabledSettingKey, defaultValue: true, cancellationToken); - } - - public Task GetFastUpToDateLoggingLevelAsync(CancellationToken cancellationToken) - { - return GetSettingValueOrDefaultAsync(FastUpToDateLogLevelSettingKey, defaultValue: LogLevel.None, cancellationToken); - } - - public Task GetUseDesignerByDefaultAsync(string designerCategory, bool defaultValue, CancellationToken cancellationToken) - { - return GetSettingValueOrDefaultAsync(UseDesignerByDefaultSettingKey + "\\" + designerCategory, defaultValue, cancellationToken); - } - - public Task SetUseDesignerByDefaultAsync(string designerCategory, bool value, CancellationToken cancellationToken) - { - return SetSettingValueAsync(UseDesignerByDefaultSettingKey + "\\" + designerCategory, value, cancellationToken); - } - - public Task GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(CancellationToken cancellationToken) - { - return GetSettingValueOrDefaultAsync(SkipAnalyzersForImplicitlyTriggeredBuildSettingKey, defaultValue: true, cancellationToken); - } - - public Task GetPreferSingleTargetBuildsForStartupProjectsAsync(CancellationToken cancellationToken) - { - return GetSettingValueOrDefaultAsync(PreferSingleTargetBuildsForStartupProjects, defaultValue: true, cancellationToken); - } - - private async Task GetSettingValueOrDefaultAsync(string name, T defaultValue, CancellationToken cancellationToken) - { - ISettingsManager settingsManager = await _settingsManager.GetValueAsync(cancellationToken); - - return settingsManager.GetValueOrDefault(name, defaultValue); - } - - private async Task SetSettingValueAsync(string name, object value, CancellationToken cancellationToken) - { - ISettingsManager settingsManager = await _settingsManager.GetValueAsync(cancellationToken); - - await settingsManager.SetValueAsync(name, value, isMachineLocal: false); - } - - public ValueTask IsIncrementalBuildFailureOutputLoggingEnabledAsync(CancellationToken cancellationToken) - { - return IsFlagEnabledAsync(FeatureFlagNames.EnableIncrementalBuildFailureOutputLogging, defaultValue: false, cancellationToken); - } - - public ValueTask IsIncrementalBuildFailureTelemetryEnabledAsync(CancellationToken cancellationToken) - { - return IsFlagEnabledAsync(FeatureFlagNames.EnableIncrementalBuildFailureTelemetry, defaultValue: false, cancellationToken); - } - - public ValueTask IsBuildAccelerationEnabledByDefaultAsync(CancellationToken cancellationToken) - { - return IsFlagEnabledAsync(FeatureFlagNames.EnableBuildAccelerationByDefault, defaultValue: false, cancellationToken); - } - - public ValueTask IsLspPullDiagnosticsEnabledAsync(CancellationToken cancellationToken) - { - return IsFlagEnabledAsync(FeatureFlagNames.LspPullDiagnosticsFeatureFlagName, defaultValue: false, cancellationToken); - } - - private async ValueTask IsFlagEnabledAsync(string featureName, bool defaultValue, CancellationToken cancellationToken) - { - IVsFeatureFlags featureFlags = await _featureFlagsService.GetValueAsync(cancellationToken); - - return featureFlags.IsFeatureEnabled(featureName, defaultValue); - } + ISettingsManager settingsManager = await _settingsManager.GetValueAsync(cancellationToken); + + return settingsManager.GetValueOrDefault(name, defaultValue); + } + + private async Task SetSettingValueAsync(string name, object value, CancellationToken cancellationToken) + { + ISettingsManager settingsManager = await _settingsManager.GetValueAsync(cancellationToken); + + await settingsManager.SetValueAsync(name, value, isMachineLocal: false); + } + + public ValueTask IsIncrementalBuildFailureOutputLoggingEnabledAsync(CancellationToken cancellationToken) + { + return IsFlagEnabledAsync(FeatureFlagNames.EnableIncrementalBuildFailureOutputLogging, defaultValue: false, cancellationToken); + } + + public ValueTask IsIncrementalBuildFailureTelemetryEnabledAsync(CancellationToken cancellationToken) + { + return IsFlagEnabledAsync(FeatureFlagNames.EnableIncrementalBuildFailureTelemetry, defaultValue: false, cancellationToken); + } + + public ValueTask IsBuildAccelerationEnabledByDefaultAsync(CancellationToken cancellationToken) + { + return IsFlagEnabledAsync(FeatureFlagNames.EnableBuildAccelerationByDefault, defaultValue: false, cancellationToken); + } + + public ValueTask IsLspPullDiagnosticsEnabledAsync(CancellationToken cancellationToken) + { + return IsFlagEnabledAsync(FeatureFlagNames.LspPullDiagnosticsFeatureFlagName, defaultValue: false, cancellationToken); + } + + private async ValueTask IsFlagEnabledAsync(string featureName, bool defaultValue, CancellationToken cancellationToken) + { + IVsFeatureFlags featureFlags = await _featureFlagsService.GetValueAsync(cancellationToken); + + return featureFlags.IsFeatureEnabled(featureName, defaultValue); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectTreeVsExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectTreeVsExtensions.cs index f7d52afb21..35aa64420d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectTreeVsExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/ProjectTreeVsExtensions.cs @@ -2,19 +2,18 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class ProjectTreeVsExtensions { - internal static class ProjectTreeVsExtensions + /// + /// Returns the specified for the specified node. + /// + /// + /// This method should only be called on and the result used on the UI thread. + /// + public static HierarchyId GetHierarchyId(this IProjectTree node) { - /// - /// Returns the specified for the specified node. - /// - /// - /// This method should only be called on and the result used on the UI thread. - /// - public static HierarchyId GetHierarchyId(this IProjectTree node) - { - return new HierarchyId(node.IsRoot() ? VSConstants.VSITEMID_ROOT : unchecked((uint)node.Identity)); - } + return new HierarchyId(node.IsRoot() ? VSConstants.VSITEMID_ROOT : unchecked((uint)node.Identity)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AbstractProjectConfigurationProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AbstractProjectConfigurationProperties.cs index f4d2d38539..424147549a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AbstractProjectConfigurationProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AbstractProjectConfigurationProperties.cs @@ -3,220 +3,219 @@ using VSLangProj; using VSLangProj80; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public abstract class AbstractProjectConfigurationProperties : ProjectConfigurationProperties3 { - public abstract class AbstractProjectConfigurationProperties : ProjectConfigurationProperties3 - { - private readonly ProjectProperties _projectProperties; - private readonly IProjectThreadingService _threadingService; + private readonly ProjectProperties _projectProperties; + private readonly IProjectThreadingService _threadingService; - internal AbstractProjectConfigurationProperties( - ProjectProperties projectProperties, - IProjectThreadingService threadingService) - { - Requires.NotNull(projectProperties); - Requires.NotNull(threadingService); + internal AbstractProjectConfigurationProperties( + ProjectProperties projectProperties, + IProjectThreadingService threadingService) + { + Requires.NotNull(projectProperties); + Requires.NotNull(threadingService); - _projectProperties = projectProperties; - _threadingService = threadingService; - } + _projectProperties = projectProperties; + _threadingService = threadingService; + } - public string LanguageVersion + public string LanguageVersion + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - return await browseObjectProperties.LangVersion.GetEvaluatedValueAtEndAsync(); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + return await browseObjectProperties.LangVersion.GetEvaluatedValueAtEndAsync(); + }); + } - set + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.LangVersion.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.LangVersion.SetValueAsync(value); + }); } + } - public string CodeAnalysisRuleSet + public string CodeAnalysisRuleSet + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - return await browseObjectProperties.CodeAnalysisRuleSet.GetEvaluatedValueAtEndAsync(); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + return await browseObjectProperties.CodeAnalysisRuleSet.GetEvaluatedValueAtEndAsync(); + }); + } - set + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.CodeAnalysisRuleSet.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.CodeAnalysisRuleSet.SetValueAsync(value); + }); } + } - public string OutputPath + public string OutputPath + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - return await browseObjectProperties.OutputPath.GetEvaluatedValueAtEndAsync(); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + return await browseObjectProperties.OutputPath.GetEvaluatedValueAtEndAsync(); + }); + } - set + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.OutputPath.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.OutputPath.SetValueAsync(value); + }); } + } - public string PlatformTarget + public string PlatformTarget + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - return await browseObjectProperties.PlatformTarget.GetEvaluatedValueAtEndAsync(); - }); - } - set + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + return await browseObjectProperties.PlatformTarget.GetEvaluatedValueAtEndAsync(); + }); + } + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.PlatformTarget.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.PlatformTarget.SetValueAsync(value); + }); } + } - public string IntermediatePath + public string IntermediatePath + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - return await browseObjectProperties.IntermediatePath.GetEvaluatedValueAtEndAsync(); - }); - } - set + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + return await browseObjectProperties.IntermediatePath.GetEvaluatedValueAtEndAsync(); + }); + } + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.IntermediatePath.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.IntermediatePath.SetValueAsync(value); + }); } - public bool RunCodeAnalysis + } + public bool RunCodeAnalysis + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - object? value = await browseObjectProperties.RunCodeAnalysis.GetValueAsync(); - return ((bool?)value).GetValueOrDefault(); - }); - } - set + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + object? value = await browseObjectProperties.RunCodeAnalysis.GetValueAsync(); + return ((bool?)value).GetValueOrDefault(); + }); + } + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.RunCodeAnalysis.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.RunCodeAnalysis.SetValueAsync(value); + }); } + } - public object? ExtenderNames => null; - public string __id => throw new NotImplementedException(); - public bool DebugSymbols { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool DefineDebug { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool DefineTrace { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string DefineConstants { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool RemoveIntegerChecks { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public uint BaseAddress { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool AllowUnsafeBlocks { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CheckForOverflowUnderflow { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string DocumentationFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool Optimize { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool IncrementalBuild { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string StartProgram { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string StartWorkingDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string StartURL { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string StartPage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string StartArguments { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool StartWithIE { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool EnableASPDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool EnableASPXDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool EnableUnmanagedDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public prjStartAction StartAction { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public object get_Extender(string arg) => throw new NotImplementedException(); - public string ExtenderCATID => throw new NotImplementedException(); - public prjWarningLevel WarningLevel { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool TreatWarningsAsErrors { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool EnableSQLServerDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public uint FileAlignment { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool RegisterForComInterop { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string ConfigurationOverrideFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool RemoteDebugEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string RemoteDebugMachine { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string NoWarn { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool NoStdLib { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string DebugInfo + public object? ExtenderNames => null; + public string __id => throw new NotImplementedException(); + public bool DebugSymbols { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool DefineDebug { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool DefineTrace { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string DefineConstants { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool RemoveIntegerChecks { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public uint BaseAddress { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool AllowUnsafeBlocks { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CheckForOverflowUnderflow { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string DocumentationFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool Optimize { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool IncrementalBuild { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string StartProgram { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string StartWorkingDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string StartURL { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string StartPage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string StartArguments { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool StartWithIE { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool EnableASPDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool EnableASPXDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool EnableUnmanagedDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public prjStartAction StartAction { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public object get_Extender(string arg) => throw new NotImplementedException(); + public string ExtenderCATID => throw new NotImplementedException(); + public prjWarningLevel WarningLevel { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool TreatWarningsAsErrors { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool EnableSQLServerDebugging { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public uint FileAlignment { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool RegisterForComInterop { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string ConfigurationOverrideFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool RemoteDebugEnabled { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string RemoteDebugMachine { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string NoWarn { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool NoStdLib { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string DebugInfo + { + get { - get + return _threadingService.ExecuteSynchronously(async () => { - return _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - return await browseObjectProperties.DebugInfo.GetEvaluatedValueAtEndAsync(); - }); - } - set + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + return await browseObjectProperties.DebugInfo.GetEvaluatedValueAtEndAsync(); + }); + } + set + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); - await browseObjectProperties.DebugInfo.SetValueAsync(value); - }); - } + ConfiguredBrowseObject browseObjectProperties = await _projectProperties.GetConfiguredBrowseObjectPropertiesAsync(); + await browseObjectProperties.DebugInfo.SetValueAsync(value); + }); } - public string TreatSpecificWarningsAsErrors { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisLogFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisRuleAssemblies { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisInputAssembly { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisRules { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisSpellCheckLanguages { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CodeAnalysisUseTypeNameInSuppression { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisModuleSuppressionsFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool UseVSHostingProcess { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public sgenGenerationOption GenerateSerializationAssemblies { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CodeAnalysisIgnoreGeneratedCode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CodeAnalysisOverrideRuleVisibilities { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisDictionaries { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisCulture { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisRuleSetDirectories { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CodeAnalysisIgnoreBuiltInRuleSets { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public string CodeAnalysisRuleDirectories { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CodeAnalysisIgnoreBuiltInRules { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool CodeAnalysisFailOnMissingRules { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public bool Prefer32Bit { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } + public string TreatSpecificWarningsAsErrors { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisLogFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisRuleAssemblies { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisInputAssembly { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisRules { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisSpellCheckLanguages { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CodeAnalysisUseTypeNameInSuppression { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisModuleSuppressionsFile { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool UseVSHostingProcess { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public sgenGenerationOption GenerateSerializationAssemblies { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CodeAnalysisIgnoreGeneratedCode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CodeAnalysisOverrideRuleVisibilities { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisDictionaries { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisCulture { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisRuleSetDirectories { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CodeAnalysisIgnoreBuiltInRuleSets { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string CodeAnalysisRuleDirectories { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CodeAnalysisIgnoreBuiltInRules { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool CodeAnalysisFailOnMissingRules { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool Prefer32Bit { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs index 95d8b2e862..b6e06e8b0d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AuthenticationModeEnumProvider.cs @@ -5,54 +5,53 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +[ExportDynamicEnumValuesProvider(nameof(AuthenticationModeEnumProvider))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +internal class AuthenticationModeEnumProvider : IDynamicEnumValuesProvider { - [ExportDynamicEnumValuesProvider(nameof(AuthenticationModeEnumProvider))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - internal class AuthenticationModeEnumProvider : IDynamicEnumValuesProvider + private readonly IRemoteDebuggerAuthenticationService _remoteDebuggerAuthenticationService; + + [ImportingConstructor] + public AuthenticationModeEnumProvider(IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService) + { + _remoteDebuggerAuthenticationService = remoteDebuggerAuthenticationService; + } + + public Task GetProviderAsync(IList? options) + { + return Task.FromResult(new AuthenticationModeEnumValuesGenerator(_remoteDebuggerAuthenticationService)); + } + + private class AuthenticationModeEnumValuesGenerator : IDynamicEnumValuesGenerator { private readonly IRemoteDebuggerAuthenticationService _remoteDebuggerAuthenticationService; - [ImportingConstructor] - public AuthenticationModeEnumProvider(IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService) + public AuthenticationModeEnumValuesGenerator(IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService) { _remoteDebuggerAuthenticationService = remoteDebuggerAuthenticationService; } - public Task GetProviderAsync(IList? options) - { - return Task.FromResult(new AuthenticationModeEnumValuesGenerator(_remoteDebuggerAuthenticationService)); - } + public bool AllowCustomValues => false; - private class AuthenticationModeEnumValuesGenerator : IDynamicEnumValuesGenerator + public Task> GetListedValuesAsync() { - private readonly IRemoteDebuggerAuthenticationService _remoteDebuggerAuthenticationService; - - public AuthenticationModeEnumValuesGenerator(IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService) - { - _remoteDebuggerAuthenticationService = remoteDebuggerAuthenticationService; - } - - public bool AllowCustomValues => false; - - public Task> GetListedValuesAsync() - { - var enumValues = _remoteDebuggerAuthenticationService - .GetRemoteAuthenticationModes() - .Select(i => new PageEnumValue(new EnumValue - { - Name = i.Name, - DisplayName = i.DisplayName - })) - .ToArray(); - - return Task.FromResult>(enumValues); - } - - /// - /// The user can't add arbitrary authentication modes, so this method is unsupported. - /// - public Task TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); + var enumValues = _remoteDebuggerAuthenticationService + .GetRemoteAuthenticationModes() + .Select(i => new PageEnumValue(new EnumValue + { + Name = i.Name, + DisplayName = i.DisplayName + })) + .ToArray(); + + return Task.FromResult>(enumValues); } + + /// + /// The user can't add arbitrary authentication modes, so this method is unsupported. + /// + public Task TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProvider.cs index aef56bf6d8..d9af9bde5b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProvider.cs @@ -5,96 +5,95 @@ using Microsoft.VisualStudio.Build; using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +/// +/// Implementation of that avoids persisting the +/// project GUID property to the project file if isn't already present in the file. +/// +[Export(typeof(IProjectGuidStorageProvider))] +[AppliesTo(ProjectCapability.DotNet)] +[Order(Order.Default)] +internal class AvoidPersistingProjectGuidStorageProvider : IProjectGuidStorageProvider { - /// - /// Implementation of that avoids persisting the - /// project GUID property to the project file if isn't already present in the file. - /// - [Export(typeof(IProjectGuidStorageProvider))] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - internal class AvoidPersistingProjectGuidStorageProvider : IProjectGuidStorageProvider + private readonly IProjectAccessor _projectAccessor; + private readonly UnconfiguredProject _project; + private bool? _isPersistedInProject; + + [ImportingConstructor] + internal AvoidPersistingProjectGuidStorageProvider(IProjectAccessor projectAccessor, UnconfiguredProject project) { - private readonly IProjectAccessor _projectAccessor; - private readonly UnconfiguredProject _project; - private bool? _isPersistedInProject; + _projectAccessor = projectAccessor; + _project = project; + } - [ImportingConstructor] - internal AvoidPersistingProjectGuidStorageProvider(IProjectAccessor projectAccessor, UnconfiguredProject project) + public Task GetProjectGuidAsync() + { + // We use the construction model to avoid evaluating during asynchronous project load + return _projectAccessor.OpenProjectXmlForReadAsync(_project, projectXml => { - _projectAccessor = projectAccessor; - _project = project; - } + ProjectPropertyElement? property = FindProjectGuidProperty(projectXml); + if (property is not null) + { + _isPersistedInProject = true; - public Task GetProjectGuidAsync() - { - // We use the construction model to avoid evaluating during asynchronous project load - return _projectAccessor.OpenProjectXmlForReadAsync(_project, projectXml => + TryParseGuid(property, out Guid result); + return result; + } + else { - ProjectPropertyElement? property = FindProjectGuidProperty(projectXml); - if (property is not null) - { - _isPersistedInProject = true; + _isPersistedInProject = false; + } - TryParseGuid(property, out Guid result); - return result; - } - else - { - _isPersistedInProject = false; - } + return Guid.Empty; + }); + } - return Guid.Empty; - }); - } + public Task SetProjectGuidAsync(Guid value) + { + // Avoid searching for the if we've already checked previously in GetProjectGuidAsync. + // This handles project open, avoids us needed to take another read-lock during setting of it. + // + // Technically a project could add a latter down the track by editing the project or + // reloading from disk, however, both the solution, CPS and other components within Visual Studio + // do not handle the GUID changing underneath them. + if (_isPersistedInProject == false) + return Task.CompletedTask; - public Task SetProjectGuidAsync(Guid value) + return _projectAccessor.OpenProjectXmlForUpgradeableReadAsync(_project, async (projectXml, cancellationToken) => { - // Avoid searching for the if we've already checked previously in GetProjectGuidAsync. - // This handles project open, avoids us needed to take another read-lock during setting of it. - // - // Technically a project could add a latter down the track by editing the project or - // reloading from disk, however, both the solution, CPS and other components within Visual Studio - // do not handle the GUID changing underneath them. - if (_isPersistedInProject == false) - return Task.CompletedTask; - - return _projectAccessor.OpenProjectXmlForUpgradeableReadAsync(_project, async (projectXml, cancellationToken) => + ProjectPropertyElement property = FindProjectGuidProperty(projectXml); + if (property is not null) { - ProjectPropertyElement property = FindProjectGuidProperty(projectXml); - if (property is not null) - { - _isPersistedInProject = true; + _isPersistedInProject = true; - // Avoid touching the project file unless the actual GUID has changed, regardless of format - if (!TryParseGuid(property, out Guid result) || value != result) - { - await _projectAccessor.OpenProjectXmlForWriteAsync(_project, (root) => - { - property.Value = ProjectCollection.Escape(value.ToString("B").ToUpperInvariant()); - }, cancellationToken); - } - } - else + // Avoid touching the project file unless the actual GUID has changed, regardless of format + if (!TryParseGuid(property, out Guid result) || value != result) { - _isPersistedInProject = false; + await _projectAccessor.OpenProjectXmlForWriteAsync(_project, (root) => + { + property.Value = ProjectCollection.Escape(value.ToString("B").ToUpperInvariant()); + }, cancellationToken); } - }); - } + } + else + { + _isPersistedInProject = false; + } + }); + } - private static ProjectPropertyElement FindProjectGuidProperty(ProjectRootElement projectXml) - { - // NOTE: Unlike evaluation, we return the first to mimic legacy project system behavior - return projectXml.PropertyGroups.SelectMany(group => group.Properties) - .FirstOrDefault(p => StringComparers.PropertyNames.Equals(BuildProperty.ProjectGuid, p.Name)); - } + private static ProjectPropertyElement FindProjectGuidProperty(ProjectRootElement projectXml) + { + // NOTE: Unlike evaluation, we return the first to mimic legacy project system behavior + return projectXml.PropertyGroups.SelectMany(group => group.Properties) + .FirstOrDefault(p => StringComparers.PropertyNames.Equals(BuildProperty.ProjectGuid, p.Name)); + } - private static bool TryParseGuid(ProjectPropertyElement property, out Guid result) - { - string unescapedValue = property.GetUnescapedValue(); + private static bool TryParseGuid(ProjectPropertyElement property, out Guid result) + { + string unescapedValue = property.GetUnescapedValue(); - return Guid.TryParse(unescapedValue, out result); - } + return Guid.TryParse(unescapedValue, out result); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationProperties.cs index d1a450322c..4e6f4b71fb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationProperties.cs @@ -3,23 +3,22 @@ using VSLangProj110; using VSLangProj80; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp; + +[Export(ExportContractNames.VsTypes.ConfiguredProjectPropertiesAutomationObject)] +[Order(Order.Default)] +[AppliesTo(ProjectCapability.CSharp)] +public class CSharpProjectConfigurationProperties : AbstractProjectConfigurationProperties, + CSharpProjectConfigurationProperties3, + CSharpProjectConfigurationProperties6 { - [Export(ExportContractNames.VsTypes.ConfiguredProjectPropertiesAutomationObject)] - [Order(Order.Default)] - [AppliesTo(ProjectCapability.CSharp)] - public class CSharpProjectConfigurationProperties : AbstractProjectConfigurationProperties, - CSharpProjectConfigurationProperties3, - CSharpProjectConfigurationProperties6 + [ImportingConstructor] + internal CSharpProjectConfigurationProperties( + ProjectProperties projectProperties, + IProjectThreadingService threadingService) + : base(projectProperties, threadingService) { - [ImportingConstructor] - internal CSharpProjectConfigurationProperties( - ProjectProperties projectProperties, - IProjectThreadingService threadingService) - : base(projectProperties, threadingService) - { - } - - public string ErrorReport { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } + + public string ErrorReport { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPage.cs index 4ed1769db2..5621f96d79 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPage.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp; + +/// +/// Provides common well-known C# project property pages. +/// +internal static class CSharpProjectDesignerPage { - /// - /// Provides common well-known C# project property pages. - /// - internal static class CSharpProjectDesignerPage - { - public static readonly ProjectDesignerPageMetadata Application = new(new Guid("{5E9A8AC2-4F34-4521-858F-4C248BA31532}"), pageOrder: 0, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Build = new(new Guid("{A54AD834-9219-4aa6-B589-607AF21C3E26}"), pageOrder: 1, hasConfigurationCondition: true); - public static readonly ProjectDesignerPageMetadata BuildEvents = new(new Guid("{1E78F8DB-6C07-4d61-A18F-7514010ABD56}"), pageOrder: 2, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Package = new(new Guid("{21b78be8-3957-4caa-bf2f-e626107da58e}"), pageOrder: 3, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Debug = new(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"), pageOrder: 4, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata ReferencePaths = new(new Guid("{031911C8-6148-4e25-B1B1-44BCA9A0C45C}"), pageOrder: 5, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Signing = new(new Guid("{F8D6553F-F752-4DBF-ACB6-F291B744A792}"), pageOrder: 6, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata CodeAnalysis = new(new Guid("{c02f393c-8a1e-480d-aa82-6a75d693559d}"), pageOrder: 7, hasConfigurationCondition: false); - } + public static readonly ProjectDesignerPageMetadata Application = new(new Guid("{5E9A8AC2-4F34-4521-858F-4C248BA31532}"), pageOrder: 0, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Build = new(new Guid("{A54AD834-9219-4aa6-B589-607AF21C3E26}"), pageOrder: 1, hasConfigurationCondition: true); + public static readonly ProjectDesignerPageMetadata BuildEvents = new(new Guid("{1E78F8DB-6C07-4d61-A18F-7514010ABD56}"), pageOrder: 2, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Package = new(new Guid("{21b78be8-3957-4caa-bf2f-e626107da58e}"), pageOrder: 3, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Debug = new(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"), pageOrder: 4, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata ReferencePaths = new(new Guid("{031911C8-6148-4e25-B1B1-44BCA9A0C45C}"), pageOrder: 5, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Signing = new(new Guid("{F8D6553F-F752-4DBF-ACB6-F291B744A792}"), pageOrder: 6, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata CodeAnalysis = new(new Guid("{c02f393c-8a1e-480d-aa82-6a75d693559d}"), pageOrder: 7, hasConfigurationCondition: false); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProvider.cs index a9b17a4b59..70ad6562ed 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProvider.cs @@ -2,45 +2,44 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp; + +/// +/// Provides project designer property pages. +/// +[Export(typeof(IVsProjectDesignerPageProvider))] +[AppliesTo(ProjectCapability.CSharpAppDesigner)] +internal class CSharpProjectDesignerPageProvider : IVsProjectDesignerPageProvider { - /// - /// Provides project designer property pages. - /// - [Export(typeof(IVsProjectDesignerPageProvider))] - [AppliesTo(ProjectCapability.CSharpAppDesigner)] - internal class CSharpProjectDesignerPageProvider : IVsProjectDesignerPageProvider + private readonly IProjectCapabilitiesService _capabilities; + + [ImportingConstructor] + internal CSharpProjectDesignerPageProvider(IProjectCapabilitiesService capabilities) { - private readonly IProjectCapabilitiesService _capabilities; + _capabilities = capabilities; + } + + public Task> GetPagesAsync() + { + var builder = PooledArray.GetInstance(capacity: 7); + + builder.Add(CSharpProjectDesignerPage.Application); + builder.Add(CSharpProjectDesignerPage.Build); + builder.Add(CSharpProjectDesignerPage.BuildEvents); - [ImportingConstructor] - internal CSharpProjectDesignerPageProvider(IProjectCapabilitiesService capabilities) + if (_capabilities.Contains(ProjectCapability.Pack)) { - _capabilities = capabilities; + builder.Add(CSharpProjectDesignerPage.Package); } - public Task> GetPagesAsync() + if (_capabilities.Contains(ProjectCapability.LaunchProfiles)) { - var builder = PooledArray.GetInstance(capacity: 7); - - builder.Add(CSharpProjectDesignerPage.Application); - builder.Add(CSharpProjectDesignerPage.Build); - builder.Add(CSharpProjectDesignerPage.BuildEvents); - - if (_capabilities.Contains(ProjectCapability.Pack)) - { - builder.Add(CSharpProjectDesignerPage.Package); - } - - if (_capabilities.Contains(ProjectCapability.LaunchProfiles)) - { - builder.Add(CSharpProjectDesignerPage.Debug); - } + builder.Add(CSharpProjectDesignerPage.Debug); + } - builder.Add(CSharpProjectDesignerPage.Signing); - builder.Add(CSharpProjectDesignerPage.CodeAnalysis); + builder.Add(CSharpProjectDesignerPage.Signing); + builder.Add(CSharpProjectDesignerPage.CodeAnalysis); - return Task.FromResult>(builder.ToImmutableAndFree()); - } + return Task.FromResult>(builder.ToImmutableAndFree()); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPage.cs index d3dd12fce1..99b9927980 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPage.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.FSharp; + +/// +/// Provides common well-known F# project property pages. +/// +internal static class FSharpProjectDesignerPage { - /// - /// Provides common well-known F# project property pages. - /// - internal static class FSharpProjectDesignerPage - { - public static readonly ProjectDesignerPageMetadata Application = new(new Guid("{6D2D9B56-2691-4624-A1BF-D07A14594748}"), pageOrder: 0, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Build = new(new Guid("{FAC0A17E-2E70-4211-916A-0D34FB708BFF}"), pageOrder: 1, hasConfigurationCondition: true); - public static readonly ProjectDesignerPageMetadata BuildEvents = new(new Guid("{DD84AA8F-71BB-462a-8EF8-C9992CB325B7}"), pageOrder: 2, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Debug = new(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"), pageOrder: 3, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Package = new(new Guid("{21b78be8-3957-4caa-bf2f-e626107da58e}"), pageOrder: 4, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata ReferencePaths = new(new Guid("{DF16B1A2-0E91-4499-AE60-C7144E614BF1}"), pageOrder: 5, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Signing = new(new Guid("{F8D6553F-F752-4DBF-ACB6-F291B744A792}"), pageOrder: 6, hasConfigurationCondition: false); - } + public static readonly ProjectDesignerPageMetadata Application = new(new Guid("{6D2D9B56-2691-4624-A1BF-D07A14594748}"), pageOrder: 0, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Build = new(new Guid("{FAC0A17E-2E70-4211-916A-0D34FB708BFF}"), pageOrder: 1, hasConfigurationCondition: true); + public static readonly ProjectDesignerPageMetadata BuildEvents = new(new Guid("{DD84AA8F-71BB-462a-8EF8-C9992CB325B7}"), pageOrder: 2, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Debug = new(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"), pageOrder: 3, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Package = new(new Guid("{21b78be8-3957-4caa-bf2f-e626107da58e}"), pageOrder: 4, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata ReferencePaths = new(new Guid("{DF16B1A2-0E91-4499-AE60-C7144E614BF1}"), pageOrder: 5, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Signing = new(new Guid("{F8D6553F-F752-4DBF-ACB6-F291B744A792}"), pageOrder: 6, hasConfigurationCondition: false); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProvider.cs index ffac193e2d..35834ef57e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProvider.cs @@ -2,45 +2,44 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.FSharp; + +/// +/// Provides project designer property pages. +/// +[Export(typeof(IVsProjectDesignerPageProvider))] +[AppliesTo(ProjectCapability.FSharpAppDesigner)] +internal class FSharpProjectDesignerPageProvider : IVsProjectDesignerPageProvider { - /// - /// Provides project designer property pages. - /// - [Export(typeof(IVsProjectDesignerPageProvider))] - [AppliesTo(ProjectCapability.FSharpAppDesigner)] - internal class FSharpProjectDesignerPageProvider : IVsProjectDesignerPageProvider + private readonly IProjectCapabilitiesService _capabilities; + + [ImportingConstructor] + internal FSharpProjectDesignerPageProvider(IProjectCapabilitiesService capabilities) { - private readonly IProjectCapabilitiesService _capabilities; + _capabilities = capabilities; + } + + public Task> GetPagesAsync() + { + var builder = PooledArray.GetInstance(capacity: 7); + + builder.Add(FSharpProjectDesignerPage.Application); + builder.Add(FSharpProjectDesignerPage.Build); + builder.Add(FSharpProjectDesignerPage.BuildEvents); - [ImportingConstructor] - internal FSharpProjectDesignerPageProvider(IProjectCapabilitiesService capabilities) + if (_capabilities.Contains(ProjectCapability.LaunchProfiles)) { - _capabilities = capabilities; + builder.Add(FSharpProjectDesignerPage.Debug); } - public Task> GetPagesAsync() + if (_capabilities.Contains(ProjectCapability.Pack)) { - var builder = PooledArray.GetInstance(capacity: 7); - - builder.Add(FSharpProjectDesignerPage.Application); - builder.Add(FSharpProjectDesignerPage.Build); - builder.Add(FSharpProjectDesignerPage.BuildEvents); - - if (_capabilities.Contains(ProjectCapability.LaunchProfiles)) - { - builder.Add(FSharpProjectDesignerPage.Debug); - } - - if (_capabilities.Contains(ProjectCapability.Pack)) - { - builder.Add(FSharpProjectDesignerPage.Package); - } + builder.Add(FSharpProjectDesignerPage.Package); + } - builder.Add(FSharpProjectDesignerPage.ReferencePaths); - builder.Add(FSharpProjectDesignerPage.Signing); + builder.Add(FSharpProjectDesignerPage.ReferencePaths); + builder.Add(FSharpProjectDesignerPage.Signing); - return Task.FromResult>(builder.ToImmutableAndFree()); - } + return Task.FromResult>(builder.ToImmutableAndFree()); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.AbstractBuildEventHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.AbstractBuildEventHelper.cs index 85a728132f..11441798a5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.AbstractBuildEventHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.AbstractBuildEventHelper.cs @@ -3,151 +3,150 @@ using Microsoft.Build.Construction; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; + +internal abstract partial class AbstractBuildEventValueProvider { - internal abstract partial class AbstractBuildEventValueProvider + public abstract class AbstractBuildEventHelper { - public abstract class AbstractBuildEventHelper + private const string ExecTask = "Exec"; + private const string Command = "Command"; + + protected AbstractBuildEventHelper(string buildEvent, + string targetName, + Func getTarget, + Action setTargetDependencies) { - private const string ExecTask = "Exec"; - private const string Command = "Command"; + BuildEvent = buildEvent; + TargetName = targetName; + GetTarget = getTarget; + SetTargetDependencies = setTargetDependencies; + } + + private Func GetTarget { get; } + private Action SetTargetDependencies { get; } + private string BuildEvent { get; } + private string TargetName { get; } - protected AbstractBuildEventHelper(string buildEvent, - string targetName, - Func getTarget, - Action setTargetDependencies) + public async Task<(bool success, string? value)> TryGetUnevaluatedPropertyValueAsync(IProjectProperties defaultProperties) + { + // check if value already exists + string? unevaluatedPropertyValue = await defaultProperties.GetUnevaluatedPropertyValueAsync(BuildEvent); + if (unevaluatedPropertyValue is not null) { - BuildEvent = buildEvent; - TargetName = targetName; - GetTarget = getTarget; - SetTargetDependencies = setTargetDependencies; + return (true, unevaluatedPropertyValue); } - private Func GetTarget { get; } - private Action SetTargetDependencies { get; } - private string BuildEvent { get; } - private string TargetName { get; } + return (false, null); + } - public async Task<(bool success, string? value)> TryGetUnevaluatedPropertyValueAsync(IProjectProperties defaultProperties) - { - // check if value already exists - string? unevaluatedPropertyValue = await defaultProperties.GetUnevaluatedPropertyValueAsync(BuildEvent); - if (unevaluatedPropertyValue is not null) - { - return (true, unevaluatedPropertyValue); - } + public async Task<(bool success, string value)> TryGetEvaluatedPropertyValueAsync(IProjectProperties defaultProperties) + { + string? unevaluatedPropertyValue = await defaultProperties.GetUnevaluatedPropertyValueAsync(BuildEvent); - return (false, null); + if (unevaluatedPropertyValue is null) + { + return (false, ""); } - public async Task<(bool success, string value)> TryGetEvaluatedPropertyValueAsync(IProjectProperties defaultProperties) - { - string? unevaluatedPropertyValue = await defaultProperties.GetUnevaluatedPropertyValueAsync(BuildEvent); + string evaluatedPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(BuildEvent); + return (true, evaluatedPropertyValue); + } - if (unevaluatedPropertyValue is null) - { - return (false, ""); - } + public string? TryGetValueFromTarget(ProjectRootElement projectXml) + { + ProjectTaskElement? execTask = FindExecTaskInTargets(projectXml); - string evaluatedPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(BuildEvent); - return (true, evaluatedPropertyValue); + if (execTask is null) + { + return null; } - public string? TryGetValueFromTarget(ProjectRootElement projectXml) + if (execTask.Parameters.TryGetValue(Command, out string commandText)) { - ProjectTaskElement? execTask = FindExecTaskInTargets(projectXml); + return commandText.Replace("%25", "%"); + } - if (execTask is null) - { - return null; - } + return null; // exec task as written in the project file is invalid, we should be resilient to this case. + } - if (execTask.Parameters.TryGetValue(Command, out string commandText)) - { - return commandText.Replace("%25", "%"); - } + public async Task TrySetPropertyAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + string? currentValue = await defaultProperties.GetUnevaluatedPropertyValueAsync(BuildEvent); + if (currentValue is null) + { + return false; + } - return null; // exec task as written in the project file is invalid, we should be resilient to this case. + if (OnlyWhitespaceCharacters(unevaluatedPropertyValue)) + { + await defaultProperties.DeletePropertyAsync(BuildEvent); + return true; } - public async Task TrySetPropertyAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) + await defaultProperties.SetPropertyValueAsync(BuildEvent, unevaluatedPropertyValue); + return true; + } + + public void SetProperty(string unevaluatedPropertyValue, ProjectRootElement projectXml) + { + if (OnlyWhitespaceCharacters(unevaluatedPropertyValue)) { - string? currentValue = await defaultProperties.GetUnevaluatedPropertyValueAsync(BuildEvent); - if (currentValue is null) - { - return false; - } + ProjectTargetElement? target = FindTargetToRemove(projectXml); - if (OnlyWhitespaceCharacters(unevaluatedPropertyValue)) + if (target is not null) { - await defaultProperties.DeletePropertyAsync(BuildEvent); - return true; + projectXml.RemoveChild(target); + return; } - - await defaultProperties.SetPropertyValueAsync(BuildEvent, unevaluatedPropertyValue); - return true; } - public void SetProperty(string unevaluatedPropertyValue, ProjectRootElement projectXml) - { - if (OnlyWhitespaceCharacters(unevaluatedPropertyValue)) - { - ProjectTargetElement? target = FindTargetToRemove(projectXml); + SetParameter(projectXml, unevaluatedPropertyValue); + } - if (target is not null) - { - projectXml.RemoveChild(target); - return; - } - } + private static bool OnlyWhitespaceCharacters(string unevaluatedPropertyValue) + => string.IsNullOrWhiteSpace(unevaluatedPropertyValue) && + !unevaluatedPropertyValue.Contains("\n"); - SetParameter(projectXml, unevaluatedPropertyValue); - } + private ProjectTaskElement? FindExecTaskInTargets(ProjectRootElement projectXml) + { + return projectXml.Targets + .Where(target => + StringComparers.TargetNames.Equals(GetTarget(target), BuildEvent) && + StringComparers.TargetNames.Equals(target.Name, TargetName)) + .SelectMany(target => target.Tasks) + .FirstOrDefault(task => StringComparers.TargetNames.Equals(task.Name, ExecTask)); + } - private static bool OnlyWhitespaceCharacters(string unevaluatedPropertyValue) - => string.IsNullOrWhiteSpace(unevaluatedPropertyValue) && - !unevaluatedPropertyValue.Contains("\n"); + private ProjectTargetElement? FindTargetToRemove(ProjectRootElement projectXml) + { + return projectXml.Targets + .FirstOrDefault(target => + StringComparers.TargetNames.Equals(GetTarget(target), BuildEvent) && + StringComparers.TargetNames.Equals(target.Name, TargetName) && + target.Children.Count == 1 && + target.Tasks.Count == 1 && + StringComparers.TargetNames.Equals(target.Tasks.First().Name, ExecTask)); + } - private ProjectTaskElement? FindExecTaskInTargets(ProjectRootElement projectXml) - { - return projectXml.Targets - .Where(target => - StringComparers.TargetNames.Equals(GetTarget(target), BuildEvent) && - StringComparers.TargetNames.Equals(target.Name, TargetName)) - .SelectMany(target => target.Tasks) - .FirstOrDefault(task => StringComparers.TargetNames.Equals(task.Name, ExecTask)); - } + private void SetParameter(ProjectRootElement projectXml, string unevaluatedPropertyValue) + { + ProjectTaskElement? execTask = FindExecTaskInTargets(projectXml); - private ProjectTargetElement? FindTargetToRemove(ProjectRootElement projectXml) + if (execTask is not null) { - return projectXml.Targets - .FirstOrDefault(target => - StringComparers.TargetNames.Equals(GetTarget(target), BuildEvent) && - StringComparers.TargetNames.Equals(target.Name, TargetName) && - target.Children.Count == 1 && - target.Tasks.Count == 1 && - StringComparers.TargetNames.Equals(target.Tasks.First().Name, ExecTask)); + SetExecParameter(execTask, unevaluatedPropertyValue); } - - private void SetParameter(ProjectRootElement projectXml, string unevaluatedPropertyValue) + else { - ProjectTaskElement? execTask = FindExecTaskInTargets(projectXml); - - if (execTask is not null) - { - SetExecParameter(execTask, unevaluatedPropertyValue); - } - else - { - ProjectTargetElement target = projectXml.AddTarget(TargetName); - SetTargetDependencies(target); - execTask = target.AddTask(ExecTask); - SetExecParameter(execTask, unevaluatedPropertyValue); - } + ProjectTargetElement target = projectXml.AddTarget(TargetName); + SetTargetDependencies(target); + execTask = target.AddTask(ExecTask); + SetExecParameter(execTask, unevaluatedPropertyValue); } - - private static void SetExecParameter(ProjectTaskElement execTask, string unevaluatedPropertyValue) - => execTask.SetParameter(Command, unevaluatedPropertyValue.Replace("%", "%25")); } + + private static void SetExecParameter(ProjectTaskElement execTask, string unevaluatedPropertyValue) + => execTask.SetParameter(Command, unevaluatedPropertyValue.Replace("%", "%25")); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.cs index f3055b9e4e..77d4ec7841 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/AbstractBuildEventValueProvider.cs @@ -2,101 +2,100 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; + +internal abstract partial class AbstractBuildEventValueProvider : InterceptingPropertyValueProviderBase { - internal abstract partial class AbstractBuildEventValueProvider : InterceptingPropertyValueProviderBase + private readonly IProjectAccessor _projectAccessor; + private readonly UnconfiguredProject _unconfiguredProject; + private readonly AbstractBuildEventHelper _helper; + + protected AbstractBuildEventValueProvider( + IProjectAccessor projectAccessor, + UnconfiguredProject project, + AbstractBuildEventHelper helper) { - private readonly IProjectAccessor _projectAccessor; - private readonly UnconfiguredProject _unconfiguredProject; - private readonly AbstractBuildEventHelper _helper; + _projectAccessor = projectAccessor; + _unconfiguredProject = project; + _helper = helper; + } - protected AbstractBuildEventValueProvider( - IProjectAccessor projectAccessor, - UnconfiguredProject project, - AbstractBuildEventHelper helper) - { - _projectAccessor = projectAccessor; - _unconfiguredProject = project; - _helper = helper; - } + // There are two ways of storing pre/post build events in the project. + // + // 1. As MSBuild properties (PreBuildEvent / PostBuildEvent) + // 2. As MSBuild tasks (PreBuild / PostBuild) + // + // Properties were used in legacy projects. + // + // For SDK style projects, we should use tasks. + // + // In legacy projects, the properties were defined _after_ the import of common targets, + // meaning that the properties had access to a full range of property values for use in their + // bodies. + // + // In SDK projects, it's not possible to define a property in the project after the common + // targets, so an MSBuild task is used instead. + // + // Some projects still define these events using properties, and the below code will work + // with such properties when they exist. However if these properties are absent, then + // tasks are used instead. + // + // Examples of MSBuild properties that are not available to PreBuildEvent/PostBuildEvent + // properties (but which are available to PreBuild/PostBuild targets) are ProjectExt, + // PlatformName, ProjectDir, TargetDir, TargetFileName, TargetExt, ProjectFileName, + // ProjectPath, TargetPath, TargetName, ProjectName, ConfigurationName, and OutDir. + // + // Tasks are defined as: + // + // + // + // + // + // + // - // There are two ways of storing pre/post build events in the project. - // - // 1. As MSBuild properties (PreBuildEvent / PostBuildEvent) - // 2. As MSBuild tasks (PreBuild / PostBuild) - // - // Properties were used in legacy projects. - // - // For SDK style projects, we should use tasks. - // - // In legacy projects, the properties were defined _after_ the import of common targets, - // meaning that the properties had access to a full range of property values for use in their - // bodies. - // - // In SDK projects, it's not possible to define a property in the project after the common - // targets, so an MSBuild task is used instead. - // - // Some projects still define these events using properties, and the below code will work - // with such properties when they exist. However if these properties are absent, then - // tasks are used instead. - // - // Examples of MSBuild properties that are not available to PreBuildEvent/PostBuildEvent - // properties (but which are available to PreBuild/PostBuild targets) are ProjectExt, - // PlatformName, ProjectDir, TargetDir, TargetFileName, TargetExt, ProjectFileName, - // ProjectPath, TargetPath, TargetName, ProjectName, ConfigurationName, and OutDir. - // - // Tasks are defined as: - // - // - // - // - // - // - // + public override async Task OnGetUnevaluatedPropertyValueAsync( + string propertyName, + string unevaluatedPropertyValue, + IProjectProperties defaultProperties) + { + (bool success, string? property) = await _helper.TryGetUnevaluatedPropertyValueAsync(defaultProperties); - public override async Task OnGetUnevaluatedPropertyValueAsync( - string propertyName, - string unevaluatedPropertyValue, - IProjectProperties defaultProperties) + if (success) { - (bool success, string? property) = await _helper.TryGetUnevaluatedPropertyValueAsync(defaultProperties); - - if (success) - { - return property ?? string.Empty; - } - - return await _projectAccessor.OpenProjectXmlForReadAsync(_unconfiguredProject, _helper.TryGetValueFromTarget) ?? string.Empty; + return property ?? string.Empty; } - public override async Task OnGetEvaluatedPropertyValueAsync( - string propertyName, - string evaluatedPropertyValue, - IProjectProperties defaultProperties) - { - (bool success, string property) = await _helper.TryGetEvaluatedPropertyValueAsync(defaultProperties); + return await _projectAccessor.OpenProjectXmlForReadAsync(_unconfiguredProject, _helper.TryGetValueFromTarget) ?? string.Empty; + } - if (success) - { - return property; - } + public override async Task OnGetEvaluatedPropertyValueAsync( + string propertyName, + string evaluatedPropertyValue, + IProjectProperties defaultProperties) + { + (bool success, string property) = await _helper.TryGetEvaluatedPropertyValueAsync(defaultProperties); - return await _projectAccessor.OpenProjectXmlForReadAsync(_unconfiguredProject, _helper.TryGetValueFromTarget) ?? string.Empty; + if (success) + { + return property; } - public override async Task OnSetPropertyValueAsync( - string propertyName, - string unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) - { - if (await _helper.TrySetPropertyAsync(unevaluatedPropertyValue, defaultProperties)) - { - return null; - } + return await _projectAccessor.OpenProjectXmlForReadAsync(_unconfiguredProject, _helper.TryGetValueFromTarget) ?? string.Empty; + } - await _projectAccessor.OpenProjectXmlForWriteAsync(_unconfiguredProject, projectXml => _helper.SetProperty(unevaluatedPropertyValue, projectXml)); + public override async Task OnSetPropertyValueAsync( + string propertyName, + string unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + if (await _helper.TrySetPropertyAsync(unevaluatedPropertyValue, defaultProperties)) + { return null; } + + await _projectAccessor.OpenProjectXmlForWriteAsync(_unconfiguredProject, projectXml => _helper.SetProperty(unevaluatedPropertyValue, projectXml)); + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs index fb392aa110..aa19d2ef86 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationFrameworkValueProvider.cs @@ -5,375 +5,374 @@ using Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; using static Microsoft.VisualStudio.ProjectSystem.Properties.PropertyNames; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider( +new[] { - [ExportInterceptingPropertyValueProvider( - new[] - { - ApplicationFramework, - EnableVisualStyles, - SingleInstance, - SaveMySettingsOnExit, - HighDpiMode, - AuthenticationMode, - ShutdownMode, - SplashScreen, - MinimumSplashScreenDisplayTime - }, - ExportInterceptingPropertyValueProviderFile.ProjectFile)] - [AppliesTo(ProjectCapability.WPF + "|" + ProjectCapability.WindowsForms)] - internal sealed class ApplicationFrameworkValueProvider : InterceptingPropertyValueProviderBase + ApplicationFramework, + EnableVisualStyles, + SingleInstance, + SaveMySettingsOnExit, + HighDpiMode, + AuthenticationMode, + ShutdownMode, + SplashScreen, + MinimumSplashScreenDisplayTime +}, +ExportInterceptingPropertyValueProviderFile.ProjectFile)] +[AppliesTo(ProjectCapability.WPF + "|" + ProjectCapability.WindowsForms)] +internal sealed class ApplicationFrameworkValueProvider : InterceptingPropertyValueProviderBase +{ + private const string EnabledValue = "WindowsForms"; + private const string DisabledValue = "WindowsFormsWithCustomSubMain"; + + private const string WinExeOutputType = "WinExe"; + private const string NoneItemType = "None"; + private const string ApplicationDefinitionItemType = "ApplicationDefinition"; + + private readonly UnconfiguredProject _project; + private readonly IProjectItemProvider _sourceItemsProvider; + private readonly IMyAppFileAccessor _myAppXmlFileAccessor; + private readonly IProjectAccessor _projectAccessor; + + [ImportingConstructor] + public ApplicationFrameworkValueProvider( + UnconfiguredProject project, + [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, + IMyAppFileAccessor myAppXamlFileAccessor, + IProjectAccessor projectAccessor) { - private const string EnabledValue = "WindowsForms"; - private const string DisabledValue = "WindowsFormsWithCustomSubMain"; - - private const string WinExeOutputType = "WinExe"; - private const string NoneItemType = "None"; - private const string ApplicationDefinitionItemType = "ApplicationDefinition"; - - private readonly UnconfiguredProject _project; - private readonly IProjectItemProvider _sourceItemsProvider; - private readonly IMyAppFileAccessor _myAppXmlFileAccessor; - private readonly IProjectAccessor _projectAccessor; - - [ImportingConstructor] - public ApplicationFrameworkValueProvider( - UnconfiguredProject project, - [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, - IMyAppFileAccessor myAppXamlFileAccessor, - IProjectAccessor projectAccessor) - { - _project = project; - _sourceItemsProvider = sourceItemsProvider; - _myAppXmlFileAccessor = myAppXamlFileAccessor; - _projectAccessor = projectAccessor; - } + _project = project; + _sourceItemsProvider = sourceItemsProvider; + _myAppXmlFileAccessor = myAppXamlFileAccessor; + _projectAccessor = projectAccessor; + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) { - if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) + if (await IsWPFApplicationAsync(defaultProperties)) { - if (await IsWPFApplicationAsync(defaultProperties)) - { - return await SetPropertyValueForWPFApplicationAsync(unevaluatedPropertyValue, defaultProperties); - } - else - { - return await SetPropertyValueForDefaultProjectTypesAsync(unevaluatedPropertyValue, defaultProperties); - } + return await SetPropertyValueForWPFApplicationAsync(unevaluatedPropertyValue, defaultProperties); } else { - return await SetPropertyValueAsync(propertyName, unevaluatedPropertyValue); + return await SetPropertyValueForDefaultProjectTypesAsync(unevaluatedPropertyValue, defaultProperties); } } - - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + else { - if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) - { - return GetPropertyValueAsync(defaultProperties); - } - else - { - return GetPropertyValueAsync(propertyName); - } + return await SetPropertyValueAsync(propertyName, unevaluatedPropertyValue); } + } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) { - if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) - { - return GetPropertyValueAsync(defaultProperties); - } - else - { - return GetPropertyValueAsync(propertyName); - } + return GetPropertyValueAsync(defaultProperties); } - - private async Task GetPropertyValueAsync(IProjectProperties defaultProperties) + else { - if (await IsWPFApplicationAsync(defaultProperties)) - { - return await GetPropertyValueForWPFApplicationAsync(defaultProperties); - } - else - { - return await GetPropertyValueForDefaultProjectTypesAsync(defaultProperties); - } + return GetPropertyValueAsync(propertyName); } + } - private async Task GetPropertyValueForWPFApplicationAsync(IProjectProperties defaultProperties) + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + if (StringComparers.PropertyNames.Equals(propertyName, ApplicationFramework)) { - string startupObject = await defaultProperties.GetEvaluatedPropertyValueAsync(StartupObjectMSBuild); - if (!string.IsNullOrEmpty(startupObject)) - { - // A start-up object is specified for this project. This takes precedence over the Startup URI, so set Use Application Framework to "false". - return "false"; - } - - string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: false); - if (string.IsNullOrEmpty(appXamlFilePath)) - { - // No Application.xaml file; set Use Application Framework to "false". - return "false"; - } - - return "true"; + return GetPropertyValueAsync(defaultProperties); } + else + { + return GetPropertyValueAsync(propertyName); + } + } - private static async Task GetPropertyValueForDefaultProjectTypesAsync(IProjectProperties defaultProperties) + private async Task GetPropertyValueAsync(IProjectProperties defaultProperties) + { + if (await IsWPFApplicationAsync(defaultProperties)) { - string? value = await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationFrameworkMSBuild); + return await GetPropertyValueForWPFApplicationAsync(defaultProperties); + } + else + { + return await GetPropertyValueForDefaultProjectTypesAsync(defaultProperties); + } + } - return value switch - { - EnabledValue => "true", - DisabledValue => "false", - _ => string.Empty - }; + private async Task GetPropertyValueForWPFApplicationAsync(IProjectProperties defaultProperties) + { + string startupObject = await defaultProperties.GetEvaluatedPropertyValueAsync(StartupObjectMSBuild); + if (!string.IsNullOrEmpty(startupObject)) + { + // A start-up object is specified for this project. This takes precedence over the Startup URI, so set Use Application Framework to "false". + return "false"; } - private async Task IsWPFApplicationAsync(IProjectProperties defaultProperties) + string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: false); + if (string.IsNullOrEmpty(appXamlFilePath)) { - IProjectCapabilitiesScope capabilities = _project.Capabilities; + // No Application.xaml file; set Use Application Framework to "false". + return "false"; + } - bool useWPF = capabilities.Contains(ProjectCapability.WPF); - bool useWindowsForms = capabilities.Contains(ProjectCapability.WindowsForms); - string outputTypeString = await defaultProperties.GetEvaluatedPropertyValueAsync(OutputTypeMSBuild); + return "true"; + } - return useWPF - && StringComparers.PropertyLiteralValues.Equals(outputTypeString, WinExeOutputType) - && !useWindowsForms; - } + private static async Task GetPropertyValueForDefaultProjectTypesAsync(IProjectProperties defaultProperties) + { + string? value = await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationFrameworkMSBuild); - private async Task SetPropertyValueForDefaultProjectTypesAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) + return value switch { - string rootNamespace = await defaultProperties.GetEvaluatedPropertyValueAsync("RootNamespace"); + EnabledValue => "true", + DisabledValue => "false", + _ => string.Empty + }; + } + + private async Task IsWPFApplicationAsync(IProjectProperties defaultProperties) + { + IProjectCapabilitiesScope capabilities = _project.Capabilities; + + bool useWPF = capabilities.Contains(ProjectCapability.WPF); + bool useWindowsForms = capabilities.Contains(ProjectCapability.WindowsForms); + string outputTypeString = await defaultProperties.GetEvaluatedPropertyValueAsync(OutputTypeMSBuild); + + return useWPF + && StringComparers.PropertyLiteralValues.Equals(outputTypeString, WinExeOutputType) + && !useWindowsForms; + } - if (bool.TryParse(unevaluatedPropertyValue, out bool value)) + private async Task SetPropertyValueForDefaultProjectTypesAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + string rootNamespace = await defaultProperties.GetEvaluatedPropertyValueAsync("RootNamespace"); + + if (bool.TryParse(unevaluatedPropertyValue, out bool value)) + { + if (value) { - if (value) - { - // Set in project file: WindowsForms - await defaultProperties.SetPropertyValueAsync(ApplicationFrameworkMSBuild, EnabledValue); + // Set in project file: WindowsForms + await defaultProperties.SetPropertyValueAsync(ApplicationFrameworkMSBuild, EnabledValue); - // Set in myapp file: true - await _myAppXmlFileAccessor.SetMySubMainAsync("true"); + // Set in myapp file: true + await _myAppXmlFileAccessor.SetMySubMainAsync("true"); - // Set the StartupObject to namespace.My.MyApplication; we should save the actual value in the myapp file. - string? startupObjectValue = await defaultProperties.GetEvaluatedPropertyValueAsync(StartupObjectMSBuild); + // Set the StartupObject to namespace.My.MyApplication; we should save the actual value in the myapp file. + string? startupObjectValue = await defaultProperties.GetEvaluatedPropertyValueAsync(StartupObjectMSBuild); - await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, rootNamespace + ".My.MyApplication"); + await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, rootNamespace + ".My.MyApplication"); - if (startupObjectValue is not null) + if (startupObjectValue is not null) + { + string prefix = rootNamespace + "."; + // Use StringComparison.OrdinalIgnoreCase because VB is _not_ case-sensitive + if (startupObjectValue.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - string prefix = rootNamespace + "."; - // Use StringComparison.OrdinalIgnoreCase because VB is _not_ case-sensitive - if (startupObjectValue.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - startupObjectValue = startupObjectValue.Substring(prefix.Length); - } - await _myAppXmlFileAccessor.SetMainFormAsync(startupObjectValue); + startupObjectValue = startupObjectValue.Substring(prefix.Length); } + await _myAppXmlFileAccessor.SetMainFormAsync(startupObjectValue); } - else - { - // Set in project file: WindowsFormsWithCustomSubMain - await defaultProperties.SetPropertyValueAsync(ApplicationFrameworkMSBuild, DisabledValue); + } + else + { + // Set in project file: WindowsFormsWithCustomSubMain + await defaultProperties.SetPropertyValueAsync(ApplicationFrameworkMSBuild, DisabledValue); - // Set in myapp file: false - await _myAppXmlFileAccessor.SetMySubMainAsync("false"); + // Set in myapp file: false + await _myAppXmlFileAccessor.SetMySubMainAsync("false"); - // Recover the StartupObject from myapp file and save it to the project file. - string? startupObjectValue = await _myAppXmlFileAccessor.GetMainFormAsync(); + // Recover the StartupObject from myapp file and save it to the project file. + string? startupObjectValue = await _myAppXmlFileAccessor.GetMainFormAsync(); - if (startupObjectValue is not null) - await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, rootNamespace + "." + startupObjectValue); - } + if (startupObjectValue is not null) + await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, rootNamespace + "." + startupObjectValue); } - - return null; } - private async Task GetAppXamlRelativeFilePathAsync(bool create) - { - SpecialFileFlags flags = create ? SpecialFileFlags.CreateIfNotExist : 0; + return null; + } - return await _project.GetSpecialFilePathAsync(SpecialFiles.AppXaml, flags); - } + private async Task GetAppXamlRelativeFilePathAsync(bool create) + { + SpecialFileFlags flags = create ? SpecialFileFlags.CreateIfNotExist : 0; - private async Task SetPropertyValueForWPFApplicationAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) + return await _project.GetSpecialFilePathAsync(SpecialFiles.AppXaml, flags); + } + + private async Task SetPropertyValueForWPFApplicationAsync(string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + if (bool.TryParse(unevaluatedPropertyValue, out bool value)) { - if (bool.TryParse(unevaluatedPropertyValue, out bool value)) + if (value) { - if (value) - { - // Enabled + // Enabled - string? appXamlFileName = Path.GetFileName(await GetAppXamlRelativeFilePathAsync(create: false)); + string? appXamlFileName = Path.GetFileName(await GetAppXamlRelativeFilePathAsync(create: false)); - // Project accessor to remove the ApplicationDefinition property if re-enabled - // Note: this code causes CPS tree validation warning in Debug builds only, but we are ignorning - await _projectAccessor.OpenProjectXmlForWriteAsync(_project, project => + // Project accessor to remove the ApplicationDefinition property if re-enabled + // Note: this code causes CPS tree validation warning in Debug builds only, but we are ignorning + await _projectAccessor.OpenProjectXmlForWriteAsync(_project, project => + { + foreach (ProjectItemGroupElement itemGroup in project.ItemGroups) { - foreach (ProjectItemGroupElement itemGroup in project.ItemGroups) + foreach (ProjectItemElement item in itemGroup.Items.ToList()) { - foreach (ProjectItemElement item in itemGroup.Items.ToList()) + if (StringComparers.ItemTypes.Equals(item.ItemType, ApplicationDefinitionItemType)) { - if (StringComparers.ItemTypes.Equals(item.ItemType, ApplicationDefinitionItemType)) - { - itemGroup.RemoveChild(item); - } - else if (StringComparers.ItemTypes.Equals(item.ItemType, NoneItemType) && StringComparers.Paths.Equals(item.Include, appXamlFileName)) - { - itemGroup.RemoveChild(item); - } + itemGroup.RemoveChild(item); + } + else if (StringComparers.ItemTypes.Equals(item.ItemType, NoneItemType) && StringComparers.Paths.Equals(item.Include, appXamlFileName)) + { + itemGroup.RemoveChild(item); } } - }); + } + }); - // Create the Application.xaml if it doesn't exist. We don't care about the path, we just need it to be created. - string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: true); + // Create the Application.xaml if it doesn't exist. We don't care about the path, we just need it to be created. + string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: true); - // Clear out the StartupObject if it has a value. - string? startupObject = await defaultProperties.GetUnevaluatedPropertyValueAsync(StartupObjectMSBuild); - if (!string.IsNullOrEmpty(startupObject)) - { - await defaultProperties.DeletePropertyAsync(StartupObjectMSBuild); - } - } - else + // Clear out the StartupObject if it has a value. + string? startupObject = await defaultProperties.GetUnevaluatedPropertyValueAsync(StartupObjectMSBuild); + if (!string.IsNullOrEmpty(startupObject)) { - // Disabled + await defaultProperties.DeletePropertyAsync(StartupObjectMSBuild); + } + } + else + { + // Disabled - // Set the StartupObject if it doesn't already have a value. - string? startupObject = await defaultProperties.GetUnevaluatedPropertyValueAsync(StartupObjectMSBuild); - if (string.IsNullOrEmpty(startupObject)) - { - await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, "Sub Main"); - } + // Set the StartupObject if it doesn't already have a value. + string? startupObject = await defaultProperties.GetUnevaluatedPropertyValueAsync(StartupObjectMSBuild); + if (string.IsNullOrEmpty(startupObject)) + { + await defaultProperties.SetPropertyValueAsync(StartupObjectMSBuild, "Sub Main"); + } - // Set the Application.xaml file's build action to None, which will also remove it from the ApplicationDefinition item group. - string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: false); - if (appXamlFilePath is not null) + // Set the Application.xaml file's build action to None, which will also remove it from the ApplicationDefinition item group. + string? appXamlFilePath = await GetAppXamlRelativeFilePathAsync(create: false); + if (appXamlFilePath is not null) + { + IEnumerable matchingItems = await _sourceItemsProvider.GetItemsAsync(ApplicationDefinitionItemType, appXamlFilePath); + IProjectItem? appXamlItem = matchingItems.FirstOrDefault(); + if (appXamlItem is not null) { - IEnumerable matchingItems = await _sourceItemsProvider.GetItemsAsync(ApplicationDefinitionItemType, appXamlFilePath); - IProjectItem? appXamlItem = matchingItems.FirstOrDefault(); - if (appXamlItem is not null) - { - await appXamlItem.SetItemTypeAsync(NoneItemType); - } + await appXamlItem.SetItemTypeAsync(NoneItemType); } } } - - return null; } - private async Task GetPropertyValueAsync(string propertyName) + return null; + } + + private async Task GetPropertyValueAsync(string propertyName) + { + string value = propertyName switch { - string value = propertyName switch + ApplicationFramework => (await _myAppXmlFileAccessor.GetMySubMainAsync()).ToString() ?? string.Empty, + EnableVisualStyles => (await _myAppXmlFileAccessor.GetEnableVisualStylesAsync()).ToString() ?? string.Empty, + SingleInstance => (await _myAppXmlFileAccessor.GetSingleInstanceAsync()).ToString() ?? string.Empty, + SaveMySettingsOnExit => (await _myAppXmlFileAccessor.GetSaveMySettingsOnExitAsync()).ToString() ?? string.Empty, + HighDpiMode => (await _myAppXmlFileAccessor.GetHighDpiModeAsync()).ToString() ?? string.Empty, + AuthenticationMode => (await _myAppXmlFileAccessor.GetAuthenticationModeAsync()).ToString() ?? string.Empty, + ShutdownMode => (await _myAppXmlFileAccessor.GetShutdownModeAsync()).ToString() ?? string.Empty, + SplashScreen => await _myAppXmlFileAccessor.GetSplashScreenAsync() ?? string.Empty, + MinimumSplashScreenDisplayTime => (await _myAppXmlFileAccessor.GetMinimumSplashScreenDisplayTimeAsync()).ToString() ?? string.Empty, + + _ => throw new InvalidOperationException($"The provider does not support the '{propertyName}' property.") + }; + + if (propertyName == AuthenticationMode) + { + value = value switch { - ApplicationFramework => (await _myAppXmlFileAccessor.GetMySubMainAsync()).ToString() ?? string.Empty, - EnableVisualStyles => (await _myAppXmlFileAccessor.GetEnableVisualStylesAsync()).ToString() ?? string.Empty, - SingleInstance => (await _myAppXmlFileAccessor.GetSingleInstanceAsync()).ToString() ?? string.Empty, - SaveMySettingsOnExit => (await _myAppXmlFileAccessor.GetSaveMySettingsOnExitAsync()).ToString() ?? string.Empty, - HighDpiMode => (await _myAppXmlFileAccessor.GetHighDpiModeAsync()).ToString() ?? string.Empty, - AuthenticationMode => (await _myAppXmlFileAccessor.GetAuthenticationModeAsync()).ToString() ?? string.Empty, - ShutdownMode => (await _myAppXmlFileAccessor.GetShutdownModeAsync()).ToString() ?? string.Empty, - SplashScreen => await _myAppXmlFileAccessor.GetSplashScreenAsync() ?? string.Empty, - MinimumSplashScreenDisplayTime => (await _myAppXmlFileAccessor.GetMinimumSplashScreenDisplayTimeAsync()).ToString() ?? string.Empty, - - _ => throw new InvalidOperationException($"The provider does not support the '{propertyName}' property.") - }; + "0" => "Windows", + "1" => "ApplicationDefined", + "" => "", - if (propertyName == AuthenticationMode) + _ => throw new InvalidOperationException($"Invalid value '{value}' for '{propertyName}' property.") + }; + } + else if (propertyName == ShutdownMode) + { + value = value switch { - value = value switch - { - "0" => "Windows", - "1" => "ApplicationDefined", - "" => "", + "0" => "AfterMainFormCloses", + "1" => "AfterAllFormsClose", + "" => "", - _ => throw new InvalidOperationException($"Invalid value '{value}' for '{propertyName}' property.") - }; - } - else if (propertyName == ShutdownMode) - { - value = value switch - { - "0" => "AfterMainFormCloses", - "1" => "AfterAllFormsClose", - "" => "", + _ => throw new InvalidOperationException($"Invalid value '{value}' for '{propertyName}' property.") + }; + } - _ => throw new InvalidOperationException($"Invalid value '{value}' for '{propertyName}' property.") - }; - } + return value; + } - return value; + private async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue) + { + // ValueProvider needs to convert string enums to valid values to be saved. + if (propertyName == AuthenticationMode) + { + unevaluatedPropertyValue = unevaluatedPropertyValue switch + { + "Windows" => "0", + "ApplicationDefined" => "1", + _ => unevaluatedPropertyValue + }; } - - private async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue) + else if (propertyName == ShutdownMode) { - // ValueProvider needs to convert string enums to valid values to be saved. - if (propertyName == AuthenticationMode) + unevaluatedPropertyValue = unevaluatedPropertyValue switch { - unevaluatedPropertyValue = unevaluatedPropertyValue switch - { - "Windows" => "0", - "ApplicationDefined" => "1", - _ => unevaluatedPropertyValue - }; - } - else if (propertyName == ShutdownMode) + "AfterMainFormCloses" => "0", + "AfterAllFormsClose" => "1", + _ => unevaluatedPropertyValue + }; + } + else if (propertyName == MinimumSplashScreenDisplayTime) + { + if (short.TryParse(unevaluatedPropertyValue, out short value)) { - unevaluatedPropertyValue = unevaluatedPropertyValue switch + if (value < 0) { - "AfterMainFormCloses" => "0", - "AfterAllFormsClose" => "1", - _ => unevaluatedPropertyValue - }; + // Convert negative value to positive. + unevaluatedPropertyValue = (-value).ToString(); + } } - else if (propertyName == MinimumSplashScreenDisplayTime) + else { - if (short.TryParse(unevaluatedPropertyValue, out short value)) + if (string.IsNullOrEmpty(unevaluatedPropertyValue)) { - if (value < 0) - { - // Convert negative value to positive. - unevaluatedPropertyValue = (-value).ToString(); - } + // If we get an empty string, we just want to clear the value. + await _myAppXmlFileAccessor.SetMinimumSplashScreenDisplayTimeAsync(null); } - else - { - if (string.IsNullOrEmpty(unevaluatedPropertyValue)) - { - // If we get an empty string, we just want to clear the value. - await _myAppXmlFileAccessor.SetMinimumSplashScreenDisplayTimeAsync(null); - } - return null; - } + return null; } - - await (propertyName switch - { - ApplicationFramework => _myAppXmlFileAccessor.SetMySubMainAsync(unevaluatedPropertyValue), - EnableVisualStyles => _myAppXmlFileAccessor.SetEnableVisualStylesAsync(Convert.ToBoolean(unevaluatedPropertyValue)), - SingleInstance => _myAppXmlFileAccessor.SetSingleInstanceAsync(Convert.ToBoolean(unevaluatedPropertyValue)), - SaveMySettingsOnExit => _myAppXmlFileAccessor.SetSaveMySettingsOnExitAsync(Convert.ToBoolean(unevaluatedPropertyValue)), - HighDpiMode => _myAppXmlFileAccessor.SetHighDpiModeAsync(Convert.ToInt16(unevaluatedPropertyValue)), - AuthenticationMode => _myAppXmlFileAccessor.SetAuthenticationModeAsync(Convert.ToInt16(unevaluatedPropertyValue)), - ShutdownMode => _myAppXmlFileAccessor.SetShutdownModeAsync(Convert.ToInt16(unevaluatedPropertyValue)), - SplashScreen => _myAppXmlFileAccessor.SetSplashScreenAsync(unevaluatedPropertyValue), - MinimumSplashScreenDisplayTime => _myAppXmlFileAccessor.SetMinimumSplashScreenDisplayTimeAsync(Convert.ToInt16(unevaluatedPropertyValue)), - - _ => throw new InvalidOperationException($"The provider does not support the '{propertyName}' property.") - }); - - return null; } + + await (propertyName switch + { + ApplicationFramework => _myAppXmlFileAccessor.SetMySubMainAsync(unevaluatedPropertyValue), + EnableVisualStyles => _myAppXmlFileAccessor.SetEnableVisualStylesAsync(Convert.ToBoolean(unevaluatedPropertyValue)), + SingleInstance => _myAppXmlFileAccessor.SetSingleInstanceAsync(Convert.ToBoolean(unevaluatedPropertyValue)), + SaveMySettingsOnExit => _myAppXmlFileAccessor.SetSaveMySettingsOnExitAsync(Convert.ToBoolean(unevaluatedPropertyValue)), + HighDpiMode => _myAppXmlFileAccessor.SetHighDpiModeAsync(Convert.ToInt16(unevaluatedPropertyValue)), + AuthenticationMode => _myAppXmlFileAccessor.SetAuthenticationModeAsync(Convert.ToInt16(unevaluatedPropertyValue)), + ShutdownMode => _myAppXmlFileAccessor.SetShutdownModeAsync(Convert.ToInt16(unevaluatedPropertyValue)), + SplashScreen => _myAppXmlFileAccessor.SetSplashScreenAsync(unevaluatedPropertyValue), + MinimumSplashScreenDisplayTime => _myAppXmlFileAccessor.SetMinimumSplashScreenDisplayTimeAsync(Convert.ToInt16(unevaluatedPropertyValue)), + + _ => throw new InvalidOperationException($"The provider does not support the '{propertyName}' property.") + }); + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeExValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeExValueProvider.cs index e9f4921393..2625fa4ce8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeExValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeExValueProvider.cs @@ -2,40 +2,39 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +/// +/// OutputTypeEx acts as a converter for the MSBuild OutputType value expressed as . +/// +[ExportInterceptingPropertyValueProvider("OutputTypeEx", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class OutputTypeExValueProvider : OutputTypeValueProviderBase { - /// - /// OutputTypeEx acts as a converter for the MSBuild OutputType value expressed as . - /// - [ExportInterceptingPropertyValueProvider("OutputTypeEx", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class OutputTypeExValueProvider : OutputTypeValueProviderBase + private static readonly ImmutableDictionary s_getOutputTypeExMap = new Dictionary() { - private static readonly ImmutableDictionary s_getOutputTypeExMap = new Dictionary() - { - {"WinExe", "0" }, - {"Exe", "1" }, - {"Library", "2" }, - {"WinMDObj", "3" }, - {"AppContainerExe", "4" }, - }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); - - private static readonly ImmutableDictionary s_setOutputTypeExMap = new Dictionary() - { - {"0", "WinExe" }, - {"1", "Exe" }, - {"2", "Library" }, - {"3", "WinMDObj"}, - {"4", "AppContainerExe" }, - }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); + {"WinExe", "0" }, + {"Exe", "1" }, + {"Library", "2" }, + {"WinMDObj", "3" }, + {"AppContainerExe", "4" }, + }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); - [ImportingConstructor] - public OutputTypeExValueProvider(ProjectProperties properties) - : base(properties) - { - } + private static readonly ImmutableDictionary s_setOutputTypeExMap = new Dictionary() + { + {"0", "WinExe" }, + {"1", "Exe" }, + {"2", "Library" }, + {"3", "WinMDObj"}, + {"4", "AppContainerExe" }, + }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); - protected override ImmutableDictionary GetMap => s_getOutputTypeExMap; - protected override ImmutableDictionary SetMap => s_setOutputTypeExMap; - protected override string DefaultGetValue => "0"; + [ImportingConstructor] + public OutputTypeExValueProvider(ProjectProperties properties) + : base(properties) + { } + + protected override ImmutableDictionary GetMap => s_getOutputTypeExMap; + protected override ImmutableDictionary SetMap => s_setOutputTypeExMap; + protected override string DefaultGetValue => "0"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProvider.cs index d4bf93aefd..69881fa493 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProvider.cs @@ -2,36 +2,35 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +/// OutputType acts as a converter for the MSBuild OutputType value expressed as . +[ExportInterceptingPropertyValueProvider("OutputType", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class OutputTypeValueProvider : OutputTypeValueProviderBase { - /// OutputType acts as a converter for the MSBuild OutputType value expressed as . - [ExportInterceptingPropertyValueProvider("OutputType", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class OutputTypeValueProvider : OutputTypeValueProviderBase + private static readonly ImmutableDictionary s_getOutputTypeMap = new Dictionary() { - private static readonly ImmutableDictionary s_getOutputTypeMap = new Dictionary() - { - {"WinExe", "0" }, - {"Exe", "1" }, - {"Library", "2" }, - {"WinMDObj", "2" }, - {"AppContainerExe", "1" }, - }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); - - private static readonly ImmutableDictionary s_setOutputTypeMap = new Dictionary() - { - {"0", "WinExe" }, - {"1", "Exe" }, - {"2", "Library" }, - }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); + {"WinExe", "0" }, + {"Exe", "1" }, + {"Library", "2" }, + {"WinMDObj", "2" }, + {"AppContainerExe", "1" }, + }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); - [ImportingConstructor] - public OutputTypeValueProvider(ProjectProperties properties) - : base(properties) - { - } + private static readonly ImmutableDictionary s_setOutputTypeMap = new Dictionary() + { + {"0", "WinExe" }, + {"1", "Exe" }, + {"2", "Library" }, + }.ToImmutableDictionary(StringComparers.PropertyLiteralValues); - protected override ImmutableDictionary GetMap => s_getOutputTypeMap; - protected override ImmutableDictionary SetMap => s_setOutputTypeMap; - protected override string DefaultGetValue => "0"; + [ImportingConstructor] + public OutputTypeValueProvider(ProjectProperties properties) + : base(properties) + { } + + protected override ImmutableDictionary GetMap => s_getOutputTypeMap; + protected override ImmutableDictionary SetMap => s_setOutputTypeMap; + protected override string DefaultGetValue => "0"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProviderBase.cs index 7e278d179d..c449083f73 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/OutputTypeValueProviderBase.cs @@ -2,45 +2,44 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +internal abstract class OutputTypeValueProviderBase : InterceptingPropertyValueProviderBase { - internal abstract class OutputTypeValueProviderBase : InterceptingPropertyValueProviderBase - { - private readonly ProjectProperties _properties; + private readonly ProjectProperties _properties; - protected abstract ImmutableDictionary GetMap { get; } - protected abstract ImmutableDictionary SetMap { get; } - protected abstract string DefaultGetValue { get; } + protected abstract ImmutableDictionary GetMap { get; } + protected abstract ImmutableDictionary SetMap { get; } + protected abstract string DefaultGetValue { get; } - protected OutputTypeValueProviderBase(ProjectProperties properties) - { - _properties = properties; - } + protected OutputTypeValueProviderBase(ProjectProperties properties) + { + _properties = properties; + } - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + string value = await configuration.OutputType.GetEvaluatedValueAtEndAsync(); + if (GetMap.TryGetValue(value, out string? returnValue)) { - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - string value = await configuration.OutputType.GetEvaluatedValueAtEndAsync(); - if (GetMap.TryGetValue(value, out string? returnValue)) - { - return returnValue; - } - - return DefaultGetValue; + return returnValue; } - public override async Task OnSetPropertyValueAsync( - string propertyName, - string unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) - { - string value = SetMap[unevaluatedPropertyValue]; - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - await configuration.OutputType.SetValueAsync(value); + return DefaultGetValue; + } - // Since we have persisted the value of OutputType, we don't have to persist the incoming value - return null; - } + public override async Task OnSetPropertyValueAsync( + string propertyName, + string unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + string value = SetMap[unevaluatedPropertyValue]; + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + await configuration.OutputType.SetValueAsync(value); + + // Since we have persisted the value of OutputType, we don't have to persist the incoming value + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PostBuildEventValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PostBuildEventValueProvider.cs index 98f4621a95..79882289c4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PostBuildEventValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PostBuildEventValueProvider.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; + +[ExportInterceptingPropertyValueProvider(PostBuildEvent, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal class PostBuildEventValueProvider : AbstractBuildEventValueProvider { - [ExportInterceptingPropertyValueProvider(PostBuildEvent, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal class PostBuildEventValueProvider : AbstractBuildEventValueProvider - { - private const string PostBuildEvent = "PostBuildEvent"; - private const string TargetName = "PostBuild"; + private const string PostBuildEvent = "PostBuildEvent"; + private const string TargetName = "PostBuild"; - [ImportingConstructor] - public PostBuildEventValueProvider( - IProjectAccessor projectAccessor, - UnconfiguredProject project) - : base(projectAccessor, - project, - new PostBuildEventHelper()) - { } + [ImportingConstructor] + public PostBuildEventValueProvider( + IProjectAccessor projectAccessor, + UnconfiguredProject project) + : base(projectAccessor, + project, + new PostBuildEventHelper()) + { } - internal class PostBuildEventHelper : AbstractBuildEventHelper - { - internal PostBuildEventHelper() - : base(PostBuildEvent, - TargetName, - target => target.AfterTargets, - target => { target.AfterTargets = PostBuildEvent; }) - { } - } + internal class PostBuildEventHelper : AbstractBuildEventHelper + { + internal PostBuildEventHelper() + : base(PostBuildEvent, + TargetName, + target => target.AfterTargets, + target => { target.AfterTargets = PostBuildEvent; }) + { } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PreBuildEventValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PreBuildEventValueProvider.cs index 6657e5859f..1b4b6123ee 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PreBuildEventValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/PreBuildEventValueProvider.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; + +[ExportInterceptingPropertyValueProvider(PreBuildEvent, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal class PreBuildEventValueProvider : AbstractBuildEventValueProvider { - [ExportInterceptingPropertyValueProvider(PreBuildEvent, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal class PreBuildEventValueProvider : AbstractBuildEventValueProvider - { - private const string PreBuildEvent = "PreBuildEvent"; - private const string TargetName = "PreBuild"; + private const string PreBuildEvent = "PreBuildEvent"; + private const string TargetName = "PreBuild"; - [ImportingConstructor] - public PreBuildEventValueProvider( - IProjectAccessor projectAccessor, - UnconfiguredProject project) - : base(projectAccessor, - project, - new PreBuildEventHelper()) - { } + [ImportingConstructor] + public PreBuildEventValueProvider( + IProjectAccessor projectAccessor, + UnconfiguredProject project) + : base(projectAccessor, + project, + new PreBuildEventHelper()) + { } - internal class PreBuildEventHelper : AbstractBuildEventHelper - { - internal PreBuildEventHelper() - : base(PreBuildEvent, - TargetName, - target => target.BeforeTargets, - target => { target.BeforeTargets = PreBuildEvent; }) - { } - } + internal class PreBuildEventHelper : AbstractBuildEventHelper + { + internal PreBuildEventHelper() + : base(PreBuildEvent, + TargetName, + target => target.BeforeTargets, + target => { target.BeforeTargets = PreBuildEvent; }) + { } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikerValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikerValueProvider.cs index fe04bea2df..96cffc8815 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikerValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikerValueProvider.cs @@ -3,53 +3,52 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +[ExportInterceptingPropertyValueProvider("TargetFrameworkMoniker", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class TargetFrameworkMonikerValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("TargetFrameworkMoniker", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class TargetFrameworkMonikerValueProvider : InterceptingPropertyValueProviderBase + private readonly IUnconfiguredProjectVsServices _unconfiguredProjectVsServices; + private readonly ProjectProperties _properties; + + [ImportingConstructor] + public TargetFrameworkMonikerValueProvider(IUnconfiguredProjectVsServices unconfiguredProjectVsServices, ProjectProperties properties) { - private readonly IUnconfiguredProjectVsServices _unconfiguredProjectVsServices; - private readonly ProjectProperties _properties; + _unconfiguredProjectVsServices = unconfiguredProjectVsServices; + _properties = properties; + } - [ImportingConstructor] - public TargetFrameworkMonikerValueProvider(IUnconfiguredProjectVsServices unconfiguredProjectVsServices, ProjectProperties properties) + public override async Task OnSetPropertyValueAsync( + string propertyName, + string unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + string? currentTargetFramework = (string?)await configuration.TargetFramework.GetValueAsync(); + string? currentTargetFrameworks = (string?)await configuration.TargetFrameworks.GetValueAsync(); + if (!string.IsNullOrEmpty(currentTargetFrameworks)) { - _unconfiguredProjectVsServices = unconfiguredProjectVsServices; - _properties = properties; + throw new InvalidOperationException(VSResources.MultiTFEditNotSupported); } - - public override async Task OnSetPropertyValueAsync( - string propertyName, - string unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) + else if (!string.IsNullOrEmpty(currentTargetFramework)) { - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - string? currentTargetFramework = (string?)await configuration.TargetFramework.GetValueAsync(); - string? currentTargetFrameworks = (string?)await configuration.TargetFrameworks.GetValueAsync(); - if (!string.IsNullOrEmpty(currentTargetFrameworks)) - { - throw new InvalidOperationException(VSResources.MultiTFEditNotSupported); - } - else if (!string.IsNullOrEmpty(currentTargetFramework)) - { - await defaultProperties.SetPropertyValueAsync(ConfigurationGeneral.TargetFrameworkProperty, unevaluatedPropertyValue); - } - else + await defaultProperties.SetPropertyValueAsync(ConfigurationGeneral.TargetFrameworkProperty, unevaluatedPropertyValue); + } + else + { + // CPS implements IVsHierarchy.SetProperty for the TFM property to call through the multi-targeting service and change the TFM. + // This causes the project to be reloaded after changing the values. + // Since the property providers are called under a write-lock, trying to reload the project on the same context fails saying it can't load the project + // if a lock is held. We are not going to write to the file under this lock (we return null from this method) and so we fork execution here to schedule + // a lambda on the UI thread and we don't pass the lock information from this context to the new one. + _unconfiguredProjectVsServices.ThreadingService.RunAndForget(() => { - // CPS implements IVsHierarchy.SetProperty for the TFM property to call through the multi-targeting service and change the TFM. - // This causes the project to be reloaded after changing the values. - // Since the property providers are called under a write-lock, trying to reload the project on the same context fails saying it can't load the project - // if a lock is held. We are not going to write to the file under this lock (we return null from this method) and so we fork execution here to schedule - // a lambda on the UI thread and we don't pass the lock information from this context to the new one. - _unconfiguredProjectVsServices.ThreadingService.RunAndForget(() => - { - _unconfiguredProjectVsServices.VsHierarchy.SetProperty(HierarchyId.Root, (int)VsHierarchyPropID.TargetFrameworkMoniker, unevaluatedPropertyValue); - return Task.CompletedTask; - }, options: ForkOptions.HideLocks | ForkOptions.StartOnMainThread, - unconfiguredProject: _unconfiguredProjectVsServices.Project); - } - return null; + _unconfiguredProjectVsServices.VsHierarchy.SetProperty(HierarchyId.Root, (int)VsHierarchyPropID.TargetFrameworkMoniker, unevaluatedPropertyValue); + return Task.CompletedTask; + }, options: ForkOptions.HideLocks | ForkOptions.StartOnMainThread, + unconfiguredProject: _unconfiguredProjectVsServices.Project); } + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikersValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikersValueProvider.cs index 0c200706d3..05db442674 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikersValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetFrameworkMonikersValueProvider.cs @@ -3,40 +3,39 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; + +[ExportInterceptingPropertyValueProvider("TargetFrameworkMonikers", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal class TargetFrameworkMonikersValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("TargetFrameworkMonikers", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal class TargetFrameworkMonikersValueProvider : InterceptingPropertyValueProviderBase + private readonly IActiveConfiguredProjectsProvider _projectProvider; + + [ImportingConstructor] + public TargetFrameworkMonikersValueProvider(IActiveConfiguredProjectsProvider projectProvider) { - private readonly IActiveConfiguredProjectsProvider _projectProvider; + _projectProvider = projectProvider; + } - [ImportingConstructor] - public TargetFrameworkMonikersValueProvider(IActiveConfiguredProjectsProvider projectProvider) - { - _projectProvider = projectProvider; - } + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + ActiveConfiguredObjects? configuredProjects = await _projectProvider.GetActiveConfiguredProjectsAsync(); - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + if (configuredProjects is null) { - ActiveConfiguredObjects? configuredProjects = await _projectProvider.GetActiveConfiguredProjectsAsync(); - - if (configuredProjects is null) - { - return ""; - } - - var builder = PooledArray.GetInstance(capacity: configuredProjects.Objects.Length); + return ""; + } - foreach (ConfiguredProject configuredProject in configuredProjects.Objects) - { - ProjectProperties projectProperties = configuredProject.Services.ExportProvider.GetExportedValue(); - ConfigurationGeneral configuration = await projectProperties.GetConfigurationGeneralPropertiesAsync(); - string? currentTargetFrameworkMoniker = (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(); - Assumes.NotNull(currentTargetFrameworkMoniker); - builder.Add(currentTargetFrameworkMoniker); - } + var builder = PooledArray.GetInstance(capacity: configuredProjects.Objects.Length); - return string.Join(";", builder.ToArrayAndFree()); + foreach (ConfiguredProject configuredProject in configuredProjects.Objects) + { + ProjectProperties projectProperties = configuredProject.Services.ExportProvider.GetExportedValue(); + ConfigurationGeneral configuration = await projectProperties.GetConfigurationGeneralPropertiesAsync(); + string? currentTargetFrameworkMoniker = (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(); + Assumes.NotNull(currentTargetFrameworkMoniker); + builder.Add(currentTargetFrameworkMoniker); } + + return string.Join(";", builder.ToArrayAndFree()); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs index 3c0670b10b..b65d11cbcd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/InterceptedProjectProperties/TargetPlatformMonikersValueProvider.cs @@ -3,42 +3,41 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; + +[ExportInterceptingPropertyValueProvider("TargetPlatformMonikers", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal class TargetPlatformMonikersValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("TargetPlatformMonikers", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal class TargetPlatformMonikersValueProvider : InterceptingPropertyValueProviderBase + private readonly IActiveConfiguredProjectsProvider _projectProvider; + + [ImportingConstructor] + public TargetPlatformMonikersValueProvider(IActiveConfiguredProjectsProvider projectProvider) { - private readonly IActiveConfiguredProjectsProvider _projectProvider; + _projectProvider = projectProvider; + } - [ImportingConstructor] - public TargetPlatformMonikersValueProvider(IActiveConfiguredProjectsProvider projectProvider) - { - _projectProvider = projectProvider; - } + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + ActiveConfiguredObjects? configuredProjects = await _projectProvider.GetActiveConfiguredProjectsAsync(); - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + if (configuredProjects is null) { - ActiveConfiguredObjects? configuredProjects = await _projectProvider.GetActiveConfiguredProjectsAsync(); - - if (configuredProjects is null) - { - return ""; - } - - var builder = PooledArray.GetInstance(capacity: configuredProjects.Objects.Length); + return ""; + } - foreach (ConfiguredProject configuredProject in configuredProjects.Objects) - { - ProjectProperties projectProperties = configuredProject.Services.ExportProvider.GetExportedValue(); - ConfigurationGeneral configuration = await projectProperties.GetConfigurationGeneralPropertiesAsync(); - string? currentPlatformMoniker = (string?)await configuration.TargetPlatformIdentifier.GetValueAsync(); - string? currentPlatformVersion = (string?)await configuration.TargetPlatformVersion.GetValueAsync(); + var builder = PooledArray.GetInstance(capacity: configuredProjects.Objects.Length); - Assumes.NotNull(currentPlatformMoniker); - builder.Add($"{currentPlatformMoniker}, Version={currentPlatformVersion}"); - } + foreach (ConfiguredProject configuredProject in configuredProjects.Objects) + { + ProjectProperties projectProperties = configuredProject.Services.ExportProvider.GetExportedValue(); + ConfigurationGeneral configuration = await projectProperties.GetConfigurationGeneralPropertiesAsync(); + string? currentPlatformMoniker = (string?)await configuration.TargetPlatformIdentifier.GetValueAsync(); + string? currentPlatformVersion = (string?)await configuration.TargetPlatformVersion.GetValueAsync(); - return string.Join(";", builder.ToArrayAndFree()); + Assumes.NotNull(currentPlatformMoniker); + builder.Add($"{currentPlatformMoniker}, Version={currentPlatformVersion}"); } + + return string.Join(";", builder.ToArrayAndFree()); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerPageMetadata.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerPageMetadata.cs index fbfc035297..e25587c810 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerPageMetadata.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerPageMetadata.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +/// +/// Concrete implementation of . +/// +internal class ProjectDesignerPageMetadata : IPageMetadata { - /// - /// Concrete implementation of . - /// - internal class ProjectDesignerPageMetadata : IPageMetadata + public ProjectDesignerPageMetadata(Guid pageGuid, int pageOrder, bool hasConfigurationCondition) { - public ProjectDesignerPageMetadata(Guid pageGuid, int pageOrder, bool hasConfigurationCondition) - { - if (pageGuid == Guid.Empty) - throw new ArgumentException(null, nameof(pageGuid)); + if (pageGuid == Guid.Empty) + throw new ArgumentException(null, nameof(pageGuid)); - PageGuid = pageGuid; - PageOrder = pageOrder; - HasConfigurationCondition = hasConfigurationCondition; - } + PageGuid = pageGuid; + PageOrder = pageOrder; + HasConfigurationCondition = hasConfigurationCondition; + } - public string? Name => null; + public string? Name => null; - public bool HasConfigurationCondition { get; } + public bool HasConfigurationCondition { get; } - public Guid PageGuid { get; } + public Guid PageGuid { get; } - public int PageOrder { get; } - } + public int PageOrder { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerService.cs index 74d45264e8..ec107b12ee 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectDesignerService.cs @@ -4,49 +4,48 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +/// +/// Provides an implementation of based on Visual Studio services. +/// +[Export(typeof(IProjectDesignerService))] +internal class ProjectDesignerService : IProjectDesignerService { - /// - /// Provides an implementation of based on Visual Studio services. - /// - [Export(typeof(IProjectDesignerService))] - internal class ProjectDesignerService : IProjectDesignerService - { - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IVsProjectDesignerPageService _vsProjectDesignerPageService; + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IVsProjectDesignerPageService _vsProjectDesignerPageService; - [ImportingConstructor] - public ProjectDesignerService(IUnconfiguredProjectVsServices projectVsServices, IVsProjectDesignerPageService vsProjectDesignerPageService) - { - _projectVsServices = projectVsServices; - _vsProjectDesignerPageService = vsProjectDesignerPageService; - } + [ImportingConstructor] + public ProjectDesignerService(IUnconfiguredProjectVsServices projectVsServices, IVsProjectDesignerPageService vsProjectDesignerPageService) + { + _projectVsServices = projectVsServices; + _vsProjectDesignerPageService = vsProjectDesignerPageService; + } - public bool SupportsProjectDesigner => _vsProjectDesignerPageService.IsProjectDesignerSupported; + public bool SupportsProjectDesigner => _vsProjectDesignerPageService.IsProjectDesignerSupported; - public Task ShowProjectDesignerAsync() + public Task ShowProjectDesignerAsync() + { + if (SupportsProjectDesigner) { - if (SupportsProjectDesigner) - { - return OpenProjectDesignerCoreAsync(); - } - - throw new InvalidOperationException("This project does not support the Project Designer (SupportsProjectDesigner is false)."); + return OpenProjectDesignerCoreAsync(); } - private async Task OpenProjectDesignerCoreAsync() - { - Guid projectDesignerGuid = _projectVsServices.VsHierarchy.GetGuidProperty(VsHierarchyPropID.ProjectDesignerEditor); + throw new InvalidOperationException("This project does not support the Project Designer (SupportsProjectDesigner is false)."); + } + + private async Task OpenProjectDesignerCoreAsync() + { + Guid projectDesignerGuid = _projectVsServices.VsHierarchy.GetGuidProperty(VsHierarchyPropID.ProjectDesignerEditor); - IVsWindowFrame? frame = _projectVsServices.VsProject.OpenItemWithSpecific(HierarchyId.Root, projectDesignerGuid); + IVsWindowFrame? frame = _projectVsServices.VsProject.OpenItemWithSpecific(HierarchyId.Root, projectDesignerGuid); - if (frame is not null) - { // Opened within Visual Studio - // Can only use Shell APIs on the UI thread - await _projectVsServices.ThreadingService.SwitchToUIThread(); + if (frame is not null) + { // Opened within Visual Studio + // Can only use Shell APIs on the UI thread + await _projectVsServices.ThreadingService.SwitchToUIThread(); - Verify.HResult(frame.Show()); - } + Verify.HResult(frame.Show()); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs index 8db5a4be53..06f364fe85 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/ProjectFileOrAssemblyInfoPropertiesProvider.cs @@ -5,43 +5,42 @@ using Microsoft.VisualStudio.ProjectSystem.LanguageServices; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +/// +/// A provider for properties that are stored either in the project file OR in the source code of the project. +/// This is defined in the VS layer so that we can import . +/// +[Export("ProjectFileOrAssemblyInfo", typeof(IProjectPropertiesProvider))] +[Export(typeof(IProjectPropertiesProvider))] +[ExportMetadata("Name", "ProjectFileOrAssemblyInfo")] +[AppliesTo(ProjectCapability.DotNet)] +internal class ProjectFileOrAssemblyInfoPropertiesProvider : AbstractProjectFileOrAssemblyInfoPropertiesProvider { - /// - /// A provider for properties that are stored either in the project file OR in the source code of the project. - /// This is defined in the VS layer so that we can import . - /// - [Export("ProjectFileOrAssemblyInfo", typeof(IProjectPropertiesProvider))] - [Export(typeof(IProjectPropertiesProvider))] - [ExportMetadata("Name", "ProjectFileOrAssemblyInfo")] - [AppliesTo(ProjectCapability.DotNet)] - internal class ProjectFileOrAssemblyInfoPropertiesProvider : AbstractProjectFileOrAssemblyInfoPropertiesProvider + [ImportingConstructor] + public ProjectFileOrAssemblyInfoPropertiesProvider( + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider delegatedProvider, + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, + [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)] IEnumerable> interceptingValueProviders, + UnconfiguredProject project, + IWorkspaceWriter workspaceWriter, + VisualStudioWorkspace workspace, + IProjectThreadingService threadingService) + : base(delegatedProvider, instanceProvider, interceptingValueProviders, project, + getActiveProjectId: () => GetProjectId(threadingService, workspaceWriter), + workspace: workspace, + threadingService: threadingService) { - [ImportingConstructor] - public ProjectFileOrAssemblyInfoPropertiesProvider( - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider delegatedProvider, - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, - [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)] IEnumerable> interceptingValueProviders, - UnconfiguredProject project, - IWorkspaceWriter workspaceWriter, - VisualStudioWorkspace workspace, - IProjectThreadingService threadingService) - : base(delegatedProvider, instanceProvider, interceptingValueProviders, project, - getActiveProjectId: () => GetProjectId(threadingService, workspaceWriter), - workspace: workspace, - threadingService: threadingService) - { - } + } - private static ProjectId? GetProjectId(IProjectThreadingService threadingService, IWorkspaceWriter workspaceWriter) + private static ProjectId? GetProjectId(IProjectThreadingService threadingService, IWorkspaceWriter workspaceWriter) + { + return threadingService.ExecuteSynchronously(() => { - return threadingService.ExecuteSynchronously(() => + return workspaceWriter.WriteAsync(workspace => { - return workspaceWriter.WriteAsync(workspace => - { - return Task.FromResult(workspace.Context.Id); - }); + return Task.FromResult(workspace.Context.Id); }); - } + }); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/StartupObjectsEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/StartupObjectsEnumProvider.cs index 830d273cc9..42be26b265 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/StartupObjectsEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/StartupObjectsEnumProvider.cs @@ -5,105 +5,104 @@ using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Returns the set of Startup objects (or entry point types) in a project. +/// +[ExportDynamicEnumValuesProvider("StartupObjectsEnumProvider")] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +internal class StartupObjectsEnumProvider : IDynamicEnumValuesProvider { - /// - /// Returns the set of Startup objects (or entry point types) in a project. - /// - [ExportDynamicEnumValuesProvider("StartupObjectsEnumProvider")] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - internal class StartupObjectsEnumProvider : IDynamicEnumValuesProvider + private readonly Workspace _workspace; + private readonly UnconfiguredProject _unconfiguredProject; + + [ImportingConstructor] + public StartupObjectsEnumProvider([Import(typeof(VisualStudioWorkspace))] Workspace workspace, UnconfiguredProject project) { - private readonly Workspace _workspace; - private readonly UnconfiguredProject _unconfiguredProject; + _workspace = workspace; + _unconfiguredProject = project; + } - [ImportingConstructor] - public StartupObjectsEnumProvider([Import(typeof(VisualStudioWorkspace))] Workspace workspace, UnconfiguredProject project) - { - _workspace = workspace; - _unconfiguredProject = project; - } + public Task GetProviderAsync(IList? options) + { + bool searchForEntryPointsInFormsOnly = options?.Any(pair => + pair.Name == "SearchForEntryPointsInFormsOnly" + && bool.TryParse(pair.Value, out bool optionValue) + && optionValue) ?? false; - public Task GetProviderAsync(IList? options) - { - bool searchForEntryPointsInFormsOnly = options?.Any(pair => - pair.Name == "SearchForEntryPointsInFormsOnly" - && bool.TryParse(pair.Value, out bool optionValue) - && optionValue) ?? false; - - // We only include a value representing the "not set" state if requested. This is - // because the old property pages explicitly add the "(Not set)" value at the UI - // layer; the new property pages do not have that option and so the value must come - // from the enum provider. - // When this project system no longer needs to support the old property pages we can - // remove this and always include the "(Not set)" value. - bool includeEmptyValue = options?.Any(pair => - pair.Name == "IncludeEmptyValue" - && bool.TryParse(pair.Value, out bool optionValue) - && optionValue) ?? false; - - return Task.FromResult(new StartupObjectsEnumGenerator(_workspace, _unconfiguredProject, includeEmptyValue, searchForEntryPointsInFormsOnly)); - } + // We only include a value representing the "not set" state if requested. This is + // because the old property pages explicitly add the "(Not set)" value at the UI + // layer; the new property pages do not have that option and so the value must come + // from the enum provider. + // When this project system no longer needs to support the old property pages we can + // remove this and always include the "(Not set)" value. + bool includeEmptyValue = options?.Any(pair => + pair.Name == "IncludeEmptyValue" + && bool.TryParse(pair.Value, out bool optionValue) + && optionValue) ?? false; + + return Task.FromResult(new StartupObjectsEnumGenerator(_workspace, _unconfiguredProject, includeEmptyValue, searchForEntryPointsInFormsOnly)); } +} - internal class StartupObjectsEnumGenerator : IDynamicEnumValuesGenerator +internal class StartupObjectsEnumGenerator : IDynamicEnumValuesGenerator +{ + public bool AllowCustomValues => true; + private readonly Workspace _workspace; + private readonly UnconfiguredProject _unconfiguredProject; + private readonly bool _includeEmptyValue; + private readonly bool _searchForEntryPointsInFormsOnly; + + public StartupObjectsEnumGenerator(Workspace workspace, UnconfiguredProject project, bool includeEmptyValue, bool searchForEntryPointsInFormsOnly) { - public bool AllowCustomValues => true; - private readonly Workspace _workspace; - private readonly UnconfiguredProject _unconfiguredProject; - private readonly bool _includeEmptyValue; - private readonly bool _searchForEntryPointsInFormsOnly; + _workspace = workspace; + _unconfiguredProject = project; + _includeEmptyValue = includeEmptyValue; + _searchForEntryPointsInFormsOnly = searchForEntryPointsInFormsOnly; + } - public StartupObjectsEnumGenerator(Workspace workspace, UnconfiguredProject project, bool includeEmptyValue, bool searchForEntryPointsInFormsOnly) + public async Task> GetListedValuesAsync() + { + Project? project = _workspace.CurrentSolution.Projects.FirstOrDefault(p => PathHelper.IsSamePath(p.FilePath!, _unconfiguredProject.FullPath)); + if (project is null) { - _workspace = workspace; - _unconfiguredProject = project; - _includeEmptyValue = includeEmptyValue; - _searchForEntryPointsInFormsOnly = searchForEntryPointsInFormsOnly; + return Array.Empty(); } - public async Task> GetListedValuesAsync() + Compilation? compilation = await project.GetCompilationAsync(); + if (compilation is null) { - Project? project = _workspace.CurrentSolution.Projects.FirstOrDefault(p => PathHelper.IsSamePath(p.FilePath!, _unconfiguredProject.FullPath)); - if (project is null) - { - return Array.Empty(); - } - - Compilation? compilation = await project.GetCompilationAsync(); - if (compilation is null) - { - // Project does not support compilations - return Array.Empty(); - } + // Project does not support compilations + return Array.Empty(); + } - List enumValues = new(); - if (_includeEmptyValue) - { - enumValues.Add(new PageEnumValue(new EnumValue { Name = string.Empty, DisplayName = VSResources.StartupObjectNotSet })); - } + List enumValues = new(); + if (_includeEmptyValue) + { + enumValues.Add(new PageEnumValue(new EnumValue { Name = string.Empty, DisplayName = VSResources.StartupObjectNotSet })); + } - IEntryPointFinderService? entryPointFinderService = project.Services.GetService(); - IEnumerable? entryPoints = entryPointFinderService?.FindEntryPoints(compilation.GlobalNamespace, _searchForEntryPointsInFormsOnly); - if (entryPoints is not null) + IEntryPointFinderService? entryPointFinderService = project.Services.GetService(); + IEnumerable? entryPoints = entryPointFinderService?.FindEntryPoints(compilation.GlobalNamespace, _searchForEntryPointsInFormsOnly); + if (entryPoints is not null) + { + enumValues.AddRange(entryPoints.Select(ep => { - enumValues.AddRange(entryPoints.Select(ep => - { - string name = ep.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)); - return new PageEnumValue(new EnumValue { Name = name, DisplayName = name }); - })); - } + string name = ep.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)); + return new PageEnumValue(new EnumValue { Name = name, DisplayName = name }); + })); + } - // Remove My.MyApplication entry if any. - enumValues.RemoveAll(ep => ep.Name.Contains("My.MyApplication")); + // Remove My.MyApplication entry if any. + enumValues.RemoveAll(ep => ep.Name.Contains("My.MyApplication")); - return enumValues; - } + return enumValues; + } - public Task TryCreateEnumValueAsync(string userSuppliedValue) - { - var value = new PageEnumValue(new EnumValue { Name = userSuppliedValue, DisplayName = userSuppliedValue }); - return Task.FromResult(value); - } + public Task TryCreateEnumValueAsync(string userSuppliedValue) + { + var value = new PageEnumValue(new EnumValue { Name = userSuppliedValue, DisplayName = userSuppliedValue }); + return Task.FromResult(value); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProvider.cs index 9df2cf3704..a561db2d32 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProvider.cs @@ -3,43 +3,42 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +/// +/// This Dynamic Enum Value Provider provides enum based msbuild property +/// value to display in the UI and also map the value, we obtain from the +/// UI to msbuild complaint values for persistence +/// +internal class MapDynamicEnumValuesProvider : IDynamicEnumValuesGenerator { - /// - /// This Dynamic Enum Value Provider provides enum based msbuild property - /// value to display in the UI and also map the value, we obtain from the - /// UI to msbuild complaint values for persistence - /// - internal class MapDynamicEnumValuesProvider : IDynamicEnumValuesGenerator + private readonly IDictionary _valueMap; + private readonly ICollection? _getValues; + + public MapDynamicEnumValuesProvider( + IDictionary valueMap, + ICollection? getValues = null) { - private readonly IDictionary _valueMap; - private readonly ICollection? _getValues; + Requires.NotNull(valueMap); - public MapDynamicEnumValuesProvider( - IDictionary valueMap, - ICollection? getValues = null) - { - Requires.NotNull(valueMap); + _valueMap = valueMap; + _getValues = getValues; + } - _valueMap = valueMap; - _getValues = getValues; - } + public bool AllowCustomValues => false; - public bool AllowCustomValues => false; + public Task> GetListedValuesAsync() + { + return Task.FromResult(_getValues ?? _valueMap.Values); + } - public Task> GetListedValuesAsync() + public Task TryCreateEnumValueAsync(string userSuppliedValue) + { + if (_valueMap.TryGetValue(userSuppliedValue, out IEnumValue? value)) { - return Task.FromResult(_getValues ?? _valueMap.Values); + return Task.FromResult(value); } - public Task TryCreateEnumValueAsync(string userSuppliedValue) - { - if (_valueMap.TryGetValue(userSuppliedValue, out IEnumValue? value)) - { - return Task.FromResult(value); - } - - return TaskResult.Null(); - } + return TaskResult.Null(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/OptionStrictEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/OptionStrictEnumProvider.cs index 2317fb9951..21e4511c1b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/OptionStrictEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/OptionStrictEnumProvider.cs @@ -3,34 +3,33 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic -{ - [ExportDynamicEnumValuesProvider("OptionStrictEnumProvider")] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class OptionStrictEnumProvider : IDynamicEnumValuesProvider - { - /// The value obtained from the EnumValue, when asked for value, is the , unless otherwise - /// a different overload is used to obtain the value of . Hence a provider usually needs just a map - /// with a typical entry looking like, [ValuePersistedByUI] -> EnumValue {Name = [ValueToPersist] DisplayName = [ValueToReturnForGet]} - /// In the case of OptionStrict, Compile Property Page explicitly gets the value from for the Enums and - /// casts them to integer. This requires us to make as '0' and '1' and hence cannot reuse - /// Values to be used when trying to retrieve value for the enum. - private readonly ICollection _listedOptionStrictEnumValues = new List - { - new PageEnumValue(new EnumValue {Name = "0", DisplayName = "Off", IsDefault = true }), - new PageEnumValue(new EnumValue {Name = "1", DisplayName = "On" }) - }; +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; - private readonly Dictionary _persistOptionStrictEnumValues = new() +[ExportDynamicEnumValuesProvider("OptionStrictEnumProvider")] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class OptionStrictEnumProvider : IDynamicEnumValuesProvider +{ + /// The value obtained from the EnumValue, when asked for value, is the , unless otherwise + /// a different overload is used to obtain the value of . Hence a provider usually needs just a map + /// with a typical entry looking like, [ValuePersistedByUI] -> EnumValue {Name = [ValueToPersist] DisplayName = [ValueToReturnForGet]} + /// In the case of OptionStrict, Compile Property Page explicitly gets the value from for the Enums and + /// casts them to integer. This requires us to make as '0' and '1' and hence cannot reuse + /// Values to be used when trying to retrieve value for the enum. + private readonly ICollection _listedOptionStrictEnumValues = new List { - { "0", new PageEnumValue(new EnumValue {Name = "Off" }) }, - { "1", new PageEnumValue(new EnumValue {Name = "On" }) }, - }; + new PageEnumValue(new EnumValue {Name = "0", DisplayName = "Off", IsDefault = true }), + new PageEnumValue(new EnumValue {Name = "1", DisplayName = "On" }) + }; - public Task GetProviderAsync(IList? options) - { - return Task.FromResult( - new MapDynamicEnumValuesProvider(_persistOptionStrictEnumValues, _listedOptionStrictEnumValues)); - } + private readonly Dictionary _persistOptionStrictEnumValues = new() + { + { "0", new PageEnumValue(new EnumValue {Name = "Off" }) }, + { "1", new PageEnumValue(new EnumValue {Name = "On" }) }, + }; + + public Task GetProviderAsync(IList? options) + { + return Task.FromResult( + new MapDynamicEnumValuesProvider(_persistOptionStrictEnumValues, _listedOptionStrictEnumValues)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationProperties.cs index e7e72008c7..59bb0d5c69 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationProperties.cs @@ -2,20 +2,19 @@ using VSLangProj110; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +[Export(ExportContractNames.VsTypes.ConfiguredProjectPropertiesAutomationObject)] +[Order(Order.Default)] +[AppliesTo(ProjectCapability.VisualBasic)] +public class VisualBasicProjectConfigurationProperties : AbstractProjectConfigurationProperties, + VBProjectConfigurationProperties6 { - [Export(ExportContractNames.VsTypes.ConfiguredProjectPropertiesAutomationObject)] - [Order(Order.Default)] - [AppliesTo(ProjectCapability.VisualBasic)] - public class VisualBasicProjectConfigurationProperties : AbstractProjectConfigurationProperties, - VBProjectConfigurationProperties6 + [ImportingConstructor] + internal VisualBasicProjectConfigurationProperties( + ProjectProperties projectProperties, + IProjectThreadingService threadingService) + : base(projectProperties, threadingService) { - [ImportingConstructor] - internal VisualBasicProjectConfigurationProperties( - ProjectProperties projectProperties, - IProjectThreadingService threadingService) - : base(projectProperties, threadingService) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPage.cs index 282c63d27d..bcfe800dc0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPage.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +/// +/// Provides common well-known Visual Basic project property pages. +/// +internal static class VisualBasicProjectDesignerPage { - /// - /// Provides common well-known Visual Basic project property pages. - /// - internal static class VisualBasicProjectDesignerPage - { - public static readonly ProjectDesignerPageMetadata Application = new(new Guid("{8998E48E-B89A-4034-B66E-353D8C1FDC2E}"), pageOrder: 0, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Compile = new(new Guid("{EDA661EA-DC61-4750-B3A5-F6E9C74060F5}"), pageOrder: 0, hasConfigurationCondition: true); - public static readonly ProjectDesignerPageMetadata Package = new(new Guid("{21b78be8-3957-4caa-bf2f-e626107da58e}"), pageOrder: 0, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata References = new(new Guid("{4E43F4AB-9F03-4129-95BF-B8FF870AF6AB}"), pageOrder: 1, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Debug = new(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"), pageOrder: 2, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata Signing = new(new Guid("{F8D6553F-F752-4DBF-ACB6-F291B744A792}"), pageOrder: 6, hasConfigurationCondition: false); - public static readonly ProjectDesignerPageMetadata CodeAnalysis = new(new Guid("{c02f393c-8a1e-480d-aa82-6a75d693559d}"), pageOrder: 7, hasConfigurationCondition: false); - } + public static readonly ProjectDesignerPageMetadata Application = new(new Guid("{8998E48E-B89A-4034-B66E-353D8C1FDC2E}"), pageOrder: 0, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Compile = new(new Guid("{EDA661EA-DC61-4750-B3A5-F6E9C74060F5}"), pageOrder: 0, hasConfigurationCondition: true); + public static readonly ProjectDesignerPageMetadata Package = new(new Guid("{21b78be8-3957-4caa-bf2f-e626107da58e}"), pageOrder: 0, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata References = new(new Guid("{4E43F4AB-9F03-4129-95BF-B8FF870AF6AB}"), pageOrder: 1, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Debug = new(new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"), pageOrder: 2, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata Signing = new(new Guid("{F8D6553F-F752-4DBF-ACB6-F291B744A792}"), pageOrder: 6, hasConfigurationCondition: false); + public static readonly ProjectDesignerPageMetadata CodeAnalysis = new(new Guid("{c02f393c-8a1e-480d-aa82-6a75d693559d}"), pageOrder: 7, hasConfigurationCondition: false); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProvider.cs index 486dfa2fc7..8496d901fb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProvider.cs @@ -2,46 +2,45 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +/// +/// Provides project designer property pages. +/// +[Export(typeof(IVsProjectDesignerPageProvider))] +[AppliesTo(ProjectCapability.VisualBasicAppDesigner)] +internal class VisualBasicProjectDesignerPageProvider : IVsProjectDesignerPageProvider { - /// - /// Provides project designer property pages. - /// - [Export(typeof(IVsProjectDesignerPageProvider))] - [AppliesTo(ProjectCapability.VisualBasicAppDesigner)] - internal class VisualBasicProjectDesignerPageProvider : IVsProjectDesignerPageProvider - { - private readonly IProjectCapabilitiesService _capabilities; + private readonly IProjectCapabilitiesService _capabilities; - [ImportingConstructor] - internal VisualBasicProjectDesignerPageProvider(IProjectCapabilitiesService capabilities) - { - _capabilities = capabilities; - } + [ImportingConstructor] + internal VisualBasicProjectDesignerPageProvider(IProjectCapabilitiesService capabilities) + { + _capabilities = capabilities; + } - public Task> GetPagesAsync() - { - var builder = PooledArray.GetInstance(capacity: 7); + public Task> GetPagesAsync() + { + var builder = PooledArray.GetInstance(capacity: 7); - builder.Add(VisualBasicProjectDesignerPage.Application); - builder.Add(VisualBasicProjectDesignerPage.Compile); + builder.Add(VisualBasicProjectDesignerPage.Application); + builder.Add(VisualBasicProjectDesignerPage.Compile); - if (_capabilities.Contains(ProjectCapability.Pack)) - { - builder.Add(VisualBasicProjectDesignerPage.Package); - } + if (_capabilities.Contains(ProjectCapability.Pack)) + { + builder.Add(VisualBasicProjectDesignerPage.Package); + } - builder.Add(VisualBasicProjectDesignerPage.References); + builder.Add(VisualBasicProjectDesignerPage.References); - if (_capabilities.Contains(ProjectCapability.LaunchProfiles)) - { - builder.Add(VisualBasicProjectDesignerPage.Debug); - } + if (_capabilities.Contains(ProjectCapability.LaunchProfiles)) + { + builder.Add(VisualBasicProjectDesignerPage.Debug); + } - builder.Add(VisualBasicProjectDesignerPage.Signing); - builder.Add(VisualBasicProjectDesignerPage.CodeAnalysis); + builder.Add(VisualBasicProjectDesignerPage.Signing); + builder.Add(VisualBasicProjectDesignerPage.CodeAnalysis); - return Task.FromResult>(builder.ToImmutableAndFree()); - } + return Task.FromResult>(builder.ToImmutableAndFree()); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/WarningLevelEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/WarningLevelEnumProvider.cs index 858e298419..fda798d0eb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/WarningLevelEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Properties/VisualBasic/WarningLevelEnumProvider.cs @@ -4,24 +4,23 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +[ExportDynamicEnumValuesProvider("WarningLevelEnumProvider")] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class WarningLevelEnumProvider : IDynamicEnumValuesProvider { - [ExportDynamicEnumValuesProvider("WarningLevelEnumProvider")] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class WarningLevelEnumProvider : IDynamicEnumValuesProvider + private readonly Dictionary _persistWarningLevelEnumValues = new() { - private readonly Dictionary _persistWarningLevelEnumValues = new() - { - { nameof(prjWarningLevel.prjWarningLevel0), new PageEnumValue(new EnumValue {Name = "0" }) }, - { nameof(prjWarningLevel.prjWarningLevel1), new PageEnumValue(new EnumValue {Name = "1" }) }, - { nameof(prjWarningLevel.prjWarningLevel2), new PageEnumValue(new EnumValue {Name = "2" }) }, - { nameof(prjWarningLevel.prjWarningLevel3), new PageEnumValue(new EnumValue {Name = "3" }) }, - { nameof(prjWarningLevel.prjWarningLevel4), new PageEnumValue(new EnumValue {Name = "4" }) }, - }; + { nameof(prjWarningLevel.prjWarningLevel0), new PageEnumValue(new EnumValue {Name = "0" }) }, + { nameof(prjWarningLevel.prjWarningLevel1), new PageEnumValue(new EnumValue {Name = "1" }) }, + { nameof(prjWarningLevel.prjWarningLevel2), new PageEnumValue(new EnumValue {Name = "2" }) }, + { nameof(prjWarningLevel.prjWarningLevel3), new PageEnumValue(new EnumValue {Name = "3" }) }, + { nameof(prjWarningLevel.prjWarningLevel4), new PageEnumValue(new EnumValue {Name = "4" }) }, + }; - public Task GetProviderAsync(IList? options) - { - return Task.FromResult(new MapDynamicEnumValuesProvider(_persistWarningLevelEnumValues)); - } + public Task GetProviderAsync(IList? options) + { + return Task.FromResult(new MapDynamicEnumValuesProvider(_persistWarningLevelEnumValues)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/BuildMacroInfo.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/BuildMacroInfo.cs index d3b85a1b3a..7b5de368ed 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/BuildMacroInfo.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/BuildMacroInfo.cs @@ -2,69 +2,68 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +/// +/// Implements the interface to be consumed by project properties. +/// +[ExportProjectNodeComService(typeof(IVsBuildMacroInfo))] +[AppliesTo(ProjectCapability.DotNet)] +internal class BuildMacroInfo : IVsBuildMacroInfo, IDisposable { + private IProjectThreadingService? _threadingService; + private IActiveConfiguredValue? _configuredProject; + /// - /// Implements the interface to be consumed by project properties. + /// Initializes a new instance of the class. /// - [ExportProjectNodeComService(typeof(IVsBuildMacroInfo))] - [AppliesTo(ProjectCapability.DotNet)] - internal class BuildMacroInfo : IVsBuildMacroInfo, IDisposable + /// Project being evaluated. + /// Project threading service. + [ImportingConstructor] + public BuildMacroInfo( + IActiveConfiguredValue configuredProject, + IProjectThreadingService threadingService) { - private IProjectThreadingService? _threadingService; - private IActiveConfiguredValue? _configuredProject; + _threadingService = threadingService; + _configuredProject = configuredProject; + } - /// - /// Initializes a new instance of the class. - /// - /// Project being evaluated. - /// Project threading service. - [ImportingConstructor] - public BuildMacroInfo( - IActiveConfiguredValue configuredProject, - IProjectThreadingService threadingService) + /// + /// Retrieves the value or body of a macro based on the macro's name. + /// + /// String containing the name of the macro. + /// String containing the value or body of the macro. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public int GetBuildMacroValue(string bstrBuildMacroName, out string? pbstrBuildMacroValue) + { + if (_configuredProject is null) { - _threadingService = threadingService; - _configuredProject = configuredProject; + pbstrBuildMacroValue = null; + return HResult.Unexpected; } - /// - /// Retrieves the value or body of a macro based on the macro's name. - /// - /// String containing the name of the macro. - /// String containing the value or body of the macro. - /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. - public int GetBuildMacroValue(string bstrBuildMacroName, out string? pbstrBuildMacroValue) - { - if (_configuredProject is null) - { - pbstrBuildMacroValue = null; - return HResult.Unexpected; - } + Assumes.Present(_configuredProject.Value.Services.ProjectPropertiesProvider); - Assumes.Present(_configuredProject.Value.Services.ProjectPropertiesProvider); + pbstrBuildMacroValue = null; + ProjectSystem.Properties.IProjectProperties commonProperties = _configuredProject.Value.Services.ProjectPropertiesProvider.GetCommonProperties(); + pbstrBuildMacroValue = _threadingService?.ExecuteSynchronously(() => commonProperties.GetEvaluatedPropertyValueAsync(bstrBuildMacroName)); - pbstrBuildMacroValue = null; - ProjectSystem.Properties.IProjectProperties commonProperties = _configuredProject.Value.Services.ProjectPropertiesProvider.GetCommonProperties(); - pbstrBuildMacroValue = _threadingService?.ExecuteSynchronously(() => commonProperties.GetEvaluatedPropertyValueAsync(bstrBuildMacroName)); - - if (string.IsNullOrEmpty(pbstrBuildMacroValue)) - { - pbstrBuildMacroValue = string.Empty; - return HResult.Fail; - } - else - { - return HResult.OK; - } + if (string.IsNullOrEmpty(pbstrBuildMacroValue)) + { + pbstrBuildMacroValue = string.Empty; + return HResult.Fail; } - - public void Dispose() + else { - // Important for ProjectNodeComServices to null out fields to reduce the amount - // of data we leak when extensions incorrectly holds onto the IVsHierarchy. - _threadingService = null; - _configuredProject = null; + return HResult.OK; } } + + public void Dispose() + { + // Important for ProjectNodeComServices to null out fields to reduce the amount + // of data we leak when extensions incorrectly holds onto the IVsHierarchy. + _threadingService = null; + _configuredProject = null; + } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs index 897e5cd84d..c1d3fa2454 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/ImplicitUsingsEditor/CSharpImplicitUsingsControl.cs @@ -8,387 +8,386 @@ using Microsoft.VisualStudio.PlatformUI; using Newtonsoft.Json; -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Controls +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Controls; + +internal sealed class CSharpImplicitUsingsControl : Control { - internal sealed class CSharpImplicitUsingsControl : Control + public static readonly DependencyProperty AddUserUsingCommandProperty = DependencyProperty.Register( + nameof(AddUserUsingCommand), + typeof(ICommand), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata()); + + public static readonly DependencyProperty StringListProperty = DependencyProperty.Register( + nameof(StringList), + typeof(string), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata(string.Empty, (o, e) => ((CSharpImplicitUsingsControl)o).OnStringListOrSupportedValuesPropertyChanged())); + + public static readonly DependencyProperty UsingCollectionStateProperty = DependencyProperty.Register( + nameof(UsingCollectionState), + typeof(ObservableCollection), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata(new ObservableCollection())); + + public static readonly DependencyProperty EnteredUserUsingIncludesProperty = DependencyProperty.Register( + nameof(EnteredUserUsingIncludes), + typeof(string), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata(string.Empty)); + + public static readonly DependencyProperty EnteredUserUsingAliasProperty = DependencyProperty.Register( + nameof(EnteredUserUsingAlias), + typeof(string), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata(string.Empty)); + + public static readonly DependencyProperty EnteredUserUsingIsStaticProperty = DependencyProperty.Register( + nameof(EnteredUserUsingIsStatic), + typeof(bool), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata(false)); + + public static readonly DependencyProperty ShouldShowReadonlyUsingsProperty = DependencyProperty.Register( + nameof(ShouldShowReadonlyUsings), + typeof(bool), + typeof(CSharpImplicitUsingsControl), + new PropertyMetadata(true, (o, e) => ((CSharpImplicitUsingsControl)o).OnStringListOrSupportedValuesPropertyChanged())); + + // Used to suppress event handling during our own updates, breaking infinite loops. + private bool _isUpdating; + + public CSharpImplicitUsingsControl() { - public static readonly DependencyProperty AddUserUsingCommandProperty = DependencyProperty.Register( - nameof(AddUserUsingCommand), - typeof(ICommand), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata()); - - public static readonly DependencyProperty StringListProperty = DependencyProperty.Register( - nameof(StringList), - typeof(string), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata(string.Empty, (o, e) => ((CSharpImplicitUsingsControl)o).OnStringListOrSupportedValuesPropertyChanged())); - - public static readonly DependencyProperty UsingCollectionStateProperty = DependencyProperty.Register( - nameof(UsingCollectionState), - typeof(ObservableCollection), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata(new ObservableCollection())); - - public static readonly DependencyProperty EnteredUserUsingIncludesProperty = DependencyProperty.Register( - nameof(EnteredUserUsingIncludes), - typeof(string), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata(string.Empty)); - - public static readonly DependencyProperty EnteredUserUsingAliasProperty = DependencyProperty.Register( - nameof(EnteredUserUsingAlias), - typeof(string), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata(string.Empty)); - - public static readonly DependencyProperty EnteredUserUsingIsStaticProperty = DependencyProperty.Register( - nameof(EnteredUserUsingIsStatic), - typeof(bool), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata(false)); - - public static readonly DependencyProperty ShouldShowReadonlyUsingsProperty = DependencyProperty.Register( - nameof(ShouldShowReadonlyUsings), - typeof(bool), - typeof(CSharpImplicitUsingsControl), - new PropertyMetadata(true, (o, e) => ((CSharpImplicitUsingsControl)o).OnStringListOrSupportedValuesPropertyChanged())); - - // Used to suppress event handling during our own updates, breaking infinite loops. - private bool _isUpdating; - - public CSharpImplicitUsingsControl() - { - UsingCollectionState = new ObservableCollection(); + UsingCollectionState = new ObservableCollection(); #pragma warning disable VSTHRD012 - AddUserUsingCommand = new DelegateCommand(_ => + AddUserUsingCommand = new DelegateCommand(_ => + { + string usingIncludes = EnteredUserUsingIncludes; + if (usingIncludes.Length == 0) { - string usingIncludes = EnteredUserUsingIncludes; - if (usingIncludes.Length == 0) - { - return; - } + return; + } - if (!UsingCollectionState.Any(implicitUsing => - string.Equals(implicitUsing.Include, usingIncludes, StringComparison.Ordinal))) - { - UsingCollectionState.Add(new ImplicitUsingModel(usingIncludes, EnteredUserUsingAlias, EnteredUserUsingIsStatic, false, this)); - NotifyPairChanged(); - } + if (!UsingCollectionState.Any(implicitUsing => + string.Equals(implicitUsing.Include, usingIncludes, StringComparison.Ordinal))) + { + UsingCollectionState.Add(new ImplicitUsingModel(usingIncludes, EnteredUserUsingAlias, EnteredUserUsingIsStatic, false, this)); + NotifyPairChanged(); + } - EnteredUserUsingIncludes = string.Empty; - EnteredUserUsingAlias = string.Empty; - EnteredUserUsingIsStatic = false; - }); + EnteredUserUsingIncludes = string.Empty; + EnteredUserUsingAlias = string.Empty; + EnteredUserUsingIsStatic = false; + }); #pragma warning restore VSTHRD012 - OnStringListOrSupportedValuesPropertyChanged(); - } + OnStringListOrSupportedValuesPropertyChanged(); + } - public ICommand AddUserUsingCommand - { - get => (ICommand)GetValue(AddUserUsingCommandProperty); - set => SetValue(AddUserUsingCommandProperty, value); - } + public ICommand AddUserUsingCommand + { + get => (ICommand)GetValue(AddUserUsingCommandProperty); + set => SetValue(AddUserUsingCommandProperty, value); + } - public string EnteredUserUsingIncludes - { - get => (string)GetValue(EnteredUserUsingIncludesProperty); - set => SetValue(EnteredUserUsingIncludesProperty, value); - } + public string EnteredUserUsingIncludes + { + get => (string)GetValue(EnteredUserUsingIncludesProperty); + set => SetValue(EnteredUserUsingIncludesProperty, value); + } - public string EnteredUserUsingAlias - { - get => (string)GetValue(EnteredUserUsingAliasProperty); - set => SetValue(EnteredUserUsingAliasProperty, value); - } + public string EnteredUserUsingAlias + { + get => (string)GetValue(EnteredUserUsingAliasProperty); + set => SetValue(EnteredUserUsingAliasProperty, value); + } - public bool EnteredUserUsingIsStatic - { - get => (bool)GetValue(EnteredUserUsingIsStaticProperty); - set => SetValue(EnteredUserUsingIsStaticProperty, value); - } + public bool EnteredUserUsingIsStatic + { + get => (bool)GetValue(EnteredUserUsingIsStaticProperty); + set => SetValue(EnteredUserUsingIsStaticProperty, value); + } - public bool ShouldShowReadonlyUsings - { - get => (bool)GetValue(ShouldShowReadonlyUsingsProperty); - set => SetValue(ShouldShowReadonlyUsingsProperty, value); - } + public bool ShouldShowReadonlyUsings + { + get => (bool)GetValue(ShouldShowReadonlyUsingsProperty); + set => SetValue(ShouldShowReadonlyUsingsProperty, value); + } - public string StringList - { - get => (string)GetValue(StringListProperty); - set => SetValue(StringListProperty, value); - } + public string StringList + { + get => (string)GetValue(StringListProperty); + set => SetValue(StringListProperty, value); + } + + public ObservableCollection UsingCollectionState + { + get => (ObservableCollection)GetValue(UsingCollectionStateProperty); + set => SetValue(UsingCollectionStateProperty, value); + } - public ObservableCollection UsingCollectionState + private void OnStringListOrSupportedValuesPropertyChanged() + { + if (_isUpdating) { - get => (ObservableCollection)GetValue(UsingCollectionStateProperty); - set => SetValue(UsingCollectionStateProperty, value); + return; } - private void OnStringListOrSupportedValuesPropertyChanged() + _isUpdating = true; + + try { - if (_isUpdating) + if (JsonConvert.DeserializeObject(StringList, typeof(List)) is not List rawImplicitUsings) { return; } - _isUpdating = true; - - try + rawImplicitUsings.Sort((x, y) => { - if (JsonConvert.DeserializeObject(StringList, typeof(List)) is not List rawImplicitUsings) + if ((x.IsReadOnly && y.IsReadOnly) || (!x.IsReadOnly && !y.IsReadOnly)) { - return; + return string.Compare(x.Include, y.Include, StringComparison.Ordinal); } - rawImplicitUsings.Sort((x, y) => - { - if ((x.IsReadOnly && y.IsReadOnly) || (!x.IsReadOnly && !y.IsReadOnly)) - { - return string.Compare(x.Include, y.Include, StringComparison.Ordinal); - } - - return x.IsReadOnly ? 1 : -1; - }); + return x.IsReadOnly ? 1 : -1; + }); - if (!ShouldShowReadonlyUsings) - { - rawImplicitUsings.RemoveAll(implicitUsing => implicitUsing.IsReadOnly); - } + if (!ShouldShowReadonlyUsings) + { + rawImplicitUsings.RemoveAll(implicitUsing => implicitUsing.IsReadOnly); + } - var currentlySetImplicitUsings = rawImplicitUsings.GroupBy(implicitUsing => implicitUsing.Include).Select(group => group.First()).ToList(); + var currentlySetImplicitUsings = rawImplicitUsings.GroupBy(implicitUsing => implicitUsing.Include).Select(group => group.First()).ToList(); - if (UsingCollectionState.Count == 0) + if (UsingCollectionState.Count == 0) + { + foreach (ImplicitUsing implicitUsing in currentlySetImplicitUsings) { - foreach (ImplicitUsing implicitUsing in currentlySetImplicitUsings) - { - UsingCollectionState.Add(new ImplicitUsingModel(implicitUsing.Include, implicitUsing.Alias ?? string.Empty, implicitUsing.IsStatic, implicitUsing.IsReadOnly, this)); - } + UsingCollectionState.Add(new ImplicitUsingModel(implicitUsing.Include, implicitUsing.Alias ?? string.Empty, implicitUsing.IsStatic, implicitUsing.IsReadOnly, this)); } - else - { - int stringIndex = 0; + } + else + { + int stringIndex = 0; - foreach (ImplicitUsing implicitUsing in currentlySetImplicitUsings) + foreach (ImplicitUsing implicitUsing in currentlySetImplicitUsings) + { + if (stringIndex < UsingCollectionState.Count) { - if (stringIndex < UsingCollectionState.Count) + ImplicitUsingModel existingUsingModel = UsingCollectionState[stringIndex]; + if (!existingUsingModel.ToImplicitUsing().Equals(implicitUsing)) { - ImplicitUsingModel existingUsingModel = UsingCollectionState[stringIndex]; - if (!existingUsingModel.ToImplicitUsing().Equals(implicitUsing)) + if (string.Equals(existingUsingModel.Include, implicitUsing.Include, StringComparison.Ordinal)) { - if (string.Equals(existingUsingModel.Include, implicitUsing.Include, StringComparison.Ordinal)) + if (!string.Equals(existingUsingModel.Alias, implicitUsing.Alias ?? string.Empty, StringComparison.Ordinal)) + { + existingUsingModel.Alias = implicitUsing.Alias ?? string.Empty; + } + + if (existingUsingModel.IsStatic != implicitUsing.IsStatic) { - if (!string.Equals(existingUsingModel.Alias, implicitUsing.Alias ?? string.Empty, StringComparison.Ordinal)) - { - existingUsingModel.Alias = implicitUsing.Alias ?? string.Empty; - } - - if (existingUsingModel.IsStatic != implicitUsing.IsStatic) - { - existingUsingModel.IsStatic = implicitUsing.IsStatic; - } - - if (existingUsingModel.IsReadOnly != implicitUsing.IsReadOnly) - { - existingUsingModel.IsReadOnly = implicitUsing.IsReadOnly; - } + existingUsingModel.IsStatic = implicitUsing.IsStatic; } - else + + if (existingUsingModel.IsReadOnly != implicitUsing.IsReadOnly) { - UsingCollectionState[stringIndex] = new ImplicitUsingModel(implicitUsing.Include, implicitUsing.Alias ?? string.Empty, implicitUsing.IsStatic, implicitUsing.IsReadOnly, this); + existingUsingModel.IsReadOnly = implicitUsing.IsReadOnly; } } + else + { + UsingCollectionState[stringIndex] = new ImplicitUsingModel(implicitUsing.Include, implicitUsing.Alias ?? string.Empty, implicitUsing.IsStatic, implicitUsing.IsReadOnly, this); + } } - else - { - UsingCollectionState.Add(new ImplicitUsingModel(implicitUsing.Include, implicitUsing.Alias ?? string.Empty, implicitUsing.IsStatic, implicitUsing.IsReadOnly, this)); - } - - stringIndex++; } + else + { + UsingCollectionState.Add(new ImplicitUsingModel(implicitUsing.Include, implicitUsing.Alias ?? string.Empty, implicitUsing.IsStatic, implicitUsing.IsReadOnly, this)); + } + + stringIndex++; + } - if (stringIndex < UsingCollectionState.Count - 1) + if (stringIndex < UsingCollectionState.Count - 1) + { + for (int i = UsingCollectionState.Count - 1; i >= stringIndex; i--) { - for (int i = UsingCollectionState.Count - 1; i >= stringIndex; i--) - { - UsingCollectionState.RemoveAt(i); - } + UsingCollectionState.RemoveAt(i); } } } - finally - { - _isUpdating = false; - } } - - public void NotifyPairChanged() + finally { - if (_isUpdating) - { - return; - } - - StringList = JsonConvert.SerializeObject(UsingCollectionState.Select(implicitUsingModel => implicitUsingModel.ToImplicitUsing())); + _isUpdating = false; } } - internal sealed class ImplicitUsingModel : INotifyPropertyChanged + public void NotifyPairChanged() { - private readonly CSharpImplicitUsingsControl _parent; - private static readonly PropertyChangedEventArgs s_includeChangeArgs = new(nameof(Include)); - private static readonly PropertyChangedEventArgs s_aliasChangeArgs = new(nameof(Alias)); - private static readonly PropertyChangedEventArgs s_isStaticChangeArgs = new(nameof(IsStatic)); - private static readonly PropertyChangedEventArgs s_isReadOnlyChangeArgs = new(nameof(IsReadOnly)); - private static readonly PropertyChangedEventArgs s_forceIncludesFocusChangeArgs = new(nameof(ForceIncludesFocus)); - private static readonly PropertyChangedEventArgs s_forceAliasFocusChangeArgs = new(nameof(ForceAliasFocus)); - private static readonly PropertyChangedEventArgs s_forceIsStaticFocusChangeArgs = new(nameof(ForceIsStaticFocus)); - - private string _include; - private string _alias; - private bool _isStatic; - private bool _isReadOnly; - - private bool _forceIncludesFocus; - private bool _forceAliasFocus; - private bool _forceIsStaticFocus; - - public ImplicitUsingModel(string include, string alias, bool isStatic, bool isReadOnly, CSharpImplicitUsingsControl parent) + if (_isUpdating) { - _include = include; - _alias = alias; - _isStatic = isStatic; - _isReadOnly = isReadOnly; - _parent = parent; + return; + } + + StringList = JsonConvert.SerializeObject(UsingCollectionState.Select(implicitUsingModel => implicitUsingModel.ToImplicitUsing())); + } +} + +internal sealed class ImplicitUsingModel : INotifyPropertyChanged +{ + private readonly CSharpImplicitUsingsControl _parent; + private static readonly PropertyChangedEventArgs s_includeChangeArgs = new(nameof(Include)); + private static readonly PropertyChangedEventArgs s_aliasChangeArgs = new(nameof(Alias)); + private static readonly PropertyChangedEventArgs s_isStaticChangeArgs = new(nameof(IsStatic)); + private static readonly PropertyChangedEventArgs s_isReadOnlyChangeArgs = new(nameof(IsReadOnly)); + private static readonly PropertyChangedEventArgs s_forceIncludesFocusChangeArgs = new(nameof(ForceIncludesFocus)); + private static readonly PropertyChangedEventArgs s_forceAliasFocusChangeArgs = new(nameof(ForceAliasFocus)); + private static readonly PropertyChangedEventArgs s_forceIsStaticFocusChangeArgs = new(nameof(ForceIsStaticFocus)); + + private string _include; + private string _alias; + private bool _isStatic; + private bool _isReadOnly; + + private bool _forceIncludesFocus; + private bool _forceAliasFocus; + private bool _forceIsStaticFocus; + + public ImplicitUsingModel(string include, string alias, bool isStatic, bool isReadOnly, CSharpImplicitUsingsControl parent) + { + _include = include; + _alias = alias; + _isStatic = isStatic; + _isReadOnly = isReadOnly; + _parent = parent; - _forceIncludesFocus = false; - _forceAliasFocus = false; - _forceIsStaticFocus = false; + _forceIncludesFocus = false; + _forceAliasFocus = false; + _forceIsStaticFocus = false; #pragma warning disable VSTHRD012 - RemoveUsingCommand = new DelegateCommand(model => + RemoveUsingCommand = new DelegateCommand(model => #pragma warning restore VSTHRD012 - { - model._parent.UsingCollectionState.Remove(model); - model._parent.NotifyPairChanged(); - }); - } + { + model._parent.UsingCollectionState.Remove(model); + model._parent.NotifyPairChanged(); + }); + } - public event PropertyChangedEventHandler? PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; - public ICommand RemoveUsingCommand { get; } + public ICommand RemoveUsingCommand { get; } - public string Include + public string Include + { + get => _include; + set { - get => _include; - set + // Name may not be empty, unless it was set as such in the constructor (for a new row) + if (string.IsNullOrWhiteSpace(value)) { - // Name may not be empty, unless it was set as such in the constructor (for a new row) - if (string.IsNullOrWhiteSpace(value)) - { - return; - } - - if (string.Equals(_include, value, StringComparison.Ordinal)) - { - return; - } - - _include = value; - PropertyChanged?.Invoke(this, s_includeChangeArgs); - _parent.NotifyPairChanged(); + return; } - } - public string Alias - { - get => _alias; - set + if (string.Equals(_include, value, StringComparison.Ordinal)) { - if (string.Equals(_alias, value, StringComparison.Ordinal)) - { - return; - } - - _alias = value; - PropertyChanged?.Invoke(this, s_aliasChangeArgs); - _parent.NotifyPairChanged(); + return; } + + _include = value; + PropertyChanged?.Invoke(this, s_includeChangeArgs); + _parent.NotifyPairChanged(); } + } - public bool IsStatic + public string Alias + { + get => _alias; + set { - get => _isStatic; - set + if (string.Equals(_alias, value, StringComparison.Ordinal)) { - if (_isStatic == value) - { - return; - } - - _isStatic = value; - PropertyChanged?.Invoke(this, s_isStaticChangeArgs); - _parent.NotifyPairChanged(); + return; } + + _alias = value; + PropertyChanged?.Invoke(this, s_aliasChangeArgs); + _parent.NotifyPairChanged(); } + } - public bool IsReadOnly + public bool IsStatic + { + get => _isStatic; + set { - get => _isReadOnly; - set + if (_isStatic == value) { - if (_isReadOnly == value) - { - return; - } - - _isReadOnly = value; - PropertyChanged?.Invoke(this, s_isReadOnlyChangeArgs); - _parent.NotifyPairChanged(); + return; } + + _isStatic = value; + PropertyChanged?.Invoke(this, s_isStaticChangeArgs); + _parent.NotifyPairChanged(); } + } - public bool ForceIncludesFocus + public bool IsReadOnly + { + get => _isReadOnly; + set { - get => _forceIncludesFocus; - set + if (_isReadOnly == value) { - _forceIncludesFocus = value; - PropertyChanged?.Invoke(this, s_forceIncludesFocusChangeArgs); + return; } + + _isReadOnly = value; + PropertyChanged?.Invoke(this, s_isReadOnlyChangeArgs); + _parent.NotifyPairChanged(); } + } - public bool ForceAliasFocus + public bool ForceIncludesFocus + { + get => _forceIncludesFocus; + set { - get => _forceAliasFocus; - set - { - _forceAliasFocus = value; - PropertyChanged?.Invoke(this, s_forceAliasFocusChangeArgs); - } + _forceIncludesFocus = value; + PropertyChanged?.Invoke(this, s_forceIncludesFocusChangeArgs); } + } - public bool ForceIsStaticFocus + public bool ForceAliasFocus + { + get => _forceAliasFocus; + set { - get => _forceIsStaticFocus; - set - { - _forceIsStaticFocus = value; - PropertyChanged?.Invoke(this, s_forceIsStaticFocusChangeArgs); - } + _forceAliasFocus = value; + PropertyChanged?.Invoke(this, s_forceAliasFocusChangeArgs); } + } - public ImplicitUsing ToImplicitUsing() + public bool ForceIsStaticFocus + { + get => _forceIsStaticFocus; + set { - return new ImplicitUsing( - Include, - IsStatic ? null : string.IsNullOrWhiteSpace(Alias) ? null : Alias, - IsStatic, - IsReadOnly); + _forceIsStaticFocus = value; + PropertyChanged?.Invoke(this, s_forceIsStaticFocusChangeArgs); } } + + public ImplicitUsing ToImplicitUsing() + { + return new ImplicitUsing( + Include, + IsStatic ? null : string.IsNullOrWhiteSpace(Alias) ? null : Alias, + IsStatic, + IsReadOnly); + } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/PropertyEditorBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/PropertyEditorBase.cs index d4133ae9fd..1f91e3407b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/PropertyEditorBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/Editors/PropertyEditorBase.cs @@ -5,123 +5,122 @@ using Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages.Designer; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Implementation.PropertyPages.Designer -{ +namespace Microsoft.VisualStudio.ProjectSystem.VS.Implementation.PropertyPages.Designer; - /// - /// Base class for property editors that ship with the Project Properties UI. - /// - internal abstract class PropertyEditorBase : IPropertyEditor - { - private static readonly Lazy s_lazyResources; - private static readonly Lazy s_lazyGenericPropertyTemplate; - // DataTemplates must be sourced on the UI thread. We use lazy access here - // as they will be queried via WPF binding on the UI thread. This allows - // us to construct the editor on a worker thread. +/// +/// Base class for property editors that ship with the Project Properties UI. +/// +internal abstract class PropertyEditorBase : IPropertyEditor +{ + private static readonly Lazy s_lazyResources; + private static readonly Lazy s_lazyGenericPropertyTemplate; - private readonly Lazy? _lazyPropertyDataTemplate; - private readonly Lazy? _lazyUnconfiguredDataTemplate; - private readonly Lazy? _lazyConfiguredDataTemplate; + // DataTemplates must be sourced on the UI thread. We use lazy access here + // as they will be queried via WPF binding on the UI thread. This allows + // us to construct the editor on a worker thread. - static PropertyEditorBase() - { - s_lazyResources = new Lazy( - () => - { - if (!UriParser.IsKnownScheme("pack")) - { - UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), "pack", defaultPort: -1); - } - - return new ResourceDictionary - { - Source = new Uri("pack://application:,,,/Microsoft.VisualStudio.ProjectSystem.Managed.VS;component/ProjectSystem/VS/PropertyPages/Editors/PropertyEditorTemplates.xaml", UriKind.RelativeOrAbsolute) - }; - }, - LazyThreadSafetyMode.ExecutionAndPublication); - - s_lazyGenericPropertyTemplate = new Lazy( - () => - { - var template = (DataTemplate?)s_lazyResources.Value["GenericPropertyTemplate"]; - Assumes.NotNull(template); - return template; - }); - } + private readonly Lazy? _lazyPropertyDataTemplate; + private readonly Lazy? _lazyUnconfiguredDataTemplate; + private readonly Lazy? _lazyConfiguredDataTemplate; - protected PropertyEditorBase(string? unconfiguredDataTemplateName, string? configuredDataTemplateName, string? propertyDataTemplateName = null) - { - if (propertyDataTemplateName is not null) - { - _lazyPropertyDataTemplate = new(() => - { - UIThreadHelper.VerifyOnUIThread(); - var propertyDataTemplate = (DataTemplate?)s_lazyResources.Value[propertyDataTemplateName]; - Assumes.NotNull(propertyDataTemplate); - return propertyDataTemplate; - }); - } - - if (unconfiguredDataTemplateName is not null) + static PropertyEditorBase() + { + s_lazyResources = new Lazy( + () => { - _lazyUnconfiguredDataTemplate = new(() => + if (!UriParser.IsKnownScheme("pack")) { - UIThreadHelper.VerifyOnUIThread(); - return (DataTemplate?)s_lazyResources.Value[unconfiguredDataTemplateName]; - }); - } + UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), "pack", defaultPort: -1); + } - if (configuredDataTemplateName is not null) - { - _lazyConfiguredDataTemplate = new(() => + return new ResourceDictionary { - UIThreadHelper.VerifyOnUIThread(); - return (DataTemplate?)s_lazyResources.Value[configuredDataTemplateName]; - }); - } - } + Source = new Uri("pack://application:,,,/Microsoft.VisualStudio.ProjectSystem.Managed.VS;component/ProjectSystem/VS/PropertyPages/Editors/PropertyEditorTemplates.xaml", UriKind.RelativeOrAbsolute) + }; + }, + LazyThreadSafetyMode.ExecutionAndPublication); - public static DataTemplate GenericPropertyTemplate => s_lazyGenericPropertyTemplate.Value; - - public virtual bool ShowUnevaluatedValue => false; + s_lazyGenericPropertyTemplate = new Lazy( + () => + { + var template = (DataTemplate?)s_lazyResources.Value["GenericPropertyTemplate"]; + Assumes.NotNull(template); + return template; + }); + } - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public DataTemplate? PropertyDataTemplate + protected PropertyEditorBase(string? unconfiguredDataTemplateName, string? configuredDataTemplateName, string? propertyDataTemplateName = null) + { + if (propertyDataTemplateName is not null) { - get + _lazyPropertyDataTemplate = new(() => { UIThreadHelper.VerifyOnUIThread(); - return _lazyPropertyDataTemplate?.Value; - } + var propertyDataTemplate = (DataTemplate?)s_lazyResources.Value[propertyDataTemplateName]; + Assumes.NotNull(propertyDataTemplate); + return propertyDataTemplate; + }); } - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public DataTemplate? UnconfiguredDataTemplate + if (unconfiguredDataTemplateName is not null) { - get + _lazyUnconfiguredDataTemplate = new(() => { UIThreadHelper.VerifyOnUIThread(); - return _lazyUnconfiguredDataTemplate?.Value; - } + return (DataTemplate?)s_lazyResources.Value[unconfiguredDataTemplateName]; + }); } - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public DataTemplate? ConfiguredDataTemplate + if (configuredDataTemplateName is not null) { - get + _lazyConfiguredDataTemplate = new(() => { UIThreadHelper.VerifyOnUIThread(); - return _lazyConfiguredDataTemplate?.Value; - } + return (DataTemplate?)s_lazyResources.Value[configuredDataTemplateName]; + }); } + } - public abstract object? DefaultValue { get; } + public static DataTemplate GenericPropertyTemplate => s_lazyGenericPropertyTemplate.Value; - public virtual bool IsPseudoProperty => false; + public virtual bool ShowUnevaluatedValue => false; - public virtual bool ShouldShowDescription(int valueCount) => true; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public DataTemplate? PropertyDataTemplate + { + get + { + UIThreadHelper.VerifyOnUIThread(); + return _lazyPropertyDataTemplate?.Value; + } + } - public abstract bool IsChangedByEvaluation(string unevaluatedValue, object? evaluatedValue, ImmutableDictionary editorMetadata); + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public DataTemplate? UnconfiguredDataTemplate + { + get + { + UIThreadHelper.VerifyOnUIThread(); + return _lazyUnconfiguredDataTemplate?.Value; + } } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public DataTemplate? ConfiguredDataTemplate + { + get + { + UIThreadHelper.VerifyOnUIThread(); + return _lazyConfiguredDataTemplate?.Value; + } + } + + public abstract object? DefaultValue { get; } + + public virtual bool IsPseudoProperty => false; + + public virtual bool ShouldShowDescription(int valueCount) => true; + + public abstract bool IsChangedByEvaluation(string unevaluatedValue, object? evaluatedValue, ImmutableDictionary editorMetadata); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/GetProfileNameDialog.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/GetProfileNameDialog.xaml.cs index 5a1387e203..5e3afab4d4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/GetProfileNameDialog.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/GetProfileNameDialog.xaml.cs @@ -6,77 +6,76 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +[ExcludeFromCodeCoverage] +public partial class GetProfileNameDialog : DialogWindow { - [ExcludeFromCodeCoverage] - public partial class GetProfileNameDialog : DialogWindow + private readonly Predicate _validator; + private readonly SVsServiceProvider _serviceProvider; + private readonly IProjectThreadingService _threadingService; + + public string ProfileName { get; set; } + + public GetProfileNameDialog(SVsServiceProvider sp, IProjectThreadingService threadingService, string suggestedName, Predicate validator) + : base()// Pass help topic to base if there is one + { + InitializeComponent(); + DataContext = this; + ProfileName = suggestedName; + _validator = validator; + _serviceProvider = sp; + _threadingService = threadingService; + } + + /// + /// Validate the name is valid + /// + private void OKButton_Click(object sender, RoutedEventArgs e) { - private readonly Predicate _validator; - private readonly SVsServiceProvider _serviceProvider; - private readonly IProjectThreadingService _threadingService; + string? newName = ProfileName?.Trim(); - public string ProfileName { get; set; } + var notifyService = new UserNotificationServices(_serviceProvider, _threadingService); - public GetProfileNameDialog(SVsServiceProvider sp, IProjectThreadingService threadingService, string suggestedName, Predicate validator) - : base()// Pass help topic to base if there is one + if (Strings.IsNullOrEmpty(newName)) { - InitializeComponent(); - DataContext = this; - ProfileName = suggestedName; - _validator = validator; - _serviceProvider = sp; - _threadingService = threadingService; + notifyService.ShowError(PropertyPageResources.ProfileNameRequired); } - - /// - /// Validate the name is valid - /// - private void OKButton_Click(object sender, RoutedEventArgs e) + else if (!_validator(newName)) { - string? newName = ProfileName?.Trim(); - - var notifyService = new UserNotificationServices(_serviceProvider, _threadingService); - - if (Strings.IsNullOrEmpty(newName)) - { - notifyService.ShowError(PropertyPageResources.ProfileNameRequired); - } - else if (!_validator(newName)) - { - notifyService.ShowError(PropertyPageResources.ProfileNameInvalid); - } - else - { - ProfileName = newName; - DialogResult = true; - } + notifyService.ShowError(PropertyPageResources.ProfileNameInvalid); + } + else + { + ProfileName = newName; + DialogResult = true; } + } - /// - /// Returns the name of the current product we are instantiated in from the appropriate resource - /// Used for dialog title binding - /// - public static string DialogCaption => PropertyPageResources.NewProfileCaption; + /// + /// Returns the name of the current product we are instantiated in from the appropriate resource + /// Used for dialog title binding + /// + public static string DialogCaption => PropertyPageResources.NewProfileCaption; - /// - /// Called when window loads. Use it to set focus on the text box correctly. - /// - private void GetProfileNameDialogWindow_Loaded(object sender, RoutedEventArgs e) - { - // We need to schedule this to occur later after databinding has completed, otherwise - // focus appears in the textbox, but at the start of the suggested name rather than at - // the end. + /// + /// Called when window loads. Use it to set focus on the text box correctly. + /// + private void GetProfileNameDialogWindow_Loaded(object sender, RoutedEventArgs e) + { + // We need to schedule this to occur later after databinding has completed, otherwise + // focus appears in the textbox, but at the start of the suggested name rather than at + // the end. - JoinableTaskFactory jtfWithPriority = _threadingService.JoinableTaskFactory - .WithPriority(Dispatcher, System.Windows.Threading.DispatcherPriority.DataBind); + JoinableTaskFactory jtfWithPriority = _threadingService.JoinableTaskFactory + .WithPriority(Dispatcher, System.Windows.Threading.DispatcherPriority.DataBind); - _ = jtfWithPriority.RunAsync( - () => - { - ProfileNameTextBox.Select(0, ProfileNameTextBox.Text.Length); - ProfileNameTextBox.Focus(); - return Task.CompletedTask; - }); - } + _ = jtfWithPriority.RunAsync( + () => + { + ProfileNameTextBox.Select(0, ProfileNameTextBox.Text.Length); + ProfileNameTextBox.Focus(); + return Task.CompletedTask; + }); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/NoAuthRemoteAuthenticationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/NoAuthRemoteAuthenticationProvider.cs index 4851b96424..94024d3a20 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/NoAuthRemoteAuthenticationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/NoAuthRemoteAuthenticationProvider.cs @@ -2,29 +2,28 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +/// +/// Implementation of IRemoteAuthenticationProvider for the "No Auth" authentication type. +/// +[Export(typeof(IRemoteAuthenticationProvider))] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +[Order(10)] +internal class NoAuthRemoteAuthenticationProvider : IRemoteAuthenticationProvider { - /// - /// Implementation of IRemoteAuthenticationProvider for the "No Auth" authentication type. - /// - [Export(typeof(IRemoteAuthenticationProvider))] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - [Order(10)] - internal class NoAuthRemoteAuthenticationProvider : IRemoteAuthenticationProvider + [ImportingConstructor] + public NoAuthRemoteAuthenticationProvider() { - [ImportingConstructor] - public NoAuthRemoteAuthenticationProvider() - { - } + } - public string DisplayName => Resources.NoAuth; + public string DisplayName => Resources.NoAuth; - public string Name => "None"; + public string Name => "None"; - public Guid PortSupplierGuid => VSConstants.DebugPortSupplierGuids.NoAuth_guid; + public Guid PortSupplierGuid => VSConstants.DebugPortSupplierGuids.NoAuth_guid; - public Guid AuthenticationModeGuid => VSConstants.DebugPortSupplierGuids.NoAuth_guid; + public Guid AuthenticationModeGuid => VSConstants.DebugPortSupplierGuids.NoAuth_guid; - public uint AdditionalRemoteDiscoveryDialogFlags => 0; - } + public uint AdditionalRemoteDiscoveryDialogFlags => 0; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/ProfileCommandNames.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/ProfileCommandNames.cs index af0f45bce7..63f33c06b6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/ProfileCommandNames.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/ProfileCommandNames.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +public static class ProfileCommandNames { - public static class ProfileCommandNames - { - public const string Project = "Project"; - public const string IISExpress = "IISExpress"; - public const string Executable = "Executable"; - public const string NoAction = "NoAction"; - } + public const string Project = "Project"; + public const string IISExpress = "IISExpress"; + public const string Executable = "Executable"; + public const string NoAction = "NoAction"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/PropertyPage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/PropertyPage.cs index beec651ef5..0962a91886 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/PropertyPage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/PropertyPage.cs @@ -7,308 +7,307 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +public abstract partial class PropertyPage : UserControl, IPropertyPage, IVsDebuggerEvents { - public abstract partial class PropertyPage : UserControl, IPropertyPage, IVsDebuggerEvents - { - private IPropertyPageSite? _site; - private bool _isDirty; - private IVsDebugger? _debugger; - private uint _debuggerCookie; - private bool _isActivated; + private IPropertyPageSite? _site; + private bool _isDirty; + private IVsDebugger? _debugger; + private uint _debuggerCookie; + private bool _isActivated; - // WIN32 Constants - private const int SW_HIDE = 0; + // WIN32 Constants + private const int SW_HIDE = 0; - protected abstract string PropertyPageName { get; } + protected abstract string PropertyPageName { get; } - internal PropertyPage() - { - AutoScroll = false; - } + internal PropertyPage() + { + AutoScroll = false; + } - internal UnconfiguredProject? UnconfiguredProject { get; set; } + internal UnconfiguredProject? UnconfiguredProject { get; set; } - /// - /// Property. Gets or sets whether the page is dirty. Dirty status is pushed to owner property sheet - /// - protected bool IsDirty + /// + /// Property. Gets or sets whether the page is dirty. Dirty status is pushed to owner property sheet + /// + protected bool IsDirty + { + get { return _isDirty; } + set { - get { return _isDirty; } - set + // Only process real changes + if (value != _isDirty) { - // Only process real changes - if (value != _isDirty) - { - _isDirty = value; + _isDirty = value; - // If dirty, this causes Apply to be called - _site?.OnStatusChange((uint)(_isDirty ? PROPPAGESTATUS.PROPPAGESTATUS_DIRTY : PROPPAGESTATUS.PROPPAGESTATUS_CLEAN)); - } + // If dirty, this causes Apply to be called + _site?.OnStatusChange((uint)(_isDirty ? PROPPAGESTATUS.PROPPAGESTATUS_DIRTY : PROPPAGESTATUS.PROPPAGESTATUS_CLEAN)); } } + } - [Obsolete("This property is not used by the project system.")] - public List? ContextObjects => null; + [Obsolete("This property is not used by the project system.")] + public List? ContextObjects => null; - /// - /// IPropertyPage - /// This is called before our form is shown but after SetObjects is called. - /// This is the place from which the form can populate itself using the information available - /// in CPS. - /// - public void Activate(IntPtr hWndParent, RECT[] pRect, int bModal) + /// + /// IPropertyPage + /// This is called before our form is shown but after SetObjects is called. + /// This is the place from which the form can populate itself using the information available + /// in CPS. + /// + public void Activate(IntPtr hWndParent, RECT[] pRect, int bModal) + { + AdviseDebugger(); + SuspendLayout(); + + // Initialization can cause some events to be fired when we change some values + // so we use this flag (_ignoreEvents) to notify IsDirty to ignore + // any changes that happen during initialization + + Control parent = FromHandle(hWndParent); + if (parent is not null) + { // We're hosted in WinForms, make sure we + // set Parent so that we inherit Font & Colors + Parent = parent; + } + else { - AdviseDebugger(); - SuspendLayout(); - - // Initialization can cause some events to be fired when we change some values - // so we use this flag (_ignoreEvents) to notify IsDirty to ignore - // any changes that happen during initialization - - Control parent = FromHandle(hWndParent); - if (parent is not null) - { // We're hosted in WinForms, make sure we - // set Parent so that we inherit Font & Colors - Parent = parent; - } - else - { - Win32Methods.SetParent(Handle, hWndParent); - } - - ResumeLayout(); - _isActivated = true; + Win32Methods.SetParent(Handle, hWndParent); } - /// - /// This is where the information entered in the form should be saved in CPS - /// - public int Apply() - { - Assumes.NotNull(UnconfiguredProject); + ResumeLayout(); + _isActivated = true; + } - return UnconfiguredProject.Services.ThreadingPolicy.ExecuteSynchronously(OnApplyAsync); - } + /// + /// This is where the information entered in the form should be saved in CPS + /// + public int Apply() + { + Assumes.NotNull(UnconfiguredProject); - /// - /// Called when the page is deactivated - /// - public void Deactivate() - { - if (_isActivated) - { - Assumes.NotNull(UnconfiguredProject); + return UnconfiguredProject.Services.ThreadingPolicy.ExecuteSynchronously(OnApplyAsync); + } - UnconfiguredProject.Services.ThreadingPolicy.ExecuteSynchronously(OnDeactivateAsync); - UnadviseDebugger(); - } + /// + /// Called when the page is deactivated + /// + public void Deactivate() + { + if (_isActivated) + { + Assumes.NotNull(UnconfiguredProject); - _isActivated = false; - Dispose(true); + UnconfiguredProject.Services.ThreadingPolicy.ExecuteSynchronously(OnDeactivateAsync); + UnadviseDebugger(); } - /// - /// Returns a struct describing our property page - /// - public void GetPageInfo(PROPPAGEINFO[] pPageInfo) + _isActivated = false; + Dispose(true); + } + + /// + /// Returns a struct describing our property page + /// + public void GetPageInfo(PROPPAGEINFO[] pPageInfo) + { + if (pPageInfo?.Length > 0) { - if (pPageInfo?.Length > 0) + pPageInfo[0] = new PROPPAGEINFO { - pPageInfo[0] = new PROPPAGEINFO - { - cb = (uint)Marshal.SizeOf(typeof(PROPPAGEINFO)), - dwHelpContext = 0, - pszDocString = null, - pszHelpFile = null, - pszTitle = PropertyPageName, - // set the size to 0 so the host doesn't use scroll bars - // we want to do that within our own container. - SIZE = { cx = 0, cy = 0 } - }; - } + cb = (uint)Marshal.SizeOf(typeof(PROPPAGEINFO)), + dwHelpContext = 0, + pszDocString = null, + pszHelpFile = null, + pszTitle = PropertyPageName, + // set the size to 0 so the host doesn't use scroll bars + // we want to do that within our own container. + SIZE = { cx = 0, cy = 0 } + }; } + } - /// - /// Returns the help context - /// - public void Help(string pszHelpDir) - { - return; - } + /// + /// Returns the help context + /// + public void Help(string pszHelpDir) + { + return; + } - public int IsPageDirty() - { - if (IsDirty) - return HResult.OK; + public int IsPageDirty() + { + if (IsDirty) + return HResult.OK; - return HResult.False; - } + return HResult.False; + } - /// - /// IPropertyPage - /// Called when the page is moved or sized - /// - public new void Move(RECT[] pRect) - { - if (pRect is null || pRect.Length <= 0) - throw new ArgumentNullException(nameof(pRect)); + /// + /// IPropertyPage + /// Called when the page is moved or sized + /// + public new void Move(RECT[] pRect) + { + if (pRect is null || pRect.Length <= 0) + throw new ArgumentNullException(nameof(pRect)); - RECT r = pRect[0]; + RECT r = pRect[0]; - Location = new Point(r.left, r.top); - Size = new Size(r.right - r.left, r.bottom - r.top); - } + Location = new Point(r.left, r.top); + Size = new Size(r.right - r.left, r.bottom - r.top); + } - /// - /// Notification that debug mode changed - /// - public int OnModeChange(DBGMODE dbgmodeNew) - { - Enabled = (dbgmodeNew == DBGMODE.DBGMODE_Design); - return HResult.OK; - } + /// + /// Notification that debug mode changed + /// + public int OnModeChange(DBGMODE dbgmodeNew) + { + Enabled = (dbgmodeNew == DBGMODE.DBGMODE_Design); + return HResult.OK; + } - /// - /// Informs derived classes that configuration has changed - /// - internal void SetObjects(bool isClosing) - { - Assumes.NotNull(UnconfiguredProject); + /// + /// Informs derived classes that configuration has changed + /// + internal void SetObjects(bool isClosing) + { + Assumes.NotNull(UnconfiguredProject); - UnconfiguredProject.Services.ThreadingPolicy.ExecuteSynchronously(() => OnSetObjectsAsync(isClosing)); - } + UnconfiguredProject.Services.ThreadingPolicy.ExecuteSynchronously(() => OnSetObjectsAsync(isClosing)); + } - /// - /// IPropertyPage - /// Site for our page - /// - public void SetPageSite(IPropertyPageSite pPageSite) - { - _site = pPageSite; - } + /// + /// IPropertyPage + /// Site for our page + /// + public void SetPageSite(IPropertyPageSite pPageSite) + { + _site = pPageSite; + } - /// - /// IPropertyPage - /// Show/Hide the page - /// - public void Show(uint nCmdShow) + /// + /// IPropertyPage + /// Show/Hide the page + /// + public void Show(uint nCmdShow) + { + if (nCmdShow != SW_HIDE) { - if (nCmdShow != SW_HIDE) - { - Show(); - } - else - { - Hide(); - } + Show(); } - - /// - /// IPropertyPage - /// Handles mnemonics - /// - public int TranslateAccelerator(MSG[] pMsg) + else { - if (pMsg is null) - return VSConstants.E_POINTER; + Hide(); + } + } - var m = Message.Create(pMsg[0].hwnd, (int)pMsg[0].message, pMsg[0].wParam, pMsg[0].lParam); - bool used = false; + /// + /// IPropertyPage + /// Handles mnemonics + /// + public int TranslateAccelerator(MSG[] pMsg) + { + if (pMsg is null) + return VSConstants.E_POINTER; - // Preprocessing should be passed to the control whose handle the message refers to. - Control target = FromChildHandle(m.HWnd); - if (target is not null) - used = target.PreProcessMessage(ref m); + var m = Message.Create(pMsg[0].hwnd, (int)pMsg[0].message, pMsg[0].wParam, pMsg[0].lParam); + bool used = false; - if (used) - { - pMsg[0].message = (uint)m.Msg; - pMsg[0].wParam = m.WParam; - pMsg[0].lParam = m.LParam; - // Returning S_OK (0) indicates we handled the message ourselves - return HResult.OK; - } + // Preprocessing should be passed to the control whose handle the message refers to. + Control target = FromChildHandle(m.HWnd); + if (target is not null) + used = target.PreProcessMessage(ref m); - // Returning S_FALSE (1) indicates we have not handled the message - int result = 0; - if (_site is not null) - result = _site.TranslateAccelerator(pMsg); - return result; + if (used) + { + pMsg[0].message = (uint)m.Msg; + pMsg[0].wParam = m.WParam; + pMsg[0].lParam = m.LParam; + // Returning S_OK (0) indicates we handled the message ourselves + return HResult.OK; } - /// - /// Initialize and listen to debug mode changes - /// - internal void AdviseDebugger() + // Returning S_FALSE (1) indicates we have not handled the message + int result = 0; + if (_site is not null) + result = _site.TranslateAccelerator(pMsg); + return result; + } + + /// + /// Initialize and listen to debug mode changes + /// + internal void AdviseDebugger() + { + if (_site is System.IServiceProvider sp) { - if (_site is System.IServiceProvider sp) - { #pragma warning disable RS0030 // Do not used banned APIs - _debugger = sp.GetService(); + _debugger = sp.GetService(); #pragma warning restore RS0030 // Do not used banned APIs - if (_debugger is not null) - { - _debugger.AdviseDebuggerEvents(this, out _debuggerCookie); - var dbgMode = new DBGMODE[1]; - _debugger.GetMode(dbgMode); - ((IVsDebuggerEvents)this).OnModeChange(dbgMode[0]); - } + if (_debugger is not null) + { + _debugger.AdviseDebuggerEvents(this, out _debuggerCookie); + var dbgMode = new DBGMODE[1]; + _debugger.GetMode(dbgMode); + ((IVsDebuggerEvents)this).OnModeChange(dbgMode[0]); } } + } - /// - /// Quit listening to debug mode changes - /// - private void UnadviseDebugger() + /// + /// Quit listening to debug mode changes + /// + private void UnadviseDebugger() + { + if (_debuggerCookie != 0) { - if (_debuggerCookie != 0) - { - _debugger?.UnadviseDebuggerEvents(_debuggerCookie); - } - _debugger = null; - _debuggerCookie = 0; + _debugger?.UnadviseDebuggerEvents(_debuggerCookie); } + _debugger = null; + _debuggerCookie = 0; + } - protected abstract Task OnApplyAsync(); - protected abstract Task OnDeactivateAsync(); - protected abstract Task OnSetObjectsAsync(bool isClosing); + protected abstract Task OnApplyAsync(); + protected abstract Task OnDeactivateAsync(); + protected abstract Task OnSetObjectsAsync(bool isClosing); - public void SetObjects(uint cObjects, object[] ppunk) + public void SetObjects(uint cObjects, object[] ppunk) + { + if (cObjects == 0) { - if (cObjects == 0) + // If we have never configured anything (maybe a failure occurred on open so app designer is closing us). In this case + // do nothing + if (UnconfiguredProject is not null) { - // If we have never configured anything (maybe a failure occurred on open so app designer is closing us). In this case - // do nothing - if (UnconfiguredProject is not null) - { - SetObjects(isClosing: true); - UnconfiguredProject = null; - } - - return; + SetObjects(isClosing: true); + UnconfiguredProject = null; } - UnconfiguredProject = null; + return; + } + + UnconfiguredProject = null; - if (ppunk.Length < cObjects) - throw new ArgumentOutOfRangeException(nameof(cObjects)); + if (ppunk.Length < cObjects) + throw new ArgumentOutOfRangeException(nameof(cObjects)); - // Look for an IVsBrowseObject - for (int i = 0; i < cObjects; ++i) + // Look for an IVsBrowseObject + for (int i = 0; i < cObjects; ++i) + { + if (ppunk[i] is IVsBrowseObject browseObj) { - if (ppunk[i] is IVsBrowseObject browseObj) - { - HResult hr = browseObj.GetProjectItem(out IVsHierarchy hier, out uint itemid); + HResult hr = browseObj.GetProjectItem(out IVsHierarchy hier, out uint itemid); - if (hr.IsOK && itemid == VSConstants.VSITEMID_ROOT) - { - UnconfiguredProject = hier.AsUnconfiguredProject(); - } + if (hr.IsOK && itemid == VSConstants.VSITEMID_ROOT) + { + UnconfiguredProject = hier.AsUnconfiguredProject(); } } - - _ = OnSetObjectsAsync(isClosing: false); } + + _ = OnSetObjectsAsync(isClosing: false); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/UniversalRemoteAuthenticationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/UniversalRemoteAuthenticationProvider.cs index f1ef3b63f5..8bef4b6e71 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/UniversalRemoteAuthenticationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/UniversalRemoteAuthenticationProvider.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +/// +/// Implementation of IRemoteAuthenticationProvider for the "Universal" authentication type. +/// +[Export(typeof(IRemoteAuthenticationProvider))] +[AppliesTo(ProjectCapability.SupportUniversalAuthentication)] +[Order(30)] +internal class UniversalRemoteAuthenticationProvider : IRemoteAuthenticationProvider { - /// - /// Implementation of IRemoteAuthenticationProvider for the "Universal" authentication type. - /// - [Export(typeof(IRemoteAuthenticationProvider))] - [AppliesTo(ProjectCapability.SupportUniversalAuthentication)] - [Order(30)] - internal class UniversalRemoteAuthenticationProvider : IRemoteAuthenticationProvider - { - private static readonly Guid s_universalPortSupplier = new("EE56E4E8-E866-4915-A18E-1DE7114BD7BB"); + private static readonly Guid s_universalPortSupplier = new("EE56E4E8-E866-4915-A18E-1DE7114BD7BB"); - [ImportingConstructor] - public UniversalRemoteAuthenticationProvider() - { - } + [ImportingConstructor] + public UniversalRemoteAuthenticationProvider() + { + } - public string DisplayName => "Universal"; // TODO: Does this need to be localized? + public string DisplayName => "Universal"; // TODO: Does this need to be localized? - public string Name => "Universal"; + public string Name => "Universal"; - public Guid PortSupplierGuid => VSConstants.DebugPortSupplierGuids.NoAuth_guid; + public Guid PortSupplierGuid => VSConstants.DebugPortSupplierGuids.NoAuth_guid; - public Guid AuthenticationModeGuid => s_universalPortSupplier; + public Guid AuthenticationModeGuid => s_universalPortSupplier; - public uint AdditionalRemoteDiscoveryDialogFlags => /* DEBUG_REMOTE_DISCOVERY_FLAGS2.DRD_SUPPORTS_UNIVERSAL_AUTH */ 2; - } + public uint AdditionalRemoteDiscoveryDialogFlags => /* DEBUG_REMOTE_DISCOVERY_FLAGS2.DRD_SUPPORTS_UNIVERSAL_AUTH */ 2; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/WindowsRemoteAuthenticationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/WindowsRemoteAuthenticationProvider.cs index f1f8472e88..87a89a14f0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/WindowsRemoteAuthenticationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/PropertyPages/WindowsRemoteAuthenticationProvider.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +/// +/// Implementation of IRemoteAuthenticationProvider for the Windows authentication type. +/// +[Export(typeof(IRemoteAuthenticationProvider))] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +[Order(2)] +internal class WindowsRemoteAuthenticationProvider : IRemoteAuthenticationProvider { - /// - /// Implementation of IRemoteAuthenticationProvider for the Windows authentication type. - /// - [Export(typeof(IRemoteAuthenticationProvider))] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - [Order(2)] - internal class WindowsRemoteAuthenticationProvider : IRemoteAuthenticationProvider - { - private static readonly Guid s_localPortSupplier = new("708C1ECA-FF48-11D2-904F-00C04FA302A1"); + private static readonly Guid s_localPortSupplier = new("708C1ECA-FF48-11D2-904F-00C04FA302A1"); - [ImportingConstructor] - public WindowsRemoteAuthenticationProvider() - { - } + [ImportingConstructor] + public WindowsRemoteAuthenticationProvider() + { + } - public string DisplayName => Resources.WindowsAuth; + public string DisplayName => Resources.WindowsAuth; - public string Name => "Windows"; + public string Name => "Windows"; - public Guid PortSupplierGuid => s_localPortSupplier; + public Guid PortSupplierGuid => s_localPortSupplier; - public Guid AuthenticationModeGuid => Guid.Empty; + public Guid AuthenticationModeGuid => Guid.Empty; - public uint AdditionalRemoteDiscoveryDialogFlags => 0; - } + public uint AdditionalRemoteDiscoveryDialogFlags => 0; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/AddLaunchProfileAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/AddLaunchProfileAction.cs index db52d16584..7d18abf71e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/AddLaunchProfileAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/AddLaunchProfileAction.cs @@ -4,28 +4,27 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// handling actions. +/// +internal sealed class AddLaunchProfileAction : LaunchProfileActionBase { - /// - /// handling actions. - /// - internal sealed class AddLaunchProfileAction : LaunchProfileActionBase + private readonly AddLaunchProfile _executableStep; + + public AddLaunchProfileAction(AddLaunchProfile executableStep) { - private readonly AddLaunchProfile _executableStep; + _executableStep = executableStep; + } - public AddLaunchProfileAction(AddLaunchProfile executableStep) - { - _executableStep = executableStep; - } + protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler handler, CancellationToken cancellationToken) + { + EntityIdentity? newLaunchProfileId = await handler.AddLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.CommandName, _executableStep.NewProfileName, cancellationToken); - protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler handler, CancellationToken cancellationToken) + if (newLaunchProfileId is not null) { - EntityIdentity? newLaunchProfileId = await handler.AddLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.CommandName, _executableStep.NewProfileName, cancellationToken); - - if (newLaunchProfileId is not null) - { - AddedLaunchProfiles.Add((projectEntity, newLaunchProfileId)); - } + AddedLaunchProfiles.Add((projectEntity, newLaunchProfileId)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/DuplicateLaunchProfileAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/DuplicateLaunchProfileAction.cs index 82fd59b13e..f70054f61d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/DuplicateLaunchProfileAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/DuplicateLaunchProfileAction.cs @@ -4,28 +4,27 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// handling actions. +/// +internal sealed class DuplicateLaunchProfileAction : LaunchProfileActionBase { - /// - /// handling actions. - /// - internal sealed class DuplicateLaunchProfileAction : LaunchProfileActionBase + private readonly DuplicateLaunchProfile _executableStep; + + public DuplicateLaunchProfileAction(DuplicateLaunchProfile executableStep) { - private readonly DuplicateLaunchProfile _executableStep; + _executableStep = executableStep; + } - public DuplicateLaunchProfileAction(DuplicateLaunchProfile executableStep) - { - _executableStep = executableStep; - } + protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken) + { + EntityIdentity? newLaunchProfileId = await launchProfileHandler.DuplicateLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.CurrentProfileName, _executableStep.NewProfileName, _executableStep.NewProfileCommandName, cancellationToken); - protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken) + if (newLaunchProfileId is not null) { - EntityIdentity? newLaunchProfileId = await launchProfileHandler.DuplicateLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.CurrentProfileName, _executableStep.NewProfileName, _executableStep.NewProfileCommandName, cancellationToken); - - if (newLaunchProfileId is not null) - { - AddedLaunchProfiles.Add((projectEntity, newLaunchProfileId)); - } + AddedLaunchProfiles.Add((projectEntity, newLaunchProfileId)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ILaunchSettingsVersionPublisher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ILaunchSettingsVersionPublisher.cs index 3a21681a7e..a9be75f1c3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ILaunchSettingsVersionPublisher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ILaunchSettingsVersionPublisher.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Used to communicate version changes from +/// to without establishing a +/// dependency between the two or on Project Query API types. See +/// for a fuller explanation. +/// +internal interface ILaunchSettingsVersionPublisher { - /// - /// Used to communicate version changes from - /// to without establishing a - /// dependency between the two or on Project Query API types. See - /// for a fuller explanation. - /// - internal interface ILaunchSettingsVersionPublisher - { - void UpdateVersions(ImmutableDictionary versions); - } + void UpdateVersions(ImmutableDictionary versions); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs index c36c87a634..d7278bc103 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/IProjectLaunchProfileHandler.cs @@ -4,137 +4,136 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// -level handler for retrieving Project Query API entities for Launch Profiles. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IProjectLaunchProfileHandler { /// - /// -level handler for retrieving Project Query API entities for Launch Profiles. + /// Returns entities representing all launch profiles in the project. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectLaunchProfileHandler - { - /// - /// Returns entities representing all launch profiles in the project. - /// - Task> RetrieveAllLaunchProfileEntitiesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ILaunchProfilePropertiesAvailableStatus requestedProperties); + Task> RetrieveAllLaunchProfileEntitiesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ILaunchProfilePropertiesAvailableStatus requestedProperties); - /// - /// Returns the entity representing the launch profile specified by the given . - /// - Task RetrieveLaunchProfileEntityAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id, ILaunchProfilePropertiesAvailableStatus requestedProperties); + /// + /// Returns the entity representing the launch profile specified by the given . + /// + Task RetrieveLaunchProfileEntityAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id, ILaunchProfilePropertiesAvailableStatus requestedProperties); - /// - /// Adds a new, empty, launch profile. - /// - /// - /// The context of the current query execution. - /// - /// - /// The entity representing the project. - /// - /// - /// The launch command to associate with the new profile. - /// - /// - /// The name to give to the new profile, or to indicate that - /// the service should choose a unique name. - /// - /// - /// A token whose cancellation indicates that the caller no longer is interested in - /// the result. - /// - /// - /// The representing the new , - /// or if the profile could not be created. - /// - /// - /// If is the name of an existing launch profile, - /// the existing profile will be replaced by the new one. - /// - Task AddLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string commandName, string? newProfileName, CancellationToken cancellationToken = default); + /// + /// Adds a new, empty, launch profile. + /// + /// + /// The context of the current query execution. + /// + /// + /// The entity representing the project. + /// + /// + /// The launch command to associate with the new profile. + /// + /// + /// The name to give to the new profile, or to indicate that + /// the service should choose a unique name. + /// + /// + /// A token whose cancellation indicates that the caller no longer is interested in + /// the result. + /// + /// + /// The representing the new , + /// or if the profile could not be created. + /// + /// + /// If is the name of an existing launch profile, + /// the existing profile will be replaced by the new one. + /// + Task AddLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string commandName, string? newProfileName, CancellationToken cancellationToken = default); - /// - /// Duplicates an existing launch profile under a new name and (possibly) a - /// different launch command. - /// - /// - /// The context of the current query execution. - /// - /// - /// The entity representing the project. - /// - /// - /// The name of the existing profile to duplicate. - /// - /// - /// The name to give to the new profile, or to indicate that - /// the service should choose a unique name. - /// - /// - /// The launch command to associate with the new profile, or - /// to indicate the command name should be copied from the existing profile. - /// - /// - /// A token whose cancellation indicates that the caller no longer is interested in - /// the result. - /// - /// - /// The representing the new , - /// or if the profile could not be created. This may occur if - /// the profile specified by cannot be found. - /// - Task DuplicateLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string? newProfileName, string? newProfileCommandName, CancellationToken cancellationToken = default); + /// + /// Duplicates an existing launch profile under a new name and (possibly) a + /// different launch command. + /// + /// + /// The context of the current query execution. + /// + /// + /// The entity representing the project. + /// + /// + /// The name of the existing profile to duplicate. + /// + /// + /// The name to give to the new profile, or to indicate that + /// the service should choose a unique name. + /// + /// + /// The launch command to associate with the new profile, or + /// to indicate the command name should be copied from the existing profile. + /// + /// + /// A token whose cancellation indicates that the caller no longer is interested in + /// the result. + /// + /// + /// The representing the new , + /// or if the profile could not be created. This may occur if + /// the profile specified by cannot be found. + /// + Task DuplicateLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string? newProfileName, string? newProfileCommandName, CancellationToken cancellationToken = default); - /// - /// Removes a launch profile. - /// - /// - /// The context of the current query execution. - /// - /// - /// The entity representing the project. - /// - /// - /// The name of the profile to remove. - /// - /// ' - /// A token whose cancellation indicates that the caller no longer is interested in - /// the result. - /// - /// - /// The representing the removed , - /// or if the profile could not be found. - /// - Task RemoveLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string profileName, CancellationToken cancellationToken = default); + /// + /// Removes a launch profile. + /// + /// + /// The context of the current query execution. + /// + /// + /// The entity representing the project. + /// + /// + /// The name of the profile to remove. + /// + /// ' + /// A token whose cancellation indicates that the caller no longer is interested in + /// the result. + /// + /// + /// The representing the removed , + /// or if the profile could not be found. + /// + Task RemoveLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string profileName, CancellationToken cancellationToken = default); - /// - /// Renames an existing launch profile. - /// - /// - /// The context of the current query execution. - /// - /// - /// The entity representing the project. - /// - /// - /// The name of the profile to rename. - /// - /// - /// The new name to give to the profile. - /// - /// - /// A token whose cancellation indicates that the caller no longer is interested in - /// the result. - /// - /// - /// A tuple consisting of two s with the first - /// representing the profile under its previous name and the second representing the - /// profile under the new name, or if the profile cannot be - /// found. - /// - /// - /// If is the name of an existing launch profile, - /// the existing profile will be replaced by the newly-renamed profile. - /// - Task<(EntityIdentity oldProfileId, EntityIdentity newProfileId)?> RenameLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string newProfileName, CancellationToken cancellationToken = default); - } + /// + /// Renames an existing launch profile. + /// + /// + /// The context of the current query execution. + /// + /// + /// The entity representing the project. + /// + /// + /// The name of the profile to rename. + /// + /// + /// The new name to give to the profile. + /// + /// + /// A token whose cancellation indicates that the caller no longer is interested in + /// the result. + /// + /// + /// A tuple consisting of two s with the first + /// representing the profile under its previous name and the second representing the + /// profile under the new name, or if the profile cannot be + /// found. + /// + /// + /// If is the name of an existing launch profile, + /// the existing profile will be replaced by the newly-renamed profile. + /// + Task<(EntityIdentity oldProfileId, EntityIdentity newProfileId)?> RenameLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string newProfileName, CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionBase.cs index e0f74b1d48..1f63f63145 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionBase.cs @@ -6,234 +6,233 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Framework; using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Base class for s that modify launch settings and launch profiles. +/// +internal abstract class LaunchProfileActionBase : QueryDataProducerBase, IQueryActionExecutor { /// - /// Base class for s that modify launch settings and launch profiles. + /// The set of entity properties to request when we create a new launch profile and + /// need to obtain an entity for it. /// - internal abstract class LaunchProfileActionBase : QueryDataProducerBase, IQueryActionExecutor + private static readonly LaunchProfilePropertiesAvailableStatus s_requestedLaunchProfileProperties; + private static readonly CategoryPropertiesAvailableStatus s_requestedCategoryProperties; + private static readonly UIPropertyPropertiesAvailableStatus s_requestedPropertyProperties; + private static readonly UIPropertyEditorPropertiesAvailableStatus s_requestedEditorProperties; + private static readonly UIEditorMetadataPropertiesAvailableStatus s_requestedEditorMetadataProperties; + private static readonly UIPropertyValuePropertiesAvailableStatus s_requestedValueProperties; + private static readonly ConfigurationDimensionPropertiesAvailableStatus s_requestedConfigurationDimensionProperties; + private static readonly SupportedValuePropertiesAvailableStatus s_requestedSupportedValueProperties; + + protected readonly List<(IEntityValue projectEntity, EntityIdentity)> AddedLaunchProfiles = new(); + protected readonly List<(IEntityValue projectEntity, EntityIdentity)> RemovedLaunchProfiles = new(); + + static LaunchProfileActionBase() { - /// - /// The set of entity properties to request when we create a new launch profile and - /// need to obtain an entity for it. - /// - private static readonly LaunchProfilePropertiesAvailableStatus s_requestedLaunchProfileProperties; - private static readonly CategoryPropertiesAvailableStatus s_requestedCategoryProperties; - private static readonly UIPropertyPropertiesAvailableStatus s_requestedPropertyProperties; - private static readonly UIPropertyEditorPropertiesAvailableStatus s_requestedEditorProperties; - private static readonly UIEditorMetadataPropertiesAvailableStatus s_requestedEditorMetadataProperties; - private static readonly UIPropertyValuePropertiesAvailableStatus s_requestedValueProperties; - private static readonly ConfigurationDimensionPropertiesAvailableStatus s_requestedConfigurationDimensionProperties; - private static readonly SupportedValuePropertiesAvailableStatus s_requestedSupportedValueProperties; - - protected readonly List<(IEntityValue projectEntity, EntityIdentity)> AddedLaunchProfiles = new(); - protected readonly List<(IEntityValue projectEntity, EntityIdentity)> RemovedLaunchProfiles = new(); - - static LaunchProfileActionBase() - { - s_requestedLaunchProfileProperties = new LaunchProfilePropertiesAvailableStatus(); - s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.CategoriesPropertyName); - s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.CommandNamePropertyName); - s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.NamePropertyName); - s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.OrderPropertyName); - s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.PropertiesPropertyName); - s_requestedLaunchProfileProperties.Freeze(); - - s_requestedCategoryProperties = new CategoryPropertiesAvailableStatus(); - s_requestedCategoryProperties.RequireProperty(CategoryType.DisplayNamePropertyName); - s_requestedCategoryProperties.RequireProperty(CategoryType.NamePropertyName); - s_requestedCategoryProperties.RequireProperty(CategoryType.OrderPropertyName); - s_requestedCategoryProperties.Freeze(); - - s_requestedPropertyProperties = new UIPropertyPropertiesAvailableStatus(); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.CategoryNamePropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.ConfigurationIndependentPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.DescriptionPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.DependsOnPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.DisplayNamePropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.HelpUrlPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.IsReadOnlyPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.IsVisiblePropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.NamePropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.OrderPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.SearchTermsPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.TypePropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.VisibilityConditionPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.DimensionVisibilityConditionPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.ConfiguredValueVisibilityConditionPropertyName); - s_requestedPropertyProperties.RequireProperty(UIPropertyType.IsReadOnlyConditionPropertyName); - s_requestedPropertyProperties.Freeze(); - - s_requestedEditorProperties = new UIPropertyEditorPropertiesAvailableStatus(); - s_requestedEditorProperties.RequireProperty(UIPropertyEditorType.MetadataPropertyName); - s_requestedEditorProperties.RequireProperty(UIPropertyEditorType.NamePropertyName); - s_requestedEditorProperties.Freeze(); - - s_requestedEditorMetadataProperties = new UIEditorMetadataPropertiesAvailableStatus(); - s_requestedEditorMetadataProperties.RequireProperty(UIEditorMetadataType.NamePropertyName); - s_requestedEditorMetadataProperties.RequireProperty(UIEditorMetadataType.ValuePropertyName); - s_requestedEditorMetadataProperties.Freeze(); - - s_requestedValueProperties = new UIPropertyValuePropertiesAvailableStatus(); - s_requestedValueProperties.RequireProperty(UIPropertyValueType.ConfigurationDimensionsPropertyName); - s_requestedValueProperties.RequireProperty(UIPropertyValueType.EvaluatedValuePropertyName); - s_requestedValueProperties.RequireProperty(UIPropertyValueType.SupportedValuesPropertyName); - s_requestedValueProperties.RequireProperty(UIPropertyValueType.UnevaluatedValuePropertyName); - s_requestedValueProperties.RequireProperty(UIPropertyValueType.ValueDefinedInContextPropertyName); - s_requestedValueProperties.Freeze(); - - s_requestedSupportedValueProperties = new SupportedValuePropertiesAvailableStatus(); - s_requestedSupportedValueProperties.RequireProperty(SupportedValueType.DisplayNamePropertyName); - s_requestedSupportedValueProperties.RequireProperty(SupportedValueType.ValuePropertyName); - s_requestedSupportedValueProperties.Freeze(); - - s_requestedConfigurationDimensionProperties = new ConfigurationDimensionPropertiesAvailableStatus(); - s_requestedConfigurationDimensionProperties.RequireProperty(ConfigurationDimensionType.NamePropertyName); - s_requestedConfigurationDimensionProperties.RequireProperty(ConfigurationDimensionType.ValuePropertyName); - s_requestedConfigurationDimensionProperties.Freeze(); - } + s_requestedLaunchProfileProperties = new LaunchProfilePropertiesAvailableStatus(); + s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.CategoriesPropertyName); + s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.CommandNamePropertyName); + s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.NamePropertyName); + s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.OrderPropertyName); + s_requestedLaunchProfileProperties.RequireProperty(LaunchProfileType.PropertiesPropertyName); + s_requestedLaunchProfileProperties.Freeze(); + + s_requestedCategoryProperties = new CategoryPropertiesAvailableStatus(); + s_requestedCategoryProperties.RequireProperty(CategoryType.DisplayNamePropertyName); + s_requestedCategoryProperties.RequireProperty(CategoryType.NamePropertyName); + s_requestedCategoryProperties.RequireProperty(CategoryType.OrderPropertyName); + s_requestedCategoryProperties.Freeze(); + + s_requestedPropertyProperties = new UIPropertyPropertiesAvailableStatus(); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.CategoryNamePropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.ConfigurationIndependentPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.DescriptionPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.DependsOnPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.DisplayNamePropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.HelpUrlPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.IsReadOnlyPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.IsVisiblePropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.NamePropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.OrderPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.SearchTermsPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.TypePropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.VisibilityConditionPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.DimensionVisibilityConditionPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.ConfiguredValueVisibilityConditionPropertyName); + s_requestedPropertyProperties.RequireProperty(UIPropertyType.IsReadOnlyConditionPropertyName); + s_requestedPropertyProperties.Freeze(); + + s_requestedEditorProperties = new UIPropertyEditorPropertiesAvailableStatus(); + s_requestedEditorProperties.RequireProperty(UIPropertyEditorType.MetadataPropertyName); + s_requestedEditorProperties.RequireProperty(UIPropertyEditorType.NamePropertyName); + s_requestedEditorProperties.Freeze(); + + s_requestedEditorMetadataProperties = new UIEditorMetadataPropertiesAvailableStatus(); + s_requestedEditorMetadataProperties.RequireProperty(UIEditorMetadataType.NamePropertyName); + s_requestedEditorMetadataProperties.RequireProperty(UIEditorMetadataType.ValuePropertyName); + s_requestedEditorMetadataProperties.Freeze(); + + s_requestedValueProperties = new UIPropertyValuePropertiesAvailableStatus(); + s_requestedValueProperties.RequireProperty(UIPropertyValueType.ConfigurationDimensionsPropertyName); + s_requestedValueProperties.RequireProperty(UIPropertyValueType.EvaluatedValuePropertyName); + s_requestedValueProperties.RequireProperty(UIPropertyValueType.SupportedValuesPropertyName); + s_requestedValueProperties.RequireProperty(UIPropertyValueType.UnevaluatedValuePropertyName); + s_requestedValueProperties.RequireProperty(UIPropertyValueType.ValueDefinedInContextPropertyName); + s_requestedValueProperties.Freeze(); + + s_requestedSupportedValueProperties = new SupportedValuePropertiesAvailableStatus(); + s_requestedSupportedValueProperties.RequireProperty(SupportedValueType.DisplayNamePropertyName); + s_requestedSupportedValueProperties.RequireProperty(SupportedValueType.ValuePropertyName); + s_requestedSupportedValueProperties.Freeze(); + + s_requestedConfigurationDimensionProperties = new ConfigurationDimensionPropertiesAvailableStatus(); + s_requestedConfigurationDimensionProperties.RequireProperty(ConfigurationDimensionType.NamePropertyName); + s_requestedConfigurationDimensionProperties.RequireProperty(ConfigurationDimensionType.ValuePropertyName); + s_requestedConfigurationDimensionProperties.Freeze(); + } - public async Task OnRequestProcessFinishedAsync(IQueryProcessRequest request) + public async Task OnRequestProcessFinishedAsync(IQueryProcessRequest request) + { + foreach (IGrouping group in AddedLaunchProfiles.GroupBy(item => item.projectEntity)) { - foreach (IGrouping group in AddedLaunchProfiles.GroupBy(item => item.projectEntity)) + var projectValue = (IEntityValueFromProvider)group.Key; + if (projectValue.ProviderState is UnconfiguredProject project + && project.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler handler) { - var projectValue = (IEntityValueFromProvider)group.Key; - if (projectValue.ProviderState is UnconfiguredProject project - && project.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler handler) + var returnedLaunchProfiles = new List(); + if (projectValue.TryGetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, out IEnumerable? exitingProfiles)) { - var returnedLaunchProfiles = new List(); - if (projectValue.TryGetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, out IEnumerable? exitingProfiles)) - { - returnedLaunchProfiles.AddRange(exitingProfiles); - } + returnedLaunchProfiles.AddRange(exitingProfiles); + } - foreach ((IEntityValue projectEntity, EntityIdentity addedProfileId) in group) + foreach ((IEntityValue projectEntity, EntityIdentity addedProfileId) in group) + { + if (await handler.RetrieveLaunchProfileEntityAsync(request.QueryExecutionContext, addedProfileId, s_requestedLaunchProfileProperties) is IEntityValue launchProfileEntity) { - if (await handler.RetrieveLaunchProfileEntityAsync(request.QueryExecutionContext, addedProfileId, s_requestedLaunchProfileProperties) is IEntityValue launchProfileEntity) + if (launchProfileEntity is IEntityValueFromProvider { ProviderState: ContextAndRuleProviderState state }) { - if (launchProfileEntity is IEntityValueFromProvider { ProviderState: ContextAndRuleProviderState state }) - { - // This is a bit of a hack. We can safely assume that we should update the query - // with the entity for the new launch profile. However, there is no way for us to - // know which properties or child entities are desired. Here we make the somewhat - // arbitrary decision to include the categories and properties, but not the property - // values. We already requested the non-entity properties when creating the entity - // for the launch profile. - - // Add categories to the profile - ImmutableArray categories = ImmutableArray.CreateRange( - CategoryDataProducer.CreateCategoryValues(request.QueryExecutionContext, launchProfileEntity, state.Rule, s_requestedCategoryProperties)); - launchProfileEntity.SetRelatedEntities(LaunchProfileType.CategoriesPropertyName, categories); - - // Add properties to the profile - ImmutableArray properties = ImmutableArray.CreateRange( - UIPropertyDataProducer.CreateUIPropertyValues(request.QueryExecutionContext, launchProfileEntity, state.ProjectState, state.PropertiesContext, state.Rule, s_requestedPropertyProperties)); - launchProfileEntity.SetRelatedEntities(LaunchProfileType.PropertiesPropertyName, properties); - - await PopulateEditorsAndValuesAsync(properties); - } - - returnedLaunchProfiles.Add(launchProfileEntity); + // This is a bit of a hack. We can safely assume that we should update the query + // with the entity for the new launch profile. However, there is no way for us to + // know which properties or child entities are desired. Here we make the somewhat + // arbitrary decision to include the categories and properties, but not the property + // values. We already requested the non-entity properties when creating the entity + // for the launch profile. + + // Add categories to the profile + ImmutableArray categories = ImmutableArray.CreateRange( + CategoryDataProducer.CreateCategoryValues(request.QueryExecutionContext, launchProfileEntity, state.Rule, s_requestedCategoryProperties)); + launchProfileEntity.SetRelatedEntities(LaunchProfileType.CategoriesPropertyName, categories); + + // Add properties to the profile + ImmutableArray properties = ImmutableArray.CreateRange( + UIPropertyDataProducer.CreateUIPropertyValues(request.QueryExecutionContext, launchProfileEntity, state.ProjectState, state.PropertiesContext, state.Rule, s_requestedPropertyProperties)); + launchProfileEntity.SetRelatedEntities(LaunchProfileType.PropertiesPropertyName, properties); + + await PopulateEditorsAndValuesAsync(properties); } - } - projectValue.SetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, returnedLaunchProfiles); + returnedLaunchProfiles.Add(launchProfileEntity); + } } + + projectValue.SetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, returnedLaunchProfiles); } + } - foreach (IGrouping group in RemovedLaunchProfiles.GroupBy(item => item.projectEntity)) - { - var projectValue = (IEntityValueFromProvider)group.Key; + foreach (IGrouping group in RemovedLaunchProfiles.GroupBy(item => item.projectEntity)) + { + var projectValue = (IEntityValueFromProvider)group.Key; - var returnedLaunchProfiles = new List(); - if (projectValue.TryGetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, out IEnumerable? exitingProfiles)) - { - returnedLaunchProfiles.AddRange(exitingProfiles); - } + var returnedLaunchProfiles = new List(); + if (projectValue.TryGetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, out IEnumerable? exitingProfiles)) + { + returnedLaunchProfiles.AddRange(exitingProfiles); + } - foreach ((IEntityValue projectEntity, EntityIdentity removedProfileId) in group) + foreach ((IEntityValue projectEntity, EntityIdentity removedProfileId) in group) + { + IEntityValue? entityToRemove = returnedLaunchProfiles.FirstOrDefault(entity => ((IEntityWithId)entity).Id.Equals(removedProfileId)); + if (entityToRemove is not null) { - IEntityValue? entityToRemove = returnedLaunchProfiles.FirstOrDefault(entity => ((IEntityWithId)entity).Id.Equals(removedProfileId)); - if (entityToRemove is not null) - { - returnedLaunchProfiles.Remove(entityToRemove); - } + returnedLaunchProfiles.Remove(entityToRemove); } - - projectValue.SetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, returnedLaunchProfiles); } - await ResultReceiver.OnRequestProcessFinishedAsync(request); + projectValue.SetRelatedEntities(ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName, returnedLaunchProfiles); + } + + await ResultReceiver.OnRequestProcessFinishedAsync(request); - static async Task PopulateSupportedValuesAndConfigurationsAsync(ImmutableArray valueEntities) + static async Task PopulateSupportedValuesAndConfigurationsAsync(ImmutableArray valueEntities) + { + foreach (IEntityValue valueEntity in valueEntities) { - foreach (IEntityValue valueEntity in valueEntities) + if (valueEntity is IEntityValueFromProvider { ProviderState: PropertyValueProviderState valueState }) { - if (valueEntity is IEntityValueFromProvider { ProviderState: PropertyValueProviderState valueState }) - { - // Add supported values to values - ImmutableArray supportedValues = ImmutableArray.CreateRange( - await SupportedValueDataProducer.CreateSupportedValuesAsync(valueEntity, valueState.Property, s_requestedSupportedValueProperties)); - valueEntity.SetRelatedEntities(UIPropertyValueType.SupportedValuesPropertyName, supportedValues); - - ImmutableArray configurationDimensions = ImmutableArray.CreateRange( - ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions(valueEntity, valueState.ProjectConfiguration, valueState.Property, s_requestedConfigurationDimensionProperties)); - valueEntity.SetRelatedEntities(UIPropertyValueType.ConfigurationDimensionsPropertyName, configurationDimensions); - } + // Add supported values to values + ImmutableArray supportedValues = ImmutableArray.CreateRange( + await SupportedValueDataProducer.CreateSupportedValuesAsync(valueEntity, valueState.Property, s_requestedSupportedValueProperties)); + valueEntity.SetRelatedEntities(UIPropertyValueType.SupportedValuesPropertyName, supportedValues); + + ImmutableArray configurationDimensions = ImmutableArray.CreateRange( + ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions(valueEntity, valueState.ProjectConfiguration, valueState.Property, s_requestedConfigurationDimensionProperties)); + valueEntity.SetRelatedEntities(UIPropertyValueType.ConfigurationDimensionsPropertyName, configurationDimensions); } } + } - static void PopulateEditorMetadata(ImmutableArray editors) + static void PopulateEditorMetadata(ImmutableArray editors) + { + foreach (IEntityValue editorEntity in editors) { - foreach (IEntityValue editorEntity in editors) + if (editorEntity is IEntityValueFromProvider { ProviderState: ValueEditor editorState }) { - if (editorEntity is IEntityValueFromProvider { ProviderState: ValueEditor editorState }) - { - // Add editor metadata to the editor - ImmutableArray editorMetadata = ImmutableArray.CreateRange( - UIEditorMetadataProducer.CreateMetadataValues(editorEntity, editorState, s_requestedEditorMetadataProperties)); - editorEntity.SetRelatedEntities(UIPropertyEditorType.MetadataPropertyName, editorMetadata); - } + // Add editor metadata to the editor + ImmutableArray editorMetadata = ImmutableArray.CreateRange( + UIEditorMetadataProducer.CreateMetadataValues(editorEntity, editorState, s_requestedEditorMetadataProperties)); + editorEntity.SetRelatedEntities(UIPropertyEditorType.MetadataPropertyName, editorMetadata); } } + } - async Task PopulateEditorsAndValuesAsync(ImmutableArray properties) + async Task PopulateEditorsAndValuesAsync(ImmutableArray properties) + { + foreach (IEntityValue propertyEntity in properties) { - foreach (IEntityValue propertyEntity in properties) + if (propertyEntity is IEntityValueFromProvider { ProviderState: PropertyProviderState propertyProviderState }) { - if (propertyEntity is IEntityValueFromProvider { ProviderState: PropertyProviderState propertyProviderState }) - { - // Add editors to the property - ImmutableArray editors = ImmutableArray.CreateRange( - UIPropertyEditorDataProducer.CreateEditorValues(request.QueryExecutionContext, propertyEntity, propertyProviderState.ContainingRule, propertyProviderState.PropertyName, s_requestedEditorProperties)); - propertyEntity.SetRelatedEntities(UIPropertyType.EditorsPropertyName, editors); + // Add editors to the property + ImmutableArray editors = ImmutableArray.CreateRange( + UIPropertyEditorDataProducer.CreateEditorValues(request.QueryExecutionContext, propertyEntity, propertyProviderState.ContainingRule, propertyProviderState.PropertyName, s_requestedEditorProperties)); + propertyEntity.SetRelatedEntities(UIPropertyType.EditorsPropertyName, editors); - PopulateEditorMetadata(editors); + PopulateEditorMetadata(editors); - // Add values to the property - ImmutableArray values = ImmutableArray.CreateRange( - await UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync(request.QueryExecutionContext, propertyEntity, propertyProviderState.ProjectState, propertyProviderState.ContainingRule, propertyProviderState.PropertiesContext, propertyProviderState.PropertyName, s_requestedValueProperties)); - propertyEntity.SetRelatedEntities(UIPropertyType.ValuesPropertyName, values); + // Add values to the property + ImmutableArray values = ImmutableArray.CreateRange( + await UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync(request.QueryExecutionContext, propertyEntity, propertyProviderState.ProjectState, propertyProviderState.ContainingRule, propertyProviderState.PropertiesContext, propertyProviderState.PropertyName, s_requestedValueProperties)); + propertyEntity.SetRelatedEntities(UIPropertyType.ValuesPropertyName, values); - await PopulateSupportedValuesAndConfigurationsAsync(values); - } + await PopulateSupportedValuesAndConfigurationsAsync(values); } } } + } - public async Task ReceiveResultAsync(QueryProcessResult result) - { - result.Request.QueryExecutionContext.CancellationToken.ThrowIfCancellationRequested(); - - if (((IEntityValueFromProvider)result.Result).ProviderState is UnconfiguredProject project - && project.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler launchSettingsActionService) - { - await ExecuteAsync(result.Request.QueryExecutionContext, result.Result, launchSettingsActionService, result.Request.QueryExecutionContext.CancellationToken); - } + public async Task ReceiveResultAsync(QueryProcessResult result) + { + result.Request.QueryExecutionContext.CancellationToken.ThrowIfCancellationRequested(); - await ResultReceiver.ReceiveResultAsync(result); + if (((IEntityValueFromProvider)result.Result).ProviderState is UnconfiguredProject project + && project.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler launchSettingsActionService) + { + await ExecuteAsync(result.Request.QueryExecutionContext, result.Result, launchSettingsActionService, result.Request.QueryExecutionContext.CancellationToken); } - protected abstract Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken); + await ResultReceiver.ReceiveResultAsync(result); } + + protected abstract Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionProvider.cs index b6dc1ed8ca..524ace7f43 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchProfileActionProvider.cs @@ -7,33 +7,32 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// +/// Handles Project Query API actions that target the . +/// +/// +/// Specifically, this type is responsible for creating the appropriate +/// for a given , and all further processing is handled by that executor. +/// +/// +[QueryDataProvider(LaunchProfileType.TypeName, ProjectModel.ModelName)] +[QueryActionProvider(ProjectModelActionNames.SetLaunchProfilePropertyValue, typeof(SetLaunchProfilePropertyValue))] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryActionProvider))] +internal sealed class LaunchProfileActionProvider : IQueryActionProvider { - /// - /// - /// Handles Project Query API actions that target the . - /// - /// - /// Specifically, this type is responsible for creating the appropriate - /// for a given , and all further processing is handled by that executor. - /// - /// - [QueryDataProvider(LaunchProfileType.TypeName, ProjectModel.ModelName)] - [QueryActionProvider(ProjectModelActionNames.SetLaunchProfilePropertyValue, typeof(SetLaunchProfilePropertyValue))] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryActionProvider))] - internal sealed class LaunchProfileActionProvider : IQueryActionProvider + public IQueryActionExecutor CreateQueryActionDataTransformer(ExecutableStep executableStep) { - public IQueryActionExecutor CreateQueryActionDataTransformer(ExecutableStep executableStep) - { - Requires.NotNull(executableStep); + Requires.NotNull(executableStep); - return executableStep.Action switch - { - ProjectModelActionNames.SetLaunchProfilePropertyValue => new SetLaunchProfilePropertyAction((SetLaunchProfilePropertyValue)executableStep), + return executableStep.Action switch + { + ProjectModelActionNames.SetLaunchProfilePropertyValue => new SetLaunchProfilePropertyAction((SetLaunchProfilePropertyValue)executableStep), - _ => throw new InvalidOperationException($"{nameof(ProjectActionProvider)} does not handle action '{executableStep.Action}'.") - }; - } + _ => throw new InvalidOperationException($"{nameof(ProjectActionProvider)} does not handle action '{executableStep.Action}'.") + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProvider.cs index da5a236425..875e40a989 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProvider.cs @@ -1,80 +1,79 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Aggregates version information from the multiple s +/// and passes it on to the bound , if any. +/// +/// +/// The loads automatically for any project +/// supporting launch profiles. However, we don't want to load all of the assemblies +/// associated with the Project Query API until they are actually needed, so +/// can't refer to any of them directly. Instead, it communicates with this type, +/// which will pass on the version information only after being bound to an . +/// +[Export] +internal sealed class LaunchSettingsQueryVersionProvider { - /// - /// Aggregates version information from the multiple s - /// and passes it on to the bound , if any. - /// - /// - /// The loads automatically for any project - /// supporting launch profiles. However, we don't want to load all of the assemblies - /// associated with the Project Query API until they are actually needed, so - /// can't refer to any of them directly. Instead, it communicates with this type, - /// which will pass on the version information only after being bound to an . - /// - [Export] - internal sealed class LaunchSettingsQueryVersionProvider - { - private readonly object _lock = new(); + private readonly object _lock = new(); - private ImmutableDictionary _versions = ImmutableStringDictionary.EmptyOrdinal; + private ImmutableDictionary _versions = ImmutableStringDictionary.EmptyOrdinal; - private ILaunchSettingsVersionPublisher? _versionPublisher; + private ILaunchSettingsVersionPublisher? _versionPublisher; - /// - /// Sets the related and pushes - /// the current version information to it. - /// - internal void BindToVersionPublisher(ILaunchSettingsVersionPublisher versionPublisher) + /// + /// Sets the related and pushes + /// the current version information to it. + /// + internal void BindToVersionPublisher(ILaunchSettingsVersionPublisher versionPublisher) + { + lock (_lock) { - lock (_lock) - { - _versionPublisher = versionPublisher; - _versionPublisher.UpdateVersions(_versions); - } + _versionPublisher = versionPublisher; + _versionPublisher.UpdateVersions(_versions); } + } - /// - /// Called when the becomes active. - /// - internal void OnLaunchSettingsTrackerActivated(LaunchSettingsTracker tracker) + /// + /// Called when the becomes active. + /// + internal void OnLaunchSettingsTrackerActivated(LaunchSettingsTracker tracker) + { + lock (_lock) { - lock (_lock) - { - string key = tracker.VersionKey; + string key = tracker.VersionKey; - _versions = _versions.SetItem(key, tracker.CurrentVersion); + _versions = _versions.SetItem(key, tracker.CurrentVersion); - _versionPublisher?.UpdateVersions(_versions); - } + _versionPublisher?.UpdateVersions(_versions); } + } - /// - /// Called when the has an updated version. - /// - internal void OnLaunchSettingsVersionUpdated(LaunchSettingsTracker tracker) + /// + /// Called when the has an updated version. + /// + internal void OnLaunchSettingsVersionUpdated(LaunchSettingsTracker tracker) + { + lock (_lock) { - lock (_lock) - { - _versions = _versions.SetItem(tracker.VersionKey, tracker.CurrentVersion); - _versionPublisher?.UpdateVersions(_versions); - } + _versions = _versions.SetItem(tracker.VersionKey, tracker.CurrentVersion); + _versionPublisher?.UpdateVersions(_versions); } + } - /// - /// Called when the is deactivated. - /// - internal void OnLaunchSettingsTrackerDeactivated(LaunchSettingsTracker tracker) + /// + /// Called when the is deactivated. + /// + internal void OnLaunchSettingsTrackerDeactivated(LaunchSettingsTracker tracker) + { + lock (_lock) { - lock (_lock) - { - string key = tracker.VersionKey; + string key = tracker.VersionKey; - _versions = _versions.Remove(key); + _versions = _versions.Remove(key); - _versionPublisher?.UpdateVersions(_versions); - } + _versionPublisher?.UpdateVersions(_versions); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProviderExport.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProviderExport.cs index fdd48554d2..3b9cb7ee25 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProviderExport.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsQueryVersionProviderExport.cs @@ -4,206 +4,205 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Implementation of for launch +/// settings. Receives data from the +/// and passes it on to subscribed observers. +/// +/// +/// We need to be careful that this type is only loaded by the Project Query API. +/// If our code were to load it, then we would cause the load of the Project Query +/// assemblies in situations where they weren't actually loaded. To avoid this, our +/// code has no direct dependency on this type, only on the +/// interface (which is defined in the project system). When this type *is* loaded it +/// registers itself with the . +/// +[Export(typeof(IQueryDataSourceVersionProvider))] +[ExportMetadata("Name", LaunchSettingsQueryVersionGroupName)] +internal class LaunchSettingsQueryVersionProviderExport : OnceInitializedOnceDisposed, IQueryDataSourceVersionProvider, ILaunchSettingsVersionPublisher { + public const string LaunchSettingsQueryVersionGroupName = "LaunchSettings"; + + /// + /// Responsible for generating new version information. + /// + private readonly LaunchSettingsQueryVersionProvider _provider; + /// + /// Processes notifications to the . + /// + private readonly ITargetBlock _processingBlock; /// - /// Implementation of for launch - /// settings. Receives data from the - /// and passes it on to subscribed observers. + /// The set of change notification observers. /// + private ImmutableHashSet> _observers = ImmutableHashSet>.Empty; /// - /// We need to be careful that this type is only loaded by the Project Query API. - /// If our code were to load it, then we would cause the load of the Project Query - /// assemblies in situations where they weren't actually loaded. To avoid this, our - /// code has no direct dependency on this type, only on the - /// interface (which is defined in the project system). When this type *is* loaded it - /// registers itself with the . + /// Used to keep track of whether or not a new subscriber has received its initial snapshot. /// - [Export(typeof(IQueryDataSourceVersionProvider))] - [ExportMetadata("Name", LaunchSettingsQueryVersionGroupName)] - internal class LaunchSettingsQueryVersionProviderExport : OnceInitializedOnceDisposed, IQueryDataSourceVersionProvider, ILaunchSettingsVersionPublisher + private long _processedNotificationCount; + /// + /// The current set of versions. + /// + private QueryDataVersions _versions = QueryDataVersions.Empty; + /// + /// The most recent set of versions sent to subscribers. + /// + private QueryDataVersions _latestPostedVersions = QueryDataVersions.Empty; + + [ImportingConstructor] + public LaunchSettingsQueryVersionProviderExport(LaunchSettingsQueryVersionProvider provider) { - public const string LaunchSettingsQueryVersionGroupName = "LaunchSettings"; - - /// - /// Responsible for generating new version information. - /// - private readonly LaunchSettingsQueryVersionProvider _provider; - /// - /// Processes notifications to the . - /// - private readonly ITargetBlock _processingBlock; - /// - /// The set of change notification observers. - /// - private ImmutableHashSet> _observers = ImmutableHashSet>.Empty; - /// - /// Used to keep track of whether or not a new subscriber has received its initial snapshot. - /// - private long _processedNotificationCount; - /// - /// The current set of versions. - /// - private QueryDataVersions _versions = QueryDataVersions.Empty; - /// - /// The most recent set of versions sent to subscribers. - /// - private QueryDataVersions _latestPostedVersions = QueryDataVersions.Empty; - - [ImportingConstructor] - public LaunchSettingsQueryVersionProviderExport(LaunchSettingsQueryVersionProvider provider) - { - _provider = provider; + _provider = provider; #pragma warning disable RS0030 // Do not used banned APIs - // DataflowBlockFactory.CreateActionBlock(...) would be preferable, but it takes an - // UnconfiguredProject as a parameter. This type isn't associated with any - // particular project so we can't use it. - _processingBlock = DataflowBlockSlim.CreateActionBlock(callback => callback(), nameFormat: nameof(LaunchSettingsQueryVersionProviderExport)); + // DataflowBlockFactory.CreateActionBlock(...) would be preferable, but it takes an + // UnconfiguredProject as a parameter. This type isn't associated with any + // particular project so we can't use it. + _processingBlock = DataflowBlockSlim.CreateActionBlock(callback => callback(), nameFormat: nameof(LaunchSettingsQueryVersionProviderExport)); #pragma warning restore RS0030 // Do not used banned APIs - _provider.BindToVersionPublisher(this); - } + _provider.BindToVersionPublisher(this); + } - public void UpdateVersions(ImmutableDictionary versions) + public void UpdateVersions(ImmutableDictionary versions) + { + lock (SyncObject) { - lock (SyncObject) - { - _versions = QueryDataVersions.Empty.AddRange(versions); + _versions = QueryDataVersions.Empty.AddRange(versions); - PostVersionUpdate(); - } + PostVersionUpdate(); } + } - public IDisposable SubscribeChangeNotifications(IObserver observer) - { - EnsureInitialized(); + public IDisposable SubscribeChangeNotifications(IObserver observer) + { + EnsureInitialized(); - lock (SyncObject) + lock (SyncObject) + { + if (_observers.Contains(observer)) { - if (_observers.Contains(observer)) - { - Requires.Fail($"This observer is already subscribed to change notifications from the {nameof(LaunchSettingsQueryVersionProviderExport)}."); - } + Requires.Fail($"This observer is already subscribed to change notifications from the {nameof(LaunchSettingsQueryVersionProviderExport)}."); + } - _observers = _observers.Add(observer); + _observers = _observers.Add(observer); - // We don't immediately send the current set of versions to the observer. Instead, - // we post a message to send that data later. However, by that point the observer - // may have already received an up-to-date set of versions. To avoid double-sending - // the data we keep track of how many version updates we've already sent. If that - // number hasn't changed when we process the posted message, we know the observer - // has not received any version data yet. - long processedNotificationCountAtTimeOfSubscription = _processedNotificationCount; + // We don't immediately send the current set of versions to the observer. Instead, + // we post a message to send that data later. However, by that point the observer + // may have already received an up-to-date set of versions. To avoid double-sending + // the data we keep track of how many version updates we've already sent. If that + // number hasn't changed when we process the posted message, we know the observer + // has not received any version data yet. + long processedNotificationCountAtTimeOfSubscription = _processedNotificationCount; - _processingBlock.Post(() => + _processingBlock.Post(() => + { + bool observerRequiresNotification = false; + lock (SyncObject) { - bool observerRequiresNotification = false; - lock (SyncObject) - { - observerRequiresNotification = _observers.Contains(observer) && processedNotificationCountAtTimeOfSubscription == _processedNotificationCount; - } - - if (observerRequiresNotification - && _latestPostedVersions != QueryDataVersions.Empty) - { - observer.OnNext(new QueryDataSourceChangeNotification(updates: _latestPostedVersions)); - } - }); - } + observerRequiresNotification = _observers.Contains(observer) && processedNotificationCountAtTimeOfSubscription == _processedNotificationCount; + } - return new Unsubscriber(this, observer); + if (observerRequiresNotification + && _latestPostedVersions != QueryDataVersions.Empty) + { + observer.OnNext(new QueryDataSourceChangeNotification(updates: _latestPostedVersions)); + } + }); } - public bool TryGetVersion(string versionKey, out long version) - { - Requires.NotNullOrEmpty(versionKey); - lock (SyncObject) - { - return _versions.TryGetValue(versionKey, out version); - } - } + return new Unsubscriber(this, observer); + } - protected override void Initialize() + public bool TryGetVersion(string versionKey, out long version) + { + Requires.NotNullOrEmpty(versionKey); + lock (SyncObject) { + return _versions.TryGetValue(versionKey, out version); } + } + + protected override void Initialize() + { + } - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) + { + if (disposing) { - if (disposing) - { - _versions = QueryDataVersions.Empty; - _processingBlock.Complete(); - } + _versions = QueryDataVersions.Empty; + _processingBlock.Complete(); } + } - private void PostVersionUpdate() + private void PostVersionUpdate() + { + lock (SyncObject) { - lock (SyncObject) + if (IsInitialized) { - if (IsInitialized) + _processingBlock.Post(() => { - _processingBlock.Post(() => + ImmutableHashSet> observers; + QueryDataVersions newVersions; + lock (SyncObject) { - ImmutableHashSet> observers; - QueryDataVersions newVersions; - lock (SyncObject) - { - if (_latestPostedVersions == _versions) - { - return; - } - - observers = _observers; - newVersions = _versions; - _processedNotificationCount++; - } - - if (observers.Count == 0) + if (_latestPostedVersions == _versions) { return; } - List? expiredSources = null; - foreach (string versionKey in _latestPostedVersions.Keys) - { - if (!newVersions.ContainsKey(versionKey)) - { - expiredSources ??= new List(); + observers = _observers; + newVersions = _versions; + _processedNotificationCount++; + } - expiredSources.Add(versionKey); - } - } + if (observers.Count == 0) + { + return; + } - _latestPostedVersions = newVersions; - QueryDataSourceChangeNotification notification = new(newVersions, expiredSources?.ToImmutableArray()); - foreach (IObserver observer in observers) + List? expiredSources = null; + foreach (string versionKey in _latestPostedVersions.Keys) + { + if (!newVersions.ContainsKey(versionKey)) { - observer.OnNext(notification); + expiredSources ??= new List(); + + expiredSources.Add(versionKey); } - }); - } + } + + _latestPostedVersions = newVersions; + QueryDataSourceChangeNotification notification = new(newVersions, expiredSources?.ToImmutableArray()); + foreach (IObserver observer in observers) + { + observer.OnNext(notification); + } + }); } } + } - private class Unsubscriber : IDisposable - { - private readonly LaunchSettingsQueryVersionProviderExport _provider; - private readonly IObserver _observer; + private class Unsubscriber : IDisposable + { + private readonly LaunchSettingsQueryVersionProviderExport _provider; + private readonly IObserver _observer; - public Unsubscriber( - LaunchSettingsQueryVersionProviderExport provider, - IObserver observer) - { - _provider = provider; - _observer = observer; - } + public Unsubscriber( + LaunchSettingsQueryVersionProviderExport provider, + IObserver observer) + { + _provider = provider; + _observer = observer; + } - public void Dispose() + public void Dispose() + { + lock (_provider.SyncObject) { - lock (_provider.SyncObject) - { - _provider._observers = _provider._observers.Remove(_observer); - } + _provider._observers = _provider._observers.Remove(_observer); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsTracker.cs index 942d3c7aa8..d4f8fff996 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/LaunchSettingsTracker.cs @@ -2,104 +2,103 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Tracks the launch settings version for a particular project, and reports the version information to the . +/// +/// +/// Note that as an the +/// and may be called multiple times as the project is +/// loaded, unloaded, and as the capabilities change. +/// +[Export(typeof(LaunchSettingsTracker))] +[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal class LaunchSettingsTracker : IProjectDynamicLoadComponent { - /// - /// Tracks the launch settings version for a particular project, and reports the version information to the . - /// /// - /// Note that as an the - /// and may be called multiple times as the project is - /// loaded, unloaded, and as the capabilities change. + /// Ensures each is given a unique version key. /// - [Export(typeof(LaunchSettingsTracker))] - [Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal class LaunchSettingsTracker : IProjectDynamicLoadComponent - { - /// - /// Ensures each is given a unique version key. - /// - private static int s_nextTrackerId; - - private readonly UnconfiguredProject _project; - private readonly ILaunchSettingsProvider _launchSettingsProvider; - private readonly LaunchSettingsQueryVersionProvider _versionProvider; - - private string? _versionKey; - private IDisposable? _launchSettingsProviderLink; - - [ImportingConstructor] - public LaunchSettingsTracker( - UnconfiguredProject project, - ILaunchSettingsProvider launchSettingsProvider, - LaunchSettingsQueryVersionProvider versionProvider) - { - _project = project; - _launchSettingsProvider = launchSettingsProvider; - _versionProvider = versionProvider; - } + private static int s_nextTrackerId; - public Task LoadAsync() - { - _launchSettingsProviderLink = _launchSettingsProvider.SourceBlock.LinkToAction(OnLaunchSettingsChanged, _project); + private readonly UnconfiguredProject _project; + private readonly ILaunchSettingsProvider _launchSettingsProvider; + private readonly LaunchSettingsQueryVersionProvider _versionProvider; - ILaunchSettings? currentSnapshot = _launchSettingsProvider.CurrentSnapshot; - if (currentSnapshot is IVersionedLaunchSettings versionedSettings) - { - CurrentVersion = versionedSettings.Version; - _versionProvider.OnLaunchSettingsTrackerActivated(this); - } + private string? _versionKey; + private IDisposable? _launchSettingsProviderLink; - return Task.CompletedTask; - } + [ImportingConstructor] + public LaunchSettingsTracker( + UnconfiguredProject project, + ILaunchSettingsProvider launchSettingsProvider, + LaunchSettingsQueryVersionProvider versionProvider) + { + _project = project; + _launchSettingsProvider = launchSettingsProvider; + _versionProvider = versionProvider; + } + + public Task LoadAsync() + { + _launchSettingsProviderLink = _launchSettingsProvider.SourceBlock.LinkToAction(OnLaunchSettingsChanged, _project); - public Task UnloadAsync() + ILaunchSettings? currentSnapshot = _launchSettingsProvider.CurrentSnapshot; + if (currentSnapshot is IVersionedLaunchSettings versionedSettings) { - if (_launchSettingsProviderLink is not null) - { - _launchSettingsProviderLink.Dispose(); - _launchSettingsProviderLink = null; - } + CurrentVersion = versionedSettings.Version; + _versionProvider.OnLaunchSettingsTrackerActivated(this); + } - _versionProvider.OnLaunchSettingsTrackerDeactivated(this); + return Task.CompletedTask; + } - return Task.CompletedTask; + public Task UnloadAsync() + { + if (_launchSettingsProviderLink is not null) + { + _launchSettingsProviderLink.Dispose(); + _launchSettingsProviderLink = null; } - private void OnLaunchSettingsChanged(ILaunchSettings newSettings) + _versionProvider.OnLaunchSettingsTrackerDeactivated(this); + + return Task.CompletedTask; + } + + private void OnLaunchSettingsChanged(ILaunchSettings newSettings) + { + if (newSettings is IVersionedLaunchSettings versionedSettings) { - if (newSettings is IVersionedLaunchSettings versionedSettings) - { - CurrentVersion = versionedSettings.Version; - _versionProvider.OnLaunchSettingsVersionUpdated(this); - } + CurrentVersion = versionedSettings.Version; + _versionProvider.OnLaunchSettingsVersionUpdated(this); } + } - public long CurrentVersion { get; private set; } + public long CurrentVersion { get; private set; } - public string VersionKey + public string VersionKey + { + get { - get + if (_versionKey is null) { - if (_versionKey is null) - { - // Get a unique ID number - int trackerIdNumber = Interlocked.Increment(ref s_nextTrackerId); - - // The project name is only for diagnostic purposes. It is not guaranteed to be unique, - // and it is OK that this won't get updated if the project is renamed. - string projectName = Path.GetFileNameWithoutExtension(_project.FullPath); + // Get a unique ID number + int trackerIdNumber = Interlocked.Increment(ref s_nextTrackerId); - // Assemble the version key. - string versionKey = $"LaunchSettings:{trackerIdNumber}:{projectName}"; + // The project name is only for diagnostic purposes. It is not guaranteed to be unique, + // and it is OK that this won't get updated if the project is renamed. + string projectName = Path.GetFileNameWithoutExtension(_project.FullPath); - // Ensure that the field is only set once, even when called from multiple threads. - Interlocked.CompareExchange(location1: ref _versionKey, value: versionKey, comparand: null); - } + // Assemble the version key. + string versionKey = $"LaunchSettings:{trackerIdNumber}:{projectName}"; - return _versionKey!; + // Ensure that the field is only set once, even when called from multiple threads. + Interlocked.CompareExchange(location1: ref _versionKey, value: versionKey, comparand: null); } + + return _versionKey!; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandler.cs index cb13e6d27f..db9f08a4d3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandler.cs @@ -9,141 +9,169 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Providers; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +[Export(typeof(IProjectLaunchProfileHandler))] +internal class ProjectLaunchProfileHandler : IProjectLaunchProfileHandler { - [Export(typeof(IProjectLaunchProfileHandler))] - internal class ProjectLaunchProfileHandler : IProjectLaunchProfileHandler + private static readonly string s_commandNameMetadataName = "CommandName"; + private static readonly string s_launchProfileSourceItemTypeValue = "LaunchProfile"; + + private readonly UnconfiguredProject _project; + private readonly ILaunchSettingsProvider _launchSettingsProvider; + private readonly LaunchSettingsTracker _launchSettingsTracker; + + [ImportingConstructor] + public ProjectLaunchProfileHandler( + UnconfiguredProject project, + ILaunchSettingsProvider launchSettingsProvider, + LaunchSettingsTracker launchSettingsTracker) { - private static readonly string s_commandNameMetadataName = "CommandName"; - private static readonly string s_launchProfileSourceItemTypeValue = "LaunchProfile"; - - private readonly UnconfiguredProject _project; - private readonly ILaunchSettingsProvider _launchSettingsProvider; - private readonly LaunchSettingsTracker _launchSettingsTracker; - - [ImportingConstructor] - public ProjectLaunchProfileHandler( - UnconfiguredProject project, - ILaunchSettingsProvider launchSettingsProvider, - LaunchSettingsTracker launchSettingsTracker) - { - _project = project; - _launchSettingsProvider = launchSettingsProvider; - _launchSettingsTracker = launchSettingsTracker; - } + _project = project; + _launchSettingsProvider = launchSettingsProvider; + _launchSettingsTracker = launchSettingsTracker; + } - public async Task RetrieveLaunchProfileEntityAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id, ILaunchProfilePropertiesAvailableStatus requestedProperties) + public async Task RetrieveLaunchProfileEntityAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id, ILaunchProfilePropertiesAvailableStatus requestedProperties) + { + string desiredProfileName = ValidateIdAndExtractProfileName(id); + + if (await _project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) { - string desiredProfileName = ValidateIdAndExtractProfileName(id); + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - if (await _project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) + if (launchSettings is IVersionedLaunchSettings versionedLaunchSettings) { - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - - if (launchSettings is IVersionedLaunchSettings versionedLaunchSettings) - { - queryExecutionContext.ReportInputDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); + queryExecutionContext.ReportInputDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); - IProjectState projectState = new LaunchProfileProjectState(_project, _launchSettingsProvider, _launchSettingsTracker); + IProjectState projectState = new LaunchProfileProjectState(_project, _launchSettingsProvider, _launchSettingsTracker); - foreach ((int index, ILaunchProfile profile) in launchSettings.Profiles.WithIndices()) + foreach ((int index, ILaunchProfile profile) in launchSettings.Profiles.WithIndices()) + { + if (StringComparers.LaunchProfileNames.Equals(profile.Name, desiredProfileName) + && !Strings.IsNullOrEmpty(profile.CommandName)) { - if (StringComparers.LaunchProfileNames.Equals(profile.Name, desiredProfileName) - && !Strings.IsNullOrEmpty(profile.CommandName)) + foreach (Rule rule in DebugUtilities.GetDebugChildRules(projectCatalog)) { - foreach (Rule rule in DebugUtilities.GetDebugChildRules(projectCatalog)) + if (rule.Metadata.TryGetValue(s_commandNameMetadataName, out object? commandNameObj) + && commandNameObj is string commandName + && StringComparers.LaunchProfileCommandNames.Equals(commandName, profile.CommandName)) { - if (rule.Metadata.TryGetValue(s_commandNameMetadataName, out object? commandNameObj) - && commandNameObj is string commandName - && StringComparers.LaunchProfileCommandNames.Equals(commandName, profile.CommandName)) - { - QueryProjectPropertiesContext propertiesContext = new( - isProjectFile: true, - file: _project.FullPath, - itemType: LaunchProfileProjectItemProvider.ItemType, - itemName: profile.Name); - - IEntityValue launchProfileValue = CreateLaunchProfileValue( - queryExecutionContext, - id, - propertiesContext, - rule, - index, - projectState, - requestedProperties); - return launchProfileValue; - } + QueryProjectPropertiesContext propertiesContext = new( + isProjectFile: true, + file: _project.FullPath, + itemType: LaunchProfileProjectItemProvider.ItemType, + itemName: profile.Name); + + IEntityValue launchProfileValue = CreateLaunchProfileValue( + queryExecutionContext, + id, + propertiesContext, + rule, + index, + projectState, + requestedProperties); + return launchProfileValue; } } } } } - - return null; } - public async Task> RetrieveAllLaunchProfileEntitiesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ILaunchProfilePropertiesAvailableStatus requestedProperties) + return null; + } + + public async Task> RetrieveAllLaunchProfileEntitiesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ILaunchProfilePropertiesAvailableStatus requestedProperties) + { + if (await _project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) { - if (await _project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) - { - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - return createLaunchProfileValues(launchSettings); - } + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); + return createLaunchProfileValues(launchSettings); + } - return Enumerable.Empty(); + return Enumerable.Empty(); - IEnumerable createLaunchProfileValues(ILaunchSettings launchSettings) + IEnumerable createLaunchProfileValues(ILaunchSettings launchSettings) + { + Dictionary debugRules = new(); + foreach (Rule rule in DebugUtilities.GetDebugChildRules(projectCatalog)) { - Dictionary debugRules = new(); - foreach (Rule rule in DebugUtilities.GetDebugChildRules(projectCatalog)) + if (rule.Metadata.TryGetValue(s_commandNameMetadataName, out object? commandNameObj) + && commandNameObj is string commandName) { - if (rule.Metadata.TryGetValue(s_commandNameMetadataName, out object? commandNameObj) - && commandNameObj is string commandName) - { - debugRules[commandName] = rule; - } + debugRules[commandName] = rule; } + } - if (launchSettings is IVersionedLaunchSettings versionedLaunchSettings) - { - queryExecutionContext.ReportInputDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); - } + if (launchSettings is IVersionedLaunchSettings versionedLaunchSettings) + { + queryExecutionContext.ReportInputDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); + } - IProjectState projectState = new LaunchProfileProjectState(_project, _launchSettingsProvider, _launchSettingsTracker); + IProjectState projectState = new LaunchProfileProjectState(_project, _launchSettingsProvider, _launchSettingsTracker); - foreach ((int index, ILaunchProfile profile) in launchSettings.Profiles.WithIndices()) + foreach ((int index, ILaunchProfile profile) in launchSettings.Profiles.WithIndices()) + { + if (!Strings.IsNullOrEmpty(profile.Name) + && !Strings.IsNullOrEmpty(profile.CommandName) + && debugRules.TryGetValue(profile.CommandName, out Rule rule)) { - if (!Strings.IsNullOrEmpty(profile.Name) - && !Strings.IsNullOrEmpty(profile.CommandName) - && debugRules.TryGetValue(profile.CommandName, out Rule rule)) - { - QueryProjectPropertiesContext propertiesContext = new( - isProjectFile: true, - file: _project.FullPath, - itemType: LaunchProfileProjectItemProvider.ItemType, - itemName: profile.Name); - - EntityIdentity id = CreateLaunchProfileId(parent, profile.Name); - IEntityValue launchProfileValue = CreateLaunchProfileValue(queryExecutionContext, id, propertiesContext, rule, index, projectState, requestedProperties); - yield return launchProfileValue; - } + QueryProjectPropertiesContext propertiesContext = new( + isProjectFile: true, + file: _project.FullPath, + itemType: LaunchProfileProjectItemProvider.ItemType, + itemName: profile.Name); + + EntityIdentity id = CreateLaunchProfileId(parent, profile.Name); + IEntityValue launchProfileValue = CreateLaunchProfileValue(queryExecutionContext, id, propertiesContext, rule, index, projectState, requestedProperties); + yield return launchProfileValue; } } } + } + + public async Task AddLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string commandName, string? newProfileName, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + newProfileName ??= await GetNewProfileNameAsync(cancellationToken); + + await _launchSettingsProvider.AddOrUpdateProfileAsync( + new WritableLaunchProfile + { + Name = newProfileName, + CommandName = commandName + }.ToLaunchProfile(), + addToFront: false); - public async Task AddLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string commandName, string? newProfileName, CancellationToken cancellationToken = default) + if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) { - cancellationToken.ThrowIfCancellationRequested(); + queryExecutionContext.ReportUpdatedDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); + } + + return CreateLaunchProfileId(parent, newProfileName); + } + public async Task DuplicateLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string? newProfileName, string? newProfileCommandName, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(cancellationToken); + + ILaunchProfile? existingProfile = launchSettings.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, currentProfileName)); + if (existingProfile is not null) + { newProfileName ??= await GetNewProfileNameAsync(cancellationToken); + newProfileCommandName ??= existingProfile.CommandName; - await _launchSettingsProvider.AddOrUpdateProfileAsync( - new WritableLaunchProfile - { - Name = newProfileName, - CommandName = commandName - }.ToLaunchProfile(), - addToFront: false); + var writableProfile = new WritableLaunchProfile(existingProfile) + { + Name = newProfileName, + CommandName = newProfileCommandName + }; + + await _launchSettingsProvider.AddOrUpdateProfileAsync(writableProfile.ToLaunchProfile(), addToFront: false); if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) { @@ -153,163 +181,134 @@ await _launchSettingsProvider.AddOrUpdateProfileAsync( return CreateLaunchProfileId(parent, newProfileName); } - public async Task DuplicateLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string? newProfileName, string? newProfileCommandName, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(cancellationToken); + return null; + } - ILaunchProfile? existingProfile = launchSettings.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, currentProfileName)); - if (existingProfile is not null) - { - newProfileName ??= await GetNewProfileNameAsync(cancellationToken); - newProfileCommandName ??= existingProfile.CommandName; + public async Task RemoveLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string profileName, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); - var writableProfile = new WritableLaunchProfile(existingProfile) - { - Name = newProfileName, - CommandName = newProfileCommandName - }; + await _launchSettingsProvider.RemoveProfileAsync(profileName); - await _launchSettingsProvider.AddOrUpdateProfileAsync(writableProfile.ToLaunchProfile(), addToFront: false); + if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) + { + queryExecutionContext.ReportUpdatedDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); + } - if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) - { - queryExecutionContext.ReportUpdatedDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); - } + return CreateLaunchProfileId(parent, profileName); + } - return CreateLaunchProfileId(parent, newProfileName); - } + public async Task<(EntityIdentity oldProfileId, EntityIdentity newProfileId)?> RenameLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string newProfileName, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); - return null; - } + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(cancellationToken); - public async Task RemoveLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string profileName, CancellationToken cancellationToken = default) + ILaunchProfile? existingProfile = launchSettings.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, currentProfileName)); + if (existingProfile is not null) { - cancellationToken.ThrowIfCancellationRequested(); + var writableProfile = new WritableLaunchProfile(existingProfile) + { + Name = newProfileName + }; - await _launchSettingsProvider.RemoveProfileAsync(profileName); + await _launchSettingsProvider.RemoveProfileAsync(currentProfileName); + await _launchSettingsProvider.AddOrUpdateProfileAsync(writableProfile.ToLaunchProfile(), addToFront: false); if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) { queryExecutionContext.ReportUpdatedDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); } - return CreateLaunchProfileId(parent, profileName); + return (CreateLaunchProfileId(parent, currentProfileName), CreateLaunchProfileId(parent, newProfileName)); } - public async Task<(EntityIdentity oldProfileId, EntityIdentity newProfileId)?> RenameLaunchProfileAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, string currentProfileName, string newProfileName, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); - - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(cancellationToken); - - ILaunchProfile? existingProfile = launchSettings.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, currentProfileName)); - if (existingProfile is not null) - { - var writableProfile = new WritableLaunchProfile(existingProfile) - { - Name = newProfileName - }; - - await _launchSettingsProvider.RemoveProfileAsync(currentProfileName); - await _launchSettingsProvider.AddOrUpdateProfileAsync(writableProfile.ToLaunchProfile(), addToFront: false); - - if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) - { - queryExecutionContext.ReportUpdatedDataVersion(_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version); - } + return null; + } - return (CreateLaunchProfileId(parent, currentProfileName), CreateLaunchProfileId(parent, newProfileName)); - } + private static IEntityValue CreateLaunchProfileValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, QueryProjectPropertiesContext propertiesContext, Rule rule, int order, IProjectState cache, ILaunchProfilePropertiesAvailableStatus properties) + { + LaunchProfileSnapshot newLaunchProfile = new(queryExecutionContext.EntityRuntime, id, new LaunchProfilePropertiesAvailableStatus()); - return null; + if (properties.Name) + { + Assumes.NotNull(propertiesContext.ItemName); + newLaunchProfile.Name = propertiesContext.ItemName; } - private static IEntityValue CreateLaunchProfileValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, QueryProjectPropertiesContext propertiesContext, Rule rule, int order, IProjectState cache, ILaunchProfilePropertiesAvailableStatus properties) + if (properties.DisplayName) { - LaunchProfileSnapshot newLaunchProfile = new(queryExecutionContext.EntityRuntime, id, new LaunchProfilePropertiesAvailableStatus()); - - if (properties.Name) - { - Assumes.NotNull(propertiesContext.ItemName); - newLaunchProfile.Name = propertiesContext.ItemName; - } + Assumes.NotNull(propertiesContext.ItemName); + newLaunchProfile.DisplayName = propertiesContext.ItemName; + } - if (properties.DisplayName) + if (properties.CommandName) + { + if (rule.Metadata.TryGetValue(s_commandNameMetadataName, out object? commandNameObj) + && commandNameObj is string commandName) { - Assumes.NotNull(propertiesContext.ItemName); - newLaunchProfile.DisplayName = propertiesContext.ItemName; + newLaunchProfile.CommandName = commandName; } + } - if (properties.CommandName) - { - if (rule.Metadata.TryGetValue(s_commandNameMetadataName, out object? commandNameObj) - && commandNameObj is string commandName) - { - newLaunchProfile.CommandName = commandName; - } - } + if (properties.Order) + { + newLaunchProfile.Order = order; + } - if (properties.Order) - { - newLaunchProfile.Order = order; - } + ((IEntityValueFromProvider)newLaunchProfile).ProviderState = new ContextAndRuleProviderState(cache, propertiesContext, rule); - ((IEntityValueFromProvider)newLaunchProfile).ProviderState = new ContextAndRuleProviderState(cache, propertiesContext, rule); + return newLaunchProfile; + } - return newLaunchProfile; - } + /// + /// Creates an representing a launch profile with the + /// name . + /// + private static EntityIdentity CreateLaunchProfileId(IEntityValue parent, string profileName) + { + return new EntityIdentity( + ((IEntityWithId)parent).Id, + new Dictionary + { + { ProjectModelIdentityKeys.SourceItemType, s_launchProfileSourceItemTypeValue }, + { ProjectModelIdentityKeys.SourceItemName, profileName } + }); + } - /// - /// Creates an representing a launch profile with the - /// name . - /// - private static EntityIdentity CreateLaunchProfileId(IEntityValue parent, string profileName) - { - return new EntityIdentity( - ((IEntityWithId)parent).Id, - new Dictionary - { - { ProjectModelIdentityKeys.SourceItemType, s_launchProfileSourceItemTypeValue }, - { ProjectModelIdentityKeys.SourceItemName, profileName } - }); - } + /// + /// Validates that represents a launch profile (or a child + /// entity of a launch profile) and throws if it does not. Returns the profile name. + /// + /// + /// We expect that the Project Query engine will only give us entities and entity + /// IDs that we know how to handle, as specified by the metadata on our + /// implementations of and . + /// Anything else warrants an exception. + /// + private static string ValidateIdAndExtractProfileName(EntityIdentity id) + { + Assumes.True(id.TryGetValue(ProjectModelIdentityKeys.SourceItemType, out string? type)); + Assumes.True(StringComparers.ItemTypes.Equals(type, s_launchProfileSourceItemTypeValue)); + Assumes.True(id.TryGetValue(ProjectModelIdentityKeys.SourceItemName, out string? name)); - /// - /// Validates that represents a launch profile (or a child - /// entity of a launch profile) and throws if it does not. Returns the profile name. - /// - /// - /// We expect that the Project Query engine will only give us entities and entity - /// IDs that we know how to handle, as specified by the metadata on our - /// implementations of and . - /// Anything else warrants an exception. - /// - private static string ValidateIdAndExtractProfileName(EntityIdentity id) - { - Assumes.True(id.TryGetValue(ProjectModelIdentityKeys.SourceItemType, out string? type)); - Assumes.True(StringComparers.ItemTypes.Equals(type, s_launchProfileSourceItemTypeValue)); - Assumes.True(id.TryGetValue(ProjectModelIdentityKeys.SourceItemName, out string? name)); + return name; + } - return name; - } + private async Task GetNewProfileNameAsync(CancellationToken cancellationToken = default) + { + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(cancellationToken); - private async Task GetNewProfileNameAsync(CancellationToken cancellationToken = default) + string? newProfileName = null; + for (int i = 1; newProfileName is null; i++) { - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(cancellationToken); - - string? newProfileName = null; - for (int i = 1; newProfileName is null; i++) + string potentialProfileName = string.Format(VSResources.DefaultNewProfileName, i); + if (!launchSettings.Profiles.Any(profile => StringComparers.LaunchProfileNames.Equals(potentialProfileName, profile.Name))) { - string potentialProfileName = string.Format(VSResources.DefaultNewProfileName, i); - if (!launchSettings.Profiles.Any(profile => StringComparers.LaunchProfileNames.Equals(potentialProfileName, profile.Name))) - { - newProfileName = potentialProfileName; - } + newProfileName = potentialProfileName; } - - return newProfileName; } + + return newProfileName; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RemoveLaunchProfileAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RemoveLaunchProfileAction.cs index 84233cbff5..bc1e5f5f45 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RemoveLaunchProfileAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RemoveLaunchProfileAction.cs @@ -4,28 +4,27 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// handling actions. +/// +internal sealed class RemoveLaunchProfileAction : LaunchProfileActionBase { - /// - /// handling actions. - /// - internal sealed class RemoveLaunchProfileAction : LaunchProfileActionBase + private readonly RemoveLaunchProfile _executableStep; + + public RemoveLaunchProfileAction(RemoveLaunchProfile executableStep) { - private readonly RemoveLaunchProfile _executableStep; + _executableStep = executableStep; + } - public RemoveLaunchProfileAction(RemoveLaunchProfile executableStep) - { - _executableStep = executableStep; - } + protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken) + { + EntityIdentity? removedProfileId = await launchProfileHandler.RemoveLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.ProfileName, cancellationToken); - protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken) + if (removedProfileId is not null) { - EntityIdentity? removedProfileId = await launchProfileHandler.RemoveLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.ProfileName, cancellationToken); - - if (removedProfileId is not null) - { - RemovedLaunchProfiles.Add((projectEntity, removedProfileId)); - } + RemovedLaunchProfiles.Add((projectEntity, removedProfileId)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RenameLaunchProfileAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RenameLaunchProfileAction.cs index 9ab21f7079..ff181b33d8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RenameLaunchProfileAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/RenameLaunchProfileAction.cs @@ -4,29 +4,28 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// handling actions. +/// +internal sealed class RenameLaunchProfileAction : LaunchProfileActionBase { - /// - /// handling actions. - /// - internal sealed class RenameLaunchProfileAction : LaunchProfileActionBase + private readonly RenameLaunchProfile _executableStep; + + public RenameLaunchProfileAction(RenameLaunchProfile executableStep) { - private readonly RenameLaunchProfile _executableStep; + _executableStep = executableStep; + } - public RenameLaunchProfileAction(RenameLaunchProfile executableStep) - { - _executableStep = executableStep; - } + protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken) + { + (EntityIdentity currentProfileId, EntityIdentity newProfileId)? changes = await launchProfileHandler.RenameLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.CurrentProfileName, _executableStep.NewProfileName, cancellationToken); - protected override async Task ExecuteAsync(IQueryExecutionContext queryExecutionContext, IEntityValue projectEntity, IProjectLaunchProfileHandler launchProfileHandler, CancellationToken cancellationToken) + if (changes.HasValue) { - (EntityIdentity currentProfileId, EntityIdentity newProfileId)? changes = await launchProfileHandler.RenameLaunchProfileAsync(queryExecutionContext, projectEntity, _executableStep.CurrentProfileName, _executableStep.NewProfileName, cancellationToken); - - if (changes.HasValue) - { - RemovedLaunchProfiles.Add((projectEntity, changes.Value.currentProfileId)); - AddedLaunchProfiles.Add((projectEntity, changes.Value.newProfileId)); - } + RemovedLaunchProfiles.Add((projectEntity, changes.Value.currentProfileId)); + AddedLaunchProfiles.Add((projectEntity, changes.Value.newProfileId)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/SetLaunchProfilePropertyAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/SetLaunchProfilePropertyAction.cs index 96b789e5a1..6ca61bdf85 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/SetLaunchProfilePropertyAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/LaunchProfiles/SetLaunchProfilePropertyAction.cs @@ -6,42 +6,41 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Framework; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +internal class SetLaunchProfilePropertyAction : QueryDataProducerBase, IQueryActionExecutor { - internal class SetLaunchProfilePropertyAction : QueryDataProducerBase, IQueryActionExecutor - { - private readonly SetLaunchProfilePropertyValue _executableStep; + private readonly SetLaunchProfilePropertyValue _executableStep; - public SetLaunchProfilePropertyAction(SetLaunchProfilePropertyValue executableStep) - { - _executableStep = executableStep; - } + public SetLaunchProfilePropertyAction(SetLaunchProfilePropertyValue executableStep) + { + _executableStep = executableStep; + } - public Task OnRequestProcessFinishedAsync(IQueryProcessRequest request) - { - return ResultReceiver.OnRequestProcessFinishedAsync(request); - } + public Task OnRequestProcessFinishedAsync(IQueryProcessRequest request) + { + return ResultReceiver.OnRequestProcessFinishedAsync(request); + } - public async Task ReceiveResultAsync(QueryProcessResult result) + public async Task ReceiveResultAsync(QueryProcessResult result) + { + result.Request.QueryExecutionContext.CancellationToken.ThrowIfCancellationRequested(); + if (((IEntityValueFromProvider)result.Result).ProviderState is ContextAndRuleProviderState state) { - result.Request.QueryExecutionContext.CancellationToken.ThrowIfCancellationRequested(); - if (((IEntityValueFromProvider)result.Result).ProviderState is ContextAndRuleProviderState state) + IProjectState projectState = state.ProjectState; + if (await projectState.GetSuggestedConfigurationAsync() is ProjectConfiguration configuration + && await projectState.BindToRuleAsync(configuration, state.Rule.Name, state.PropertiesContext) is IRule boundRule + && boundRule.GetProperty(_executableStep.PropertyName) is IProperty property) { - IProjectState projectState = state.ProjectState; - if (await projectState.GetSuggestedConfigurationAsync() is ProjectConfiguration configuration - && await projectState.BindToRuleAsync(configuration, state.Rule.Name, state.PropertiesContext) is IRule boundRule - && boundRule.GetProperty(_executableStep.PropertyName) is IProperty property) - { - await property.SetValueAsync(_executableStep.Value); + await property.SetValueAsync(_executableStep.Value); - if (await projectState.GetDataVersionAsync(configuration) is (string versionKey, long versionNumber)) - { - result.Request.QueryExecutionContext.ReportUpdatedDataVersion(versionKey, versionNumber); - } + if (await projectState.GetDataVersionAsync(configuration) is (string versionKey, long versionNumber)) + { + result.Request.QueryExecutionContext.ReportUpdatedDataVersion(versionKey, versionNumber); } } - - await ResultReceiver.ReceiveResultAsync(result); } + + await ResultReceiver.ReceiveResultAsync(result); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/AbstractProjectState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/AbstractProjectState.cs index 9b6151bd12..6b7db0c3b3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/AbstractProjectState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/AbstractProjectState.cs @@ -3,119 +3,118 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +internal abstract class AbstractProjectState : IProjectState { - internal abstract class AbstractProjectState : IProjectState - { - protected readonly UnconfiguredProject Project; + protected readonly UnconfiguredProject Project; - private readonly Dictionary _catalogCache; - private readonly Dictionary<(ProjectConfiguration, string, QueryProjectPropertiesContext), IRule?> _ruleCache; + private readonly Dictionary _catalogCache; + private readonly Dictionary<(ProjectConfiguration, string, QueryProjectPropertiesContext), IRule?> _ruleCache; - private readonly AsyncLazy?> _knownProjectConfigurations; - private readonly AsyncLazy _defaultProjectConfiguration; + private readonly AsyncLazy?> _knownProjectConfigurations; + private readonly AsyncLazy _defaultProjectConfiguration; - protected AbstractProjectState(UnconfiguredProject project) - { - Project = project; - JoinableTaskFactory joinableTaskFactory = project.Services.ThreadingPolicy.JoinableTaskFactory; + protected AbstractProjectState(UnconfiguredProject project) + { + Project = project; + JoinableTaskFactory joinableTaskFactory = project.Services.ThreadingPolicy.JoinableTaskFactory; - _knownProjectConfigurations = new AsyncLazy?>(CreateKnownConfigurationsAsync, joinableTaskFactory); - _defaultProjectConfiguration = new AsyncLazy(CreateDefaultConfigurationAsync, joinableTaskFactory); - _catalogCache = new Dictionary(); - _ruleCache = new Dictionary<(ProjectConfiguration, string, QueryProjectPropertiesContext), IRule?>(); + _knownProjectConfigurations = new AsyncLazy?>(CreateKnownConfigurationsAsync, joinableTaskFactory); + _defaultProjectConfiguration = new AsyncLazy(CreateDefaultConfigurationAsync, joinableTaskFactory); + _catalogCache = new Dictionary(); + _ruleCache = new Dictionary<(ProjectConfiguration, string, QueryProjectPropertiesContext), IRule?>(); + } + + /// + /// Retrieves the with name "" within the given and . + /// + public async Task BindToRuleAsync(ProjectConfiguration projectConfiguration, string schemaName, QueryProjectPropertiesContext propertiesContext) + { + if (_ruleCache.TryGetValue((projectConfiguration, schemaName, propertiesContext), out IRule? cachedRule)) + { + return cachedRule; } - /// - /// Retrieves the with name "" within the given and . - /// - public async Task BindToRuleAsync(ProjectConfiguration projectConfiguration, string schemaName, QueryProjectPropertiesContext propertiesContext) + IRule? rule = null; + if (await GetProjectLevelPropertyPagesCatalogAsync(projectConfiguration) is IPropertyPagesCatalog catalog) { - if (_ruleCache.TryGetValue((projectConfiguration, schemaName, propertiesContext), out IRule? cachedRule)) - { - return cachedRule; - } - - IRule? rule = null; - if (await GetProjectLevelPropertyPagesCatalogAsync(projectConfiguration) is IPropertyPagesCatalog catalog) - { - rule = catalog.BindToContext(schemaName, propertiesContext); - } - - _ruleCache.Add((projectConfiguration, schemaName, propertiesContext), rule); - return rule; + rule = catalog.BindToContext(schemaName, propertiesContext); } - /// - /// Retrieves the set of s for the project. - /// Use this when you actually need all of the s; - /// use when you just need any - /// . - /// - public Task?> GetKnownConfigurationsAsync() => _knownProjectConfigurations.GetValueAsync(); + _ruleCache.Add((projectConfiguration, schemaName, propertiesContext), rule); + return rule; + } + + /// + /// Retrieves the set of s for the project. + /// Use this when you actually need all of the s; + /// use when you just need any + /// . + /// + public Task?> GetKnownConfigurationsAsync() => _knownProjectConfigurations.GetValueAsync(); - /// - /// Retrieves a default for the project. - /// - public Task GetSuggestedConfigurationAsync() => _defaultProjectConfiguration.GetValueAsync(); + /// + /// Retrieves a default for the project. + /// + public Task GetSuggestedConfigurationAsync() => _defaultProjectConfiguration.GetValueAsync(); - private async Task CreateDefaultConfigurationAsync() + private async Task CreateDefaultConfigurationAsync() + { + if (Project.Services.ProjectConfigurationsService is IProjectConfigurationsService2 configurationsService2) { - if (Project.Services.ProjectConfigurationsService is IProjectConfigurationsService2 configurationsService2) - { - return await configurationsService2.GetSuggestedProjectConfigurationAsync(); - } - else if (Project.Services.ProjectConfigurationsService is IProjectConfigurationsService configurationsService) - { - return configurationsService.SuggestedProjectConfiguration; - } - else - { - return null; - } + return await configurationsService2.GetSuggestedProjectConfigurationAsync(); } - - private async Task?> CreateKnownConfigurationsAsync() + else if (Project.Services.ProjectConfigurationsService is IProjectConfigurationsService configurationsService) + { + return configurationsService.SuggestedProjectConfiguration; + } + else { - if (Project.Services.ProjectConfigurationsService is IProjectConfigurationsService configurationsService) - { - return await configurationsService.GetKnownProjectConfigurationsAsync(); - } - return null; } + } - /// - /// Retrieves the set of property pages that apply to the project level for the given . - /// - private async Task GetProjectLevelPropertyPagesCatalogAsync(ProjectConfiguration projectConfiguration) + private async Task?> CreateKnownConfigurationsAsync() + { + if (Project.Services.ProjectConfigurationsService is IProjectConfigurationsService configurationsService) { - if (_catalogCache.TryGetValue(projectConfiguration, out IPropertyPagesCatalog? cachedCatalog)) - { - return cachedCatalog; - } + return await configurationsService.GetKnownProjectConfigurationsAsync(); + } - ConfiguredProject configuredProject = await Project.LoadConfiguredProjectAsync(projectConfiguration); - IPropertyPagesCatalog? catalog = await configuredProject.GetProjectLevelPropertyPagesCatalogAsync(); + return null; + } - _catalogCache.Add(projectConfiguration, catalog); - return catalog; + /// + /// Retrieves the set of property pages that apply to the project level for the given . + /// + private async Task GetProjectLevelPropertyPagesCatalogAsync(ProjectConfiguration projectConfiguration) + { + if (_catalogCache.TryGetValue(projectConfiguration, out IPropertyPagesCatalog? cachedCatalog)) + { + return cachedCatalog; } - public async Task<(string versionKey, long versionNumber)?> GetMetadataVersionAsync() - { - if (await GetSuggestedConfigurationAsync() is ProjectConfiguration configuration) - { - ConfiguredProject configuredProject = await Project.LoadConfiguredProjectAsync(configuration); - configuredProject.GetQueryDataVersion(out string versionKey, out long versionNumber); - return (versionKey, versionNumber); - } + ConfiguredProject configuredProject = await Project.LoadConfiguredProjectAsync(projectConfiguration); + IPropertyPagesCatalog? catalog = await configuredProject.GetProjectLevelPropertyPagesCatalogAsync(); - return null; + _catalogCache.Add(projectConfiguration, catalog); + return catalog; + } + + public async Task<(string versionKey, long versionNumber)?> GetMetadataVersionAsync() + { + if (await GetSuggestedConfigurationAsync() is ProjectConfiguration configuration) + { + ConfiguredProject configuredProject = await Project.LoadConfiguredProjectAsync(configuration); + configuredProject.GetQueryDataVersion(out string versionKey, out long versionNumber); + return (versionKey, versionNumber); } - public abstract Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration); + return null; } + + public abstract Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryByIdDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryByIdDataProducer.cs index ddcabebbbf..f0066599b6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryByIdDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryByIdDataProducer.cs @@ -5,40 +5,39 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving an based on an ID. +/// +internal class CategoryByIdDataProducer : QueryDataByIdProducerBase { - /// - /// Handles retrieving an based on an ID. - /// - internal class CategoryByIdDataProducer : QueryDataByIdProducerBase + private readonly ICategoryPropertiesAvailableStatus _properties; + private readonly IProjectService2 _projectService; + + public CategoryByIdDataProducer(ICategoryPropertiesAvailableStatus properties, IProjectService2 projectService) { - private readonly ICategoryPropertiesAvailableStatus _properties; - private readonly IProjectService2 _projectService; + _properties = properties; + _projectService = projectService; + } - public CategoryByIdDataProducer(ICategoryPropertiesAvailableStatus properties, IProjectService2 projectService) + protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) + { + if (id.KeysCount == 3 + && id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath) + && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName) + && id.TryGetValue(ProjectModelIdentityKeys.CategoryName, out string? categoryName)) { - _properties = properties; - _projectService = projectService; + return CategoryDataProducer.CreateCategoryValueAsync( + queryExecutionContext, + id, + _projectService, + projectPath, + propertyPageName, + categoryName, + _properties); } - protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) - { - if (id.KeysCount == 3 - && id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath) - && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName) - && id.TryGetValue(ProjectModelIdentityKeys.CategoryName, out string? categoryName)) - { - return CategoryDataProducer.CreateCategoryValueAsync( - queryExecutionContext, - id, - _projectService, - projectPath, - propertyPageName, - categoryName, - _properties); - } - - return NullEntityValue; - } + return NullEntityValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducer.cs index 2d7ed2968e..005e5821ee 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducer.cs @@ -7,100 +7,99 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Framework; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class CategoryDataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class CategoryDataProducer + public static IEntityValue CreateCategoryValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, Rule rule, Category category, int order, ICategoryPropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreateCategoryValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, Rule rule, Category category, int order, ICategoryPropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(parent); - Requires.NotNull(category); + Requires.NotNull(parent); + Requires.NotNull(category); - var identity = new EntityIdentity( - ((IEntityWithId)parent).Id, - new KeyValuePair[] - { - new(ProjectModelIdentityKeys.CategoryName, category.Name) - }); + var identity = new EntityIdentity( + ((IEntityWithId)parent).Id, + new KeyValuePair[] + { + new(ProjectModelIdentityKeys.CategoryName, category.Name) + }); - return CreateCategoryValue(queryExecutionContext, identity, rule, category, order, requestedProperties); - } + return CreateCategoryValue(queryExecutionContext, identity, rule, category, order, requestedProperties); + } - public static IEntityValue CreateCategoryValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, Rule rule, Category category, int order, ICategoryPropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(category); - var newCategory = new CategorySnapshot(queryExecutionContext.EntityRuntime, id, new CategoryPropertiesAvailableStatus()); + public static IEntityValue CreateCategoryValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, Rule rule, Category category, int order, ICategoryPropertiesAvailableStatus requestedProperties) + { + Requires.NotNull(category); + var newCategory = new CategorySnapshot(queryExecutionContext.EntityRuntime, id, new CategoryPropertiesAvailableStatus()); - if (requestedProperties.DisplayName) - { - newCategory.DisplayName = category.DisplayName; - } + if (requestedProperties.DisplayName) + { + newCategory.DisplayName = category.DisplayName; + } - if (requestedProperties.Name) - { - newCategory.Name = category.Name; - } + if (requestedProperties.Name) + { + newCategory.Name = category.Name; + } - if (requestedProperties.Order) - { - newCategory.Order = order; - } + if (requestedProperties.Order) + { + newCategory.Order = order; + } - ((IEntityValueFromProvider)newCategory).ProviderState = category; + ((IEntityValueFromProvider)newCategory).ProviderState = category; - return newCategory; - } + return newCategory; + } - public static IEnumerable CreateCategoryValues(IQueryExecutionContext queryExecutionContext, IEntityValue parent, Rule rule, ICategoryPropertiesAvailableStatus requestedProperties) + public static IEnumerable CreateCategoryValues(IQueryExecutionContext queryExecutionContext, IEntityValue parent, Rule rule, ICategoryPropertiesAvailableStatus requestedProperties) + { + int index = 0; + foreach (Category category in rule.EvaluatedCategories) { - int index = 0; - foreach (Category category in rule.EvaluatedCategories) - { - IEntityValue categoryValue = CreateCategoryValue(queryExecutionContext, parent, rule, category, index, requestedProperties); - yield return categoryValue; - index++; - } + IEntityValue categoryValue = CreateCategoryValue(queryExecutionContext, parent, rule, category, index, requestedProperties); + yield return categoryValue; + index++; } + } - public static async Task CreateCategoryValueAsync( - IQueryExecutionContext queryExecutionContext, - EntityIdentity id, - IProjectService2 projectService, - string projectPath, - string propertyPageName, - string categoryName, - ICategoryPropertiesAvailableStatus requestedProperties) + public static async Task CreateCategoryValueAsync( + IQueryExecutionContext queryExecutionContext, + EntityIdentity id, + IProjectService2 projectService, + string projectPath, + string propertyPageName, + string categoryName, + ICategoryPropertiesAvailableStatus requestedProperties) + { + if (projectService.GetLoadedProject(projectPath) is UnconfiguredProject project) { - if (projectService.GetLoadedProject(projectPath) is UnconfiguredProject project) - { - // TODO: Update this to match what we do in IProjectState.GetMetadataVersionAsync - project.GetQueryDataVersion(out string versionKey, out long versionNumber); - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); + // TODO: Update this to match what we do in IProjectState.GetMetadataVersionAsync + project.GetQueryDataVersion(out string versionKey, out long versionNumber); + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); - if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog - && projectCatalog.GetSchema(propertyPageName) is Rule rule) + if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog + && projectCatalog.GetSchema(propertyPageName) is Rule rule) + { + // We need the category's index in order to populate the "Order" field of the query model. + // This requires that we do a linear traversal of the categories, even though we only care + // about one. + // + // TODO: if the "Order" property hasn't been requested, we can skip the linear traversal in + // favor of just looking it up by name. + foreach ((int index, Category category) in rule.EvaluatedCategories.WithIndices()) { - // We need the category's index in order to populate the "Order" field of the query model. - // This requires that we do a linear traversal of the categories, even though we only care - // about one. - // - // TODO: if the "Order" property hasn't been requested, we can skip the linear traversal in - // favor of just looking it up by name. - foreach ((int index, Category category) in rule.EvaluatedCategories.WithIndices()) + if (StringComparers.CategoryNames.Equals(category.Name, categoryName)) { - if (StringComparers.CategoryNames.Equals(category.Name, categoryName)) - { - IEntityValue categoryValue = CreateCategoryValue(queryExecutionContext, id, rule, category, index, requestedProperties); - return categoryValue; - } + IEntityValue categoryValue = CreateCategoryValue(queryExecutionContext, id, rule, category, index, requestedProperties); + return categoryValue; } } } - - return null; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProvider.cs index feed718db1..d32bd602ab 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryDataProvider.cs @@ -7,40 +7,39 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve category information (). +/// +/// +/// Responsible for populating . Can also retrieve a +/// based on its ID. +/// Note this is almost identical to the ; the only reason we have +/// both is that cannot be applied multiple times to the same +/// type, so we can't have one type that handles multiple relationships. +/// +[QueryDataProvider(CategoryType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(PropertyPageType.TypeName, PropertyPageType.CategoriesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByIdDataProvider))] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class CategoryDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve category information (). - /// - /// - /// Responsible for populating . Can also retrieve a - /// based on its ID. - /// Note this is almost identical to the ; the only reason we have - /// both is that cannot be applied multiple times to the same - /// type, so we can't have one type that handles multiple relationships. - /// - [QueryDataProvider(CategoryType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(PropertyPageType.TypeName, PropertyPageType.CategoriesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByIdDataProvider))] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class CategoryDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider + [ImportingConstructor] + public CategoryDataProvider(IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public CategoryDataProvider(IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new CategoryByIdDataProducer((ICategoryPropertiesAvailableStatus)properties, ProjectService); - } + public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new CategoryByIdDataProducer((ICategoryPropertiesAvailableStatus)properties, ProjectService); + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new CategoryFromRuleDataProducer((ICategoryPropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new CategoryFromRuleDataProducer((ICategoryPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryFromRuleDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryFromRuleDataProducer.cs index 7cc224b7bf..57528810e1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryFromRuleDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/CategoryFromRuleDataProducer.cs @@ -5,30 +5,29 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an +/// or . +/// +internal class CategoryFromRuleDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an - /// or . - /// - internal class CategoryFromRuleDataProducer : QueryDataFromProviderStateProducerBase + private readonly ICategoryPropertiesAvailableStatus _properties; + + public CategoryFromRuleDataProducer(ICategoryPropertiesAvailableStatus properties) { - private readonly ICategoryPropertiesAvailableStatus _properties; + Requires.NotNull(properties); + _properties = properties; + } - public CategoryFromRuleDataProducer(ICategoryPropertiesAvailableStatus properties) + protected override async Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ContextAndRuleProviderState providerState) + { + if (await providerState.ProjectState.GetMetadataVersionAsync() is (string versionKey, long versionNumber)) { - Requires.NotNull(properties); - _properties = properties; + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); } - protected override async Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ContextAndRuleProviderState providerState) - { - if (await providerState.ProjectState.GetMetadataVersionAsync() is (string versionKey, long versionNumber)) - { - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); - } - - return CategoryDataProducer.CreateCategoryValues(queryExecutionContext, parent, providerState.Rule, _properties); - } + return CategoryDataProducer.CreateCategoryValues(queryExecutionContext, parent, providerState.Rule, _properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducer.cs index 5095037801..ad7eb0bf71 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducer.cs @@ -3,48 +3,47 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested +/// members. +/// +internal static class ConfigurationDimensionDataProducer { - /// - /// Handles the creation of instances and populating the requested - /// members. - /// - internal static class ConfigurationDimensionDataProducer + public static IEntityValue CreateProjectConfigurationDimension( + IEntityRuntimeModel runtimeModel, + KeyValuePair projectConfigurationDimension, + IConfigurationDimensionPropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreateProjectConfigurationDimension( - IEntityRuntimeModel runtimeModel, - KeyValuePair projectConfigurationDimension, - IConfigurationDimensionPropertiesAvailableStatus requestedProperties) + var newProjectConfigurationDimension = new ConfigurationDimensionSnapshot(runtimeModel, new ConfigurationDimensionPropertiesAvailableStatus()); + + if (requestedProperties.Name) { - var newProjectConfigurationDimension = new ConfigurationDimensionSnapshot(runtimeModel, new ConfigurationDimensionPropertiesAvailableStatus()); + newProjectConfigurationDimension.Name = projectConfigurationDimension.Key; + } - if (requestedProperties.Name) - { - newProjectConfigurationDimension.Name = projectConfigurationDimension.Key; - } + if (requestedProperties.Value) + { + newProjectConfigurationDimension.Value = projectConfigurationDimension.Value; + } - if (requestedProperties.Value) - { - newProjectConfigurationDimension.Value = projectConfigurationDimension.Value; - } + return newProjectConfigurationDimension; + } - return newProjectConfigurationDimension; + public static IEnumerable CreateProjectConfigurationDimensions(IEntityValue parent, ProjectConfiguration configuration, ProjectSystem.Properties.IProperty property, IConfigurationDimensionPropertiesAvailableStatus requestedProperties) + { + // If the property is configuration-independent then report no dimensions; + // the parent property value applies to all configurations. + if (!property.DataSource.HasConfigurationCondition) + { + yield break; } - public static IEnumerable CreateProjectConfigurationDimensions(IEntityValue parent, ProjectConfiguration configuration, ProjectSystem.Properties.IProperty property, IConfigurationDimensionPropertiesAvailableStatus requestedProperties) + foreach (KeyValuePair dimension in configuration.Dimensions) { - // If the property is configuration-independent then report no dimensions; - // the parent property value applies to all configurations. - if (!property.DataSource.HasConfigurationCondition) - { - yield break; - } - - foreach (KeyValuePair dimension in configuration.Dimensions) - { - IEntityValue projectConfigurationDimension = CreateProjectConfigurationDimension(parent.EntityRuntime, dimension, requestedProperties); - yield return projectConfigurationDimension; - } + IEntityValue projectConfigurationDimension = CreateProjectConfigurationDimension(parent.EntityRuntime, dimension, requestedProperties); + yield return projectConfigurationDimension; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProvider.cs index 24fe7d8cbd..49a397c1c2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProvider.cs @@ -6,26 +6,25 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve configuration dimension +/// information (). +/// +/// +/// Responsible for populating . See and for +/// the important logic. +/// +[QueryDataProvider(ConfigurationDimensionType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(UIPropertyValueType.TypeName, UIPropertyValueType.ConfigurationDimensionsPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class ConfigurationDimensionDataProvider : IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve configuration dimension - /// information (). - /// - /// - /// Responsible for populating . See and for - /// the important logic. - /// - [QueryDataProvider(ConfigurationDimensionType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(UIPropertyValueType.TypeName, UIPropertyValueType.ConfigurationDimensionsPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class ConfigurationDimensionDataProvider : IQueryByRelationshipDataProvider + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) { - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new ConfigurationDimensionFromPropertyDataProducer((IConfigurationDimensionPropertiesAvailableStatus)properties); - } + return new ConfigurationDimensionFromPropertyDataProducer((IConfigurationDimensionPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionFromPropertyDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionFromPropertyDataProducer.cs index c98c1cf65c..56b8cc98ff 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionFromPropertyDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionFromPropertyDataProducer.cs @@ -3,30 +3,29 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an . +/// +/// +/// The comes from the parent +/// +internal class ConfigurationDimensionFromPropertyDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an . - /// - /// - /// The comes from the parent - /// - internal class ConfigurationDimensionFromPropertyDataProducer : QueryDataFromProviderStateProducerBase - { - private readonly IConfigurationDimensionPropertiesAvailableStatus _properties; + private readonly IConfigurationDimensionPropertiesAvailableStatus _properties; - public ConfigurationDimensionFromPropertyDataProducer(IConfigurationDimensionPropertiesAvailableStatus properties) - { - _properties = properties; - } + public ConfigurationDimensionFromPropertyDataProducer(IConfigurationDimensionPropertiesAvailableStatus properties) + { + _properties = properties; + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyValueProviderState providerState) - { - return Task.FromResult(ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions( - parent, - providerState.ProjectConfiguration, - providerState.Property, - _properties)); - } + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyValueProviderState providerState) + { + return Task.FromResult(ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions( + parent, + providerState.ProjectConfiguration, + providerState.Property, + _properties)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ContextAndRuleProviderState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ContextAndRuleProviderState.cs index e9f928e33b..70cc832d2d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ContextAndRuleProviderState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ContextAndRuleProviderState.cs @@ -2,25 +2,24 @@ using Microsoft.Build.Framework.XamlTypes; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Hold a and the context in which to bind it. Used by +/// and +/// to pass the state their child producers will need, but allows the actual binding +/// of the to be delayed until needed. +/// +internal sealed class ContextAndRuleProviderState { - /// - /// Hold a and the context in which to bind it. Used by - /// and - /// to pass the state their child producers will need, but allows the actual binding - /// of the to be delayed until needed. - /// - internal sealed class ContextAndRuleProviderState + public ContextAndRuleProviderState(IProjectState projectState, QueryProjectPropertiesContext propertiesContext, Rule rule) { - public ContextAndRuleProviderState(IProjectState projectState, QueryProjectPropertiesContext propertiesContext, Rule rule) - { - ProjectState = projectState; - PropertiesContext = propertiesContext; - Rule = rule; - } - - public IProjectState ProjectState { get; } - public QueryProjectPropertiesContext PropertiesContext { get; } - public Rule Rule { get; } + ProjectState = projectState; + PropertiesContext = propertiesContext; + Rule = rule; } + + public IProjectState ProjectState { get; } + public QueryProjectPropertiesContext PropertiesContext { get; } + public Rule Rule { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/DebugUtilities.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/DebugUtilities.cs index c84f6011fd..ad3d2fec7b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/DebugUtilities.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/DebugUtilities.cs @@ -3,25 +3,24 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Helper methods to convert back and forth between the page, category, and property names +/// used by the Debug page in the Project Query API, and the names used by the underlying +/// property system. +/// +internal static class DebugUtilities { - /// - /// Helper methods to convert back and forth between the page, category, and property names - /// used by the Debug page in the Project Query API, and the names used by the underlying - /// property system. - /// - internal static class DebugUtilities - { - private const string CommandNameBasedDebuggerPageTemplate = "commandNameBasedDebugger"; + private const string CommandNameBasedDebuggerPageTemplate = "commandNameBasedDebugger"; - public static IEnumerable GetDebugChildRules(IPropertyPagesCatalog projectCatalog) + public static IEnumerable GetDebugChildRules(IPropertyPagesCatalog projectCatalog) + { + foreach (string schemaName in projectCatalog.GetPropertyPagesSchemas(itemType: "LaunchProfile")) { - foreach (string schemaName in projectCatalog.GetPropertyPagesSchemas(itemType: "LaunchProfile")) + if (projectCatalog.GetSchema(schemaName) is { PropertyPagesHidden: false, PageTemplate: CommandNameBasedDebuggerPageTemplate } possibleChildRule) { - if (projectCatalog.GetSchema(schemaName) is { PropertyPagesHidden: false, PageTemplate: CommandNameBasedDebuggerPageTemplate } possibleChildRule) - { - yield return possibleChildRule; - } + yield return possibleChildRule; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/IProjectState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/IProjectState.cs index fd5eed7e00..b5044898e8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/IProjectState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/IProjectState.cs @@ -4,43 +4,42 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Holds information about the project that is generally needed by Project Query API data providers. +/// +/// +/// +/// The expectation is that at most one instance of this type will be created per query, and that instance will be +/// passed from one provider to the next as part of . This +/// allows us to maximize the use of cached data within a query, but we are also guaranteed that the cache will not be +/// held past the end of the query. +/// +/// +/// As an example, consider what needs to occur when we want to retrieve the set of s +/// for a : +/// +/// Retrieve the set of known configurations from the . +/// For each configuration, get the property page catalog. +/// Retrieve the for the property page from the catalog. +/// Find the property within the . +/// Retrieve the property value. +/// +/// The produces s one at a time, and needs +/// to do these steps for each one. Given that a query will likely retrieve values for multiple properties on +/// multiple pages across multiple configurations, introducing caching at key levels in the process can +/// significantly reduce the amount of work we need to do. +/// +/// +internal interface IProjectState { /// - /// Holds information about the project that is generally needed by Project Query API data providers. + /// Binds the specified schema to a particular context within the given project configuration. /// - /// - /// - /// The expectation is that at most one instance of this type will be created per query, and that instance will be - /// passed from one provider to the next as part of . This - /// allows us to maximize the use of cached data within a query, but we are also guaranteed that the cache will not be - /// held past the end of the query. - /// - /// - /// As an example, consider what needs to occur when we want to retrieve the set of s - /// for a : - /// - /// Retrieve the set of known configurations from the . - /// For each configuration, get the property page catalog. - /// Retrieve the for the property page from the catalog. - /// Find the property within the . - /// Retrieve the property value. - /// - /// The produces s one at a time, and needs - /// to do these steps for each one. Given that a query will likely retrieve values for multiple properties on - /// multiple pages across multiple configurations, introducing caching at key levels in the process can - /// significantly reduce the amount of work we need to do. - /// - /// - internal interface IProjectState - { - /// - /// Binds the specified schema to a particular context within the given project configuration. - /// - Task BindToRuleAsync(ProjectConfiguration projectConfiguration, string schemaName, QueryProjectPropertiesContext propertiesContext); - Task?> GetKnownConfigurationsAsync(); - Task GetSuggestedConfigurationAsync(); - Task<(string versionKey, long versionNumber)?> GetMetadataVersionAsync(); - Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration); - } + Task BindToRuleAsync(ProjectConfiguration projectConfiguration, string schemaName, QueryProjectPropertiesContext propertiesContext); + Task?> GetKnownConfigurationsAsync(); + Task GetSuggestedConfigurationAsync(); + Task<(string versionKey, long versionNumber)?> GetMetadataVersionAsync(); + Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileByIdDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileByIdDataProducer.cs index 06adac8bfa..909cee03df 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileByIdDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileByIdDataProducer.cs @@ -4,36 +4,35 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +internal class LaunchProfileByIdDataProducer : QueryDataByIdProducerBase { - internal class LaunchProfileByIdDataProducer : QueryDataByIdProducerBase + private readonly ILaunchProfilePropertiesAvailableStatus _properties; + private readonly IProjectService2 _projectService; + + public LaunchProfileByIdDataProducer(ILaunchProfilePropertiesAvailableStatus properties, IProjectService2 projectService) { - private readonly ILaunchProfilePropertiesAvailableStatus _properties; - private readonly IProjectService2 _projectService; + _properties = properties; + _projectService = projectService; + } - public LaunchProfileByIdDataProducer(ILaunchProfilePropertiesAvailableStatus properties, IProjectService2 projectService) - { - _properties = properties; - _projectService = projectService; - } + protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) + { + string projectPath = ValidateIdAndExtractProjectPath(id); - protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) + if (_projectService.GetLoadedProject(projectPath) is UnconfiguredProject project + && project.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler launchProfileHandler) { - string projectPath = ValidateIdAndExtractProjectPath(id); - - if (_projectService.GetLoadedProject(projectPath) is UnconfiguredProject project - && project.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler launchProfileHandler) - { - return launchProfileHandler.RetrieveLaunchProfileEntityAsync(queryExecutionContext, id, _properties); - } - - return NullEntityValue; + return launchProfileHandler.RetrieveLaunchProfileEntityAsync(queryExecutionContext, id, _properties); } - private static string ValidateIdAndExtractProjectPath(EntityIdentity id) - { - Assumes.True(id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath)); - return projectPath; - } + return NullEntityValue; + } + + private static string ValidateIdAndExtractProjectPath(EntityIdentity id) + { + Assumes.True(id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath)); + return projectPath; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileCategoryDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileCategoryDataProvider.cs index ebcf96cee6..05969a70d6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileCategoryDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileCategoryDataProvider.cs @@ -7,33 +7,32 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve category information (). +/// +/// +/// Responsible for populating . +/// Note this is almost identical to the ; the only reason we have both is that +/// cannot be applied multiple times to the same type, so we +/// can't have one type that handles multiple relationships. +/// +[QueryDataProvider(CategoryType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(LaunchProfileType.TypeName, LaunchProfileType.CategoriesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class LaunchProfileCategoryDataProvider : QueryDataProviderBase, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve category information (). - /// - /// - /// Responsible for populating . - /// Note this is almost identical to the ; the only reason we have both is that - /// cannot be applied multiple times to the same type, so we - /// can't have one type that handles multiple relationships. - /// - [QueryDataProvider(CategoryType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(LaunchProfileType.TypeName, LaunchProfileType.CategoriesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class LaunchProfileCategoryDataProvider : QueryDataProviderBase, IQueryByRelationshipDataProvider + [ImportingConstructor] + public LaunchProfileCategoryDataProvider(IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public LaunchProfileCategoryDataProvider(IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new CategoryFromRuleDataProducer((ICategoryPropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new CategoryFromRuleDataProducer((ICategoryPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProducer.cs index 939a3c09ab..1b404bff84 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProducer.cs @@ -3,19 +3,18 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +internal static class LaunchProfileDataProducer { - internal static class LaunchProfileDataProducer + public static EntityIdentity CreateLaunchProfileId(IEntityValue parent, string itemType, string itemName) { - public static EntityIdentity CreateLaunchProfileId(IEntityValue parent, string itemType, string itemName) - { - return new EntityIdentity( - ((IEntityWithId)parent).Id, - new Dictionary - { - { ProjectModelIdentityKeys.SourceItemType, itemType }, - { ProjectModelIdentityKeys.SourceItemName, itemName } - }); - } + return new EntityIdentity( + ((IEntityWithId)parent).Id, + new Dictionary + { + { ProjectModelIdentityKeys.SourceItemType, itemType }, + { ProjectModelIdentityKeys.SourceItemName, itemName } + }); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProvider.cs index bf0a03d0af..38653b19c9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileDataProvider.cs @@ -7,38 +7,37 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve launch profile information +/// (see ) for a project. +/// +/// +/// Responsible for populating . +/// +[QueryDataProvider(LaunchProfileType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByIdDataProvider))] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class LaunchProfileDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve launch profile information - /// (see ) for a project. - /// - /// - /// Responsible for populating . - /// - [QueryDataProvider(LaunchProfileType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectSystem.Query.Metadata.ProjectType.LaunchProfilesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByIdDataProvider))] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class LaunchProfileDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider - { - [ImportingConstructor] - public LaunchProfileDataProvider( - IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + [ImportingConstructor] + public LaunchProfileDataProvider( + IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) + { + } - public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new LaunchProfileByIdDataProducer((ILaunchProfilePropertiesAvailableStatus)properties, ProjectService); - } + public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new LaunchProfileByIdDataProducer((ILaunchProfilePropertiesAvailableStatus)properties, ProjectService); + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new LaunchProfileFromProjectDataProducer((ILaunchProfilePropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new LaunchProfileFromProjectDataProducer((ILaunchProfilePropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileFromProjectDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileFromProjectDataProducer.cs index 25be4dc8b6..2614df44f0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileFromProjectDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileFromProjectDataProducer.cs @@ -4,25 +4,24 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +internal class LaunchProfileFromProjectDataProducer : QueryDataFromProviderStateProducerBase { - internal class LaunchProfileFromProjectDataProducer : QueryDataFromProviderStateProducerBase + private readonly ILaunchProfilePropertiesAvailableStatus _properties; + + public LaunchProfileFromProjectDataProducer(ILaunchProfilePropertiesAvailableStatus properties) { - private readonly ILaunchProfilePropertiesAvailableStatus _properties; + _properties = properties; + } - public LaunchProfileFromProjectDataProducer(ILaunchProfilePropertiesAvailableStatus properties) + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, UnconfiguredProject providerState) + { + if (providerState.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler launchProfileHandler) { - _properties = properties; + return launchProfileHandler.RetrieveAllLaunchProfileEntitiesAsync(queryExecutionContext, parent, _properties); } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, UnconfiguredProject providerState) - { - if (providerState.Services.ExportProvider.GetExportedValueOrDefault() is IProjectLaunchProfileHandler launchProfileHandler) - { - return launchProfileHandler.RetrieveAllLaunchProfileEntitiesAsync(queryExecutionContext, parent, _properties); - } - - return TaskResult.EmptyEnumerable(); - } + return TaskResult.EmptyEnumerable(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileTypeDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileTypeDataProvider.cs index 1b8d0d5902..ef2ee008e1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileTypeDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileTypeDataProvider.cs @@ -10,126 +10,125 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query.PropertyPages; + +/// +/// Creates instances that retrieve launch profile type information +/// (see ) for a project. +/// +/// +/// Responsible for populating . +/// +[QueryDataProvider(LaunchProfileTypeType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectSystem.Query.Metadata.ProjectType.LaunchProfileTypesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class LaunchProfileTypeDataProvider : QueryDataProviderBase, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve launch profile type information - /// (see ) for a project. - /// - /// - /// Responsible for populating . - /// - [QueryDataProvider(LaunchProfileTypeType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectSystem.Query.Metadata.ProjectType.LaunchProfileTypesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class LaunchProfileTypeDataProvider : QueryDataProviderBase, IQueryByRelationshipDataProvider + [ImportingConstructor] + public LaunchProfileTypeDataProvider( + IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public LaunchProfileTypeDataProvider( - IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new LaunchProfileTypeFromProjectDataProducer((ILaunchProfileTypePropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new LaunchProfileTypeFromProjectDataProducer((ILaunchProfileTypePropertiesAvailableStatus)properties); } +} + +internal class LaunchProfileTypeFromProjectDataProducer : QueryDataFromProviderStateProducerBase +{ + private readonly ILaunchProfileTypePropertiesAvailableStatus _properties; - internal class LaunchProfileTypeFromProjectDataProducer : QueryDataFromProviderStateProducerBase + public LaunchProfileTypeFromProjectDataProducer(ILaunchProfileTypePropertiesAvailableStatus properties) { - private readonly ILaunchProfileTypePropertiesAvailableStatus _properties; + _properties = properties; + } - public LaunchProfileTypeFromProjectDataProducer(ILaunchProfileTypePropertiesAvailableStatus properties) - { - _properties = properties; - } + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, UnconfiguredProject providerState) + { + return CreateLaunchProfileTypeValuesAsync(parent, providerState); + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, UnconfiguredProject providerState) + private async Task> CreateLaunchProfileTypeValuesAsync(IEntityValue parent, UnconfiguredProject project) + { + if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) { - return CreateLaunchProfileTypeValuesAsync(parent, providerState); + return createLaunchProfileTypeValues(); } - private async Task> CreateLaunchProfileTypeValuesAsync(IEntityValue parent, UnconfiguredProject project) - { - if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) - { - return createLaunchProfileTypeValues(); - } + return Enumerable.Empty(); - return Enumerable.Empty(); - - IEnumerable createLaunchProfileTypeValues() + IEnumerable createLaunchProfileTypeValues() + { + foreach (Rule rule in DebugUtilities.GetDebugChildRules(projectCatalog)) { - foreach (Rule rule in DebugUtilities.GetDebugChildRules(projectCatalog)) + if (rule.Metadata.TryGetValue("CommandName", out object? commandNameObj) + && commandNameObj is string commandName) { - if (rule.Metadata.TryGetValue("CommandName", out object? commandNameObj) - && commandNameObj is string commandName) - { - IEntityValue launchProfileTypeValue = CreateLaunchProfileTypeValue(parent, commandName, rule); - yield return launchProfileTypeValue; - } + IEntityValue launchProfileTypeValue = CreateLaunchProfileTypeValue(parent, commandName, rule); + yield return launchProfileTypeValue; } } } + } - private IEntityValue CreateLaunchProfileTypeValue(IEntityValue parent, string commandName, Rule rule) - { - EntityIdentity identity = new( - ((IEntityWithId)parent).Id, - new Dictionary - { - { ProjectModelIdentityKeys.LaunchProfileTypeName, commandName } - }); + private IEntityValue CreateLaunchProfileTypeValue(IEntityValue parent, string commandName, Rule rule) + { + EntityIdentity identity = new( + ((IEntityWithId)parent).Id, + new Dictionary + { + { ProjectModelIdentityKeys.LaunchProfileTypeName, commandName } + }); + + return CreateLaunchProfileTypeValue(parent.EntityRuntime, identity, commandName, rule); + } + + private IEntityValue CreateLaunchProfileTypeValue(IEntityRuntimeModel entityRuntime, EntityIdentity identity, string commandName, Rule rule) + { + LaunchProfileTypeSnapshot newLaunchProfileType = new(entityRuntime, identity, new LaunchProfileTypePropertiesAvailableStatus()); - return CreateLaunchProfileTypeValue(parent.EntityRuntime, identity, commandName, rule); + if (_properties.CommandName) + { + newLaunchProfileType.CommandName = commandName; } - private IEntityValue CreateLaunchProfileTypeValue(IEntityRuntimeModel entityRuntime, EntityIdentity identity, string commandName, Rule rule) + if (_properties.DisplayName) { - LaunchProfileTypeSnapshot newLaunchProfileType = new(entityRuntime, identity, new LaunchProfileTypePropertiesAvailableStatus()); + newLaunchProfileType.DisplayName = rule.DisplayName ?? commandName; + } - if (_properties.CommandName) + if (_properties.HelpUrl) + { + if (rule.Metadata.TryGetValue("HelpUrl", out object? helpUrlObj) + && helpUrlObj is string helpUrlString) { - newLaunchProfileType.CommandName = commandName; + newLaunchProfileType.HelpUrl = helpUrlString; } - - if (_properties.DisplayName) + else { - newLaunchProfileType.DisplayName = rule.DisplayName ?? commandName; + newLaunchProfileType.HelpUrl = string.Empty; } + } - if (_properties.HelpUrl) + if (_properties.ImageMoniker) + { + if (rule.Metadata.TryGetValue("ImageMonikerGuid", out object? imageMonikerGuidObj) + && imageMonikerGuidObj is Guid imageMonikerGuid + && rule.Metadata.TryGetValue("ImageMonikerId", out object? imageMonikerIdObj) + && imageMonikerIdObj is int imageMonikerId) { - if (rule.Metadata.TryGetValue("HelpUrl", out object? helpUrlObj) - && helpUrlObj is string helpUrlString) - { - newLaunchProfileType.HelpUrl = helpUrlString; - } - else - { - newLaunchProfileType.HelpUrl = string.Empty; - } + newLaunchProfileType.ImageMoniker = new ImageMoniker { Guid = imageMonikerGuid, Id = imageMonikerId }; } - - if (_properties.ImageMoniker) + else { - if (rule.Metadata.TryGetValue("ImageMonikerGuid", out object? imageMonikerGuidObj) - && imageMonikerGuidObj is Guid imageMonikerGuid - && rule.Metadata.TryGetValue("ImageMonikerId", out object? imageMonikerIdObj) - && imageMonikerIdObj is int imageMonikerId) - { - newLaunchProfileType.ImageMoniker = new ImageMoniker { Guid = imageMonikerGuid, Id = imageMonikerId }; - } - else - { - newLaunchProfileType.ImageMoniker = KnownMonikers.SettingsGroup; - } + newLaunchProfileType.ImageMoniker = KnownMonikers.SettingsGroup; } - - return newLaunchProfileType; } + + return newLaunchProfileType; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileUIPropertyDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileUIPropertyDataProvider.cs index 78b3f00c12..2b719fed82 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileUIPropertyDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/LaunchProfileUIPropertyDataProvider.cs @@ -6,33 +6,32 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve property information (see +/// ). +/// +/// +/// Responsible for populating . +/// Note this is almost identical to the ; the only reason we have both is that +/// cannot be applied multiple times to the same type, so we +/// can't have one type that handles multiple relationships. +/// +[QueryDataProvider(UIPropertyType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(LaunchProfileType.TypeName, LaunchProfileType.PropertiesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class LaunchProfileUIPropertyDataProvider : QueryDataProviderBase, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve property information (see - /// ). - /// - /// - /// Responsible for populating . - /// Note this is almost identical to the ; the only reason we have both is that - /// cannot be applied multiple times to the same type, so we - /// can't have one type that handles multiple relationships. - /// - [QueryDataProvider(UIPropertyType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(LaunchProfileType.TypeName, LaunchProfileType.PropertiesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class LaunchProfileUIPropertyDataProvider : QueryDataProviderBase, IQueryByRelationshipDataProvider + [ImportingConstructor] + public LaunchProfileUIPropertyDataProvider(IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public LaunchProfileUIPropertyDataProvider(IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIPropertyFromRuleDataProducer((IUIPropertyPropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new UIPropertyFromRuleDataProducer((IUIPropertyPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectActionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectActionProvider.cs index 2085de0f61..e1204fba53 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectActionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectActionProvider.cs @@ -6,44 +6,43 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// +/// Handles Project Query API actions that target the . +/// +/// +/// Specifically, this type is responsible for creating the appropriate +/// for a given , and all further processing is handled by that executor. +/// +/// +[QueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectModel.ModelName)] +[QueryActionProvider(ProjectModelActionNames.SetEvaluatedUIPropertyValue, typeof(SetEvaluatedUIPropertyValue))] +[QueryActionProvider(ProjectModelActionNames.SetUnevaluatedUIPropertyValue, typeof(SetUnevaluatedUIPropertyValue))] +[QueryActionProvider(ProjectModelActionNames.AddLaunchProfile, typeof(AddLaunchProfile))] +[QueryActionProvider(ProjectModelActionNames.RemoveLaunchProfile, typeof(RemoveLaunchProfile))] +[QueryActionProvider(ProjectModelActionNames.RenameLaunchProfile, typeof(RenameLaunchProfile))] +[QueryActionProvider(ProjectModelActionNames.DuplicateLaunchProfile, typeof(DuplicateLaunchProfile))] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryActionProvider))] +internal sealed class ProjectActionProvider : IQueryActionProvider { - /// - /// - /// Handles Project Query API actions that target the . - /// - /// - /// Specifically, this type is responsible for creating the appropriate - /// for a given , and all further processing is handled by that executor. - /// - /// - [QueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectModel.ModelName)] - [QueryActionProvider(ProjectModelActionNames.SetEvaluatedUIPropertyValue, typeof(SetEvaluatedUIPropertyValue))] - [QueryActionProvider(ProjectModelActionNames.SetUnevaluatedUIPropertyValue, typeof(SetUnevaluatedUIPropertyValue))] - [QueryActionProvider(ProjectModelActionNames.AddLaunchProfile, typeof(AddLaunchProfile))] - [QueryActionProvider(ProjectModelActionNames.RemoveLaunchProfile, typeof(RemoveLaunchProfile))] - [QueryActionProvider(ProjectModelActionNames.RenameLaunchProfile, typeof(RenameLaunchProfile))] - [QueryActionProvider(ProjectModelActionNames.DuplicateLaunchProfile, typeof(DuplicateLaunchProfile))] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryActionProvider))] - internal sealed class ProjectActionProvider : IQueryActionProvider + public IQueryActionExecutor CreateQueryActionDataTransformer(ExecutableStep executableStep) { - public IQueryActionExecutor CreateQueryActionDataTransformer(ExecutableStep executableStep) - { - Requires.NotNull(executableStep); + Requires.NotNull(executableStep); - return executableStep.Action switch - { - ProjectModelActionNames.SetEvaluatedUIPropertyValue => new ProjectSetEvaluatedUIPropertyValueAction((SetEvaluatedUIPropertyValue)executableStep), - ProjectModelActionNames.SetUnevaluatedUIPropertyValue => new ProjectSetUnevaluatedUIPropertyValueAction((SetUnevaluatedUIPropertyValue)executableStep), + return executableStep.Action switch + { + ProjectModelActionNames.SetEvaluatedUIPropertyValue => new ProjectSetEvaluatedUIPropertyValueAction((SetEvaluatedUIPropertyValue)executableStep), + ProjectModelActionNames.SetUnevaluatedUIPropertyValue => new ProjectSetUnevaluatedUIPropertyValueAction((SetUnevaluatedUIPropertyValue)executableStep), - ProjectModelActionNames.AddLaunchProfile => new AddLaunchProfileAction((AddLaunchProfile)executableStep), - ProjectModelActionNames.RemoveLaunchProfile => new RemoveLaunchProfileAction((RemoveLaunchProfile)executableStep), - ProjectModelActionNames.RenameLaunchProfile => new RenameLaunchProfileAction((RenameLaunchProfile)executableStep), - ProjectModelActionNames.DuplicateLaunchProfile => new DuplicateLaunchProfileAction((DuplicateLaunchProfile)executableStep), + ProjectModelActionNames.AddLaunchProfile => new AddLaunchProfileAction((AddLaunchProfile)executableStep), + ProjectModelActionNames.RemoveLaunchProfile => new RemoveLaunchProfileAction((RemoveLaunchProfile)executableStep), + ProjectModelActionNames.RenameLaunchProfile => new RenameLaunchProfileAction((RenameLaunchProfile)executableStep), + ProjectModelActionNames.DuplicateLaunchProfile => new DuplicateLaunchProfileAction((DuplicateLaunchProfile)executableStep), - _ => throw new InvalidOperationException($"{nameof(ProjectActionProvider)} does not handle action '{executableStep.Action}'.") - }; - } + _ => throw new InvalidOperationException($"{nameof(ProjectActionProvider)} does not handle action '{executableStep.Action}'.") + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetEvaluatedUIPropertyValueAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetEvaluatedUIPropertyValueAction.cs index fea3cf6ee0..d5c3938560 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetEvaluatedUIPropertyValueAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetEvaluatedUIPropertyValueAction.cs @@ -4,34 +4,33 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// handling actions. +/// +internal sealed class ProjectSetEvaluatedUIPropertyValueAction : ProjectSetUIPropertyValueActionBase { - /// - /// handling actions. - /// - internal sealed class ProjectSetEvaluatedUIPropertyValueAction : ProjectSetUIPropertyValueActionBase + private readonly SetEvaluatedUIPropertyValue _parameter; + + public ProjectSetEvaluatedUIPropertyValueAction(SetEvaluatedUIPropertyValue parameter) + : base(parameter.Page, parameter.Name, parameter.Dimensions) { - private readonly SetEvaluatedUIPropertyValue _parameter; + Requires.NotNull(parameter); + Requires.NotNull(parameter.Dimensions); - public ProjectSetEvaluatedUIPropertyValueAction(SetEvaluatedUIPropertyValue parameter) - : base(parameter.Page, parameter.Name, parameter.Dimensions) - { - Requires.NotNull(parameter); - Requires.NotNull(parameter.Dimensions); + _parameter = parameter; + } - _parameter = parameter; + protected override Task SetValueAsync(IProperty property) + { + if (_parameter.Value is null) + { + return property.DeleteAsync(); } - - protected override Task SetValueAsync(IProperty property) + else { - if (_parameter.Value is null) - { - return property.DeleteAsync(); - } - else - { - return property.SetValueAsync(_parameter.Value); - } + return property.SetValueAsync(_parameter.Value); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionBase.cs index a6f4afeac8..fb4951f2e8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionBase.cs @@ -6,77 +6,76 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// This type, along with its derived types, serves as an intermediary between the core layers of the +/// Project Query API in CPS and the core logic for setting properties which is handled by . +/// Responsible for extracting the necessary data from the Project Query API types and delegating work +/// to the . +/// +/// +/// See also: and +/// . +/// +/// +/// The type of the property to set: for setting evaluated property values, and +/// for setting unevaluated property values. +/// +internal abstract class ProjectSetUIPropertyValueActionBase : QueryDataProducerBase, IProjectUpdateActionExecutor, IQueryActionExecutor { - /// - /// This type, along with its derived types, serves as an intermediary between the core layers of the - /// Project Query API in CPS and the core logic for setting properties which is handled by . - /// Responsible for extracting the necessary data from the Project Query API types and delegating work - /// to the . - /// - /// - /// See also: and - /// . - /// - /// - /// The type of the property to set: for setting evaluated property values, and - /// for setting unevaluated property values. - /// - internal abstract class ProjectSetUIPropertyValueActionBase : QueryDataProducerBase, IProjectUpdateActionExecutor, IQueryActionExecutor - { - private readonly ProjectSetUIPropertyValueActionCore _coreExecutor; + private readonly ProjectSetUIPropertyValueActionCore _coreExecutor; - public ProjectSetUIPropertyValueActionBase( - string pageName, - string propertyName, - ReadOnlyCollection dimensions) - { - _coreExecutor = new ProjectSetUIPropertyValueActionCore( - pageName, - propertyName, - dimensions.Select(d => (d.Dimension, d.Value)), - SetValueAsync); - } + public ProjectSetUIPropertyValueActionBase( + string pageName, + string propertyName, + ReadOnlyCollection dimensions) + { + _coreExecutor = new ProjectSetUIPropertyValueActionCore( + pageName, + propertyName, + dimensions.Select(d => (d.Dimension, d.Value)), + SetValueAsync); + } - public Task OnBeforeExecutingBatchAsync(IReadOnlyList> allItems, CancellationToken cancellationToken) - { - Requires.NotNull(allItems); + public Task OnBeforeExecutingBatchAsync(IReadOnlyList> allItems, CancellationToken cancellationToken) + { + Requires.NotNull(allItems); - IEnumerable targetProjects = allItems - .Select(item => ((IEntityValueFromProvider)item.Result).ProviderState) - .OfType(); + IEnumerable targetProjects = allItems + .Select(item => ((IEntityValueFromProvider)item.Result).ProviderState) + .OfType(); - return _coreExecutor.OnBeforeExecutingBatchAsync(targetProjects); - } + return _coreExecutor.OnBeforeExecutingBatchAsync(targetProjects); + } - public async Task ReceiveResultAsync(QueryProcessResult result) + public async Task ReceiveResultAsync(QueryProcessResult result) + { + Requires.NotNull(result); + result.Request.QueryExecutionContext.CancellationToken.ThrowIfCancellationRequested(); + if (((IEntityValueFromProvider)result.Result).ProviderState is UnconfiguredProject project) { - Requires.NotNull(result); - result.Request.QueryExecutionContext.CancellationToken.ThrowIfCancellationRequested(); - if (((IEntityValueFromProvider)result.Result).ProviderState is UnconfiguredProject project) + if (await _coreExecutor.ExecuteAsync(project)) { - if (await _coreExecutor.ExecuteAsync(project)) - { - project.GetQueryDataVersion(out string versionKey, out long versionNumber); - result.Request.QueryExecutionContext.ReportUpdatedDataVersion(versionKey, versionNumber); - } + project.GetQueryDataVersion(out string versionKey, out long versionNumber); + result.Request.QueryExecutionContext.ReportUpdatedDataVersion(versionKey, versionNumber); } - - await ResultReceiver.ReceiveResultAsync(result); } - public Task OnRequestProcessFinishedAsync(IQueryProcessRequest request) - { - _coreExecutor.OnAfterExecutingBatch(); - return ResultReceiver.OnRequestProcessFinishedAsync(request); - } + await ResultReceiver.ReceiveResultAsync(result); + } - /// - /// Sets the value on the given . - /// - /// - /// Abstract because we need different logic for setting evaluated and unevaluated values. - /// - protected abstract Task SetValueAsync(IProperty property); + public Task OnRequestProcessFinishedAsync(IQueryProcessRequest request) + { + _coreExecutor.OnAfterExecutingBatch(); + return ResultReceiver.OnRequestProcessFinishedAsync(request); } + + /// + /// Sets the value on the given . + /// + /// + /// Abstract because we need different logic for setting evaluated and unevaluated values. + /// + protected abstract Task SetValueAsync(IProperty property); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionCore.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionCore.cs index 0f6b1607c6..d7086def70 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionCore.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUIPropertyValueActionCore.cs @@ -3,133 +3,132 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// +/// Handles the core logic of setting properties on projects. Note this type has no dependencies on the Project Query API; +/// extracting the necessary data from the API is handled by . +/// +/// +/// This handles setting a specific property on a specific page across multiple configurations of multiple projects. +/// +/// +internal class ProjectSetUIPropertyValueActionCore { + private readonly string _pageName; + private readonly string _propertyName; + private readonly IEnumerable<(string dimension, string value)> _dimensions; + private readonly Func _setValueAsync; + + private readonly Dictionary> _rules = new(StringComparers.Paths); + /// - /// - /// Handles the core logic of setting properties on projects. Note this type has no dependencies on the Project Query API; - /// extracting the necessary data from the API is handled by . - /// - /// - /// This handles setting a specific property on a specific page across multiple configurations of multiple projects. - /// + /// Creates a . /// - internal class ProjectSetUIPropertyValueActionCore + /// The name of the page containing the property. + /// The name of the property to update. + /// The dimension names and values indicating which project configurations should be updated with the new value. + /// A delegate that, given the to update, actually sets the value. + public ProjectSetUIPropertyValueActionCore( + string pageName, + string propertyName, + IEnumerable<(string dimension, string value)> dimensions, + Func setValueAsync) { - private readonly string _pageName; - private readonly string _propertyName; - private readonly IEnumerable<(string dimension, string value)> _dimensions; - private readonly Func _setValueAsync; - - private readonly Dictionary> _rules = new(StringComparers.Paths); - - /// - /// Creates a . - /// - /// The name of the page containing the property. - /// The name of the property to update. - /// The dimension names and values indicating which project configurations should be updated with the new value. - /// A delegate that, given the to update, actually sets the value. - public ProjectSetUIPropertyValueActionCore( - string pageName, - string propertyName, - IEnumerable<(string dimension, string value)> dimensions, - Func setValueAsync) - { - _pageName = pageName; - _propertyName = propertyName; - _dimensions = dimensions; - _setValueAsync = setValueAsync; - } + _pageName = pageName; + _propertyName = propertyName; + _dimensions = dimensions; + _setValueAsync = setValueAsync; + } - /// - /// Handles any pre-processing that should occur before actually setting the property values. - /// This is called once before . - /// - /// - /// Because of the project locks help by the core parts of the Project Query API in CPS we need - /// to retrieve and cache all of the affected s ahead of time. - /// - /// The set of projects we should try to update. - public async Task OnBeforeExecutingBatchAsync(IEnumerable targetProjects) + /// + /// Handles any pre-processing that should occur before actually setting the property values. + /// This is called once before . + /// + /// + /// Because of the project locks help by the core parts of the Project Query API in CPS we need + /// to retrieve and cache all of the affected s ahead of time. + /// + /// The set of projects we should try to update. + public async Task OnBeforeExecutingBatchAsync(IEnumerable targetProjects) + { + foreach (UnconfiguredProject project in targetProjects) { - foreach (UnconfiguredProject project in targetProjects) + if (!_rules.TryGetValue(project.FullPath, out List projectRules) + && await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog + && projectCatalog.GetSchema(_pageName) is Rule rule + && rule.GetProperty(_propertyName) is BaseProperty property) { - if (!_rules.TryGetValue(project.FullPath, out List projectRules) - && await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog - && projectCatalog.GetSchema(_pageName) is Rule rule - && rule.GetProperty(_propertyName) is BaseProperty property) + bool configurationDependent = property.IsConfigurationDependent(); + projectRules = new List(); + IProjectState projectState = new PropertyPageProjectState(project); + if (configurationDependent) { - bool configurationDependent = property.IsConfigurationDependent(); - projectRules = new List(); - IProjectState projectState = new PropertyPageProjectState(project); - if (configurationDependent) + // The property is configuration-dependent; we need to collect the bound rules for + // all matching configurations. + if (await projectState.GetKnownConfigurationsAsync() is IImmutableSet knownConfigurations) { - // The property is configuration-dependent; we need to collect the bound rules for - // all matching configurations. - if (await projectState.GetKnownConfigurationsAsync() is IImmutableSet knownConfigurations) + foreach (ProjectConfiguration knownConfiguration in knownConfigurations) { - foreach (ProjectConfiguration knownConfiguration in knownConfigurations) + if (knownConfiguration.MatchesDimensions(_dimensions) + && await projectState.BindToRuleAsync(knownConfiguration, _pageName, QueryProjectPropertiesContext.ProjectFile) is IRule boundRule) { - if (knownConfiguration.MatchesDimensions(_dimensions) - && await projectState.BindToRuleAsync(knownConfiguration, _pageName, QueryProjectPropertiesContext.ProjectFile) is IRule boundRule) - { - projectRules.Add(boundRule); - } + projectRules.Add(boundRule); } } } - else + } + else + { + // The property is configuration-independent; we only need the bound rule for a single + // configuration. + if (await projectState.GetSuggestedConfigurationAsync() is ProjectConfiguration suggestedConfiguration + && await projectState.BindToRuleAsync(suggestedConfiguration, _pageName, QueryProjectPropertiesContext.ProjectFile) is IRule boundRule) { - // The property is configuration-independent; we only need the bound rule for a single - // configuration. - if (await projectState.GetSuggestedConfigurationAsync() is ProjectConfiguration suggestedConfiguration - && await projectState.BindToRuleAsync(suggestedConfiguration, _pageName, QueryProjectPropertiesContext.ProjectFile) is IRule boundRule) - { - projectRules.Add(boundRule); - } + projectRules.Add(boundRule); } - - _rules.Add(project.FullPath, projectRules); } + + _rules.Add(project.FullPath, projectRules); } } + } - /// - /// Handles setting the property value within a single project. This is called once per - /// targeted by the query. - /// - /// The project to update. - /// - /// if at least one property was actually updated; - /// otherwise. - /// - public async Task ExecuteAsync(UnconfiguredProject targetProject) + /// + /// Handles setting the property value within a single project. This is called once per + /// targeted by the query. + /// + /// The project to update. + /// + /// if at least one property was actually updated; + /// otherwise. + /// + public async Task ExecuteAsync(UnconfiguredProject targetProject) + { + bool valueSet = false; + if (_rules.TryGetValue(targetProject.FullPath, out List boundRules)) { - bool valueSet = false; - if (_rules.TryGetValue(targetProject.FullPath, out List boundRules)) + foreach (IRule boundRule in boundRules) { - foreach (IRule boundRule in boundRules) + if (boundRule.GetProperty(_propertyName) is IProperty property) { - if (boundRule.GetProperty(_propertyName) is IProperty property) - { - await _setValueAsync(property); + await _setValueAsync(property); - valueSet = true; - } + valueSet = true; } } - - return valueSet; } - /// - /// Handles clean up when we're all done executing the project action. This is called - /// once after all calls to have completed. - /// - public void OnAfterExecutingBatch() - { - _rules.Clear(); - } + return valueSet; + } + + /// + /// Handles clean up when we're all done executing the project action. This is called + /// once after all calls to have completed. + /// + public void OnAfterExecutingBatch() + { + _rules.Clear(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUnevaluatedUIPropertyValueAction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUnevaluatedUIPropertyValueAction.cs index 5577e83782..4dff4b6a8e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUnevaluatedUIPropertyValueAction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/ProjectSetUnevaluatedUIPropertyValueAction.cs @@ -4,34 +4,33 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework.Actions; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// handling actions. +/// +internal sealed class ProjectSetUnevaluatedUIPropertyValueAction : ProjectSetUIPropertyValueActionBase { - /// - /// handling actions. - /// - internal sealed class ProjectSetUnevaluatedUIPropertyValueAction : ProjectSetUIPropertyValueActionBase + private readonly SetUnevaluatedUIPropertyValue _parameter; + + public ProjectSetUnevaluatedUIPropertyValueAction(SetUnevaluatedUIPropertyValue parameter) + : base(parameter.Page, parameter.Name, parameter.Dimensions) { - private readonly SetUnevaluatedUIPropertyValue _parameter; + Requires.NotNull(parameter); + Requires.NotNull(parameter.Dimensions); - public ProjectSetUnevaluatedUIPropertyValueAction(SetUnevaluatedUIPropertyValue parameter) - : base(parameter.Page, parameter.Name, parameter.Dimensions) - { - Requires.NotNull(parameter); - Requires.NotNull(parameter.Dimensions); + _parameter = parameter; + } - _parameter = parameter; + protected override Task SetValueAsync(IProperty property) + { + if (_parameter.Value is null) + { + return property.DeleteAsync(); } - - protected override Task SetValueAsync(IProperty property) + else { - if (_parameter.Value is null) - { - return property.DeleteAsync(); - } - else - { - return property.SetValueAsync(_parameter.Value); - } + return property.SetValueAsync(_parameter.Value); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs index 0372b1a62f..362698cf2b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageByIdDataProducer.cs @@ -4,41 +4,40 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving an based on an ID. +/// +internal class PropertyPageByIdDataProducer : QueryDataByIdProducerBase { - /// - /// Handles retrieving an based on an ID. - /// - internal class PropertyPageByIdDataProducer : QueryDataByIdProducerBase + private readonly IPropertyPagePropertiesAvailableStatus _properties; + private readonly IProjectService2 _projectService; + + public PropertyPageByIdDataProducer(IPropertyPagePropertiesAvailableStatus properties, IProjectService2 projectService) { - private readonly IPropertyPagePropertiesAvailableStatus _properties; - private readonly IProjectService2 _projectService; + Requires.NotNull(properties); + Requires.NotNull(projectService); - public PropertyPageByIdDataProducer(IPropertyPagePropertiesAvailableStatus properties, IProjectService2 projectService) - { - Requires.NotNull(properties); - Requires.NotNull(projectService); + _properties = properties; + _projectService = projectService; + } - _properties = properties; - _projectService = projectService; + protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) + { + if (QueryProjectPropertiesContext.TryCreateFromEntityId(id, out QueryProjectPropertiesContext? propertiesContext) + && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName)) + { + return PropertyPageDataProducer.CreatePropertyPageValueAsync( + queryExecutionContext, + id, + _projectService, + propertiesContext, + propertyPageName, + _properties); } - protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) - { - if (QueryProjectPropertiesContext.TryCreateFromEntityId(id, out QueryProjectPropertiesContext? propertiesContext) - && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName)) - { - return PropertyPageDataProducer.CreatePropertyPageValueAsync( - queryExecutionContext, - id, - _projectService, - propertiesContext, - propertyPageName, - _properties); - } - - return NullEntityValue; + return NullEntityValue; - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducer.cs index e4b127c767..6a9b162d67 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducer.cs @@ -6,132 +6,131 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class PropertyPageDataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class PropertyPageDataProducer + public static IEntityValue CreatePropertyPageValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, IProjectState cache, QueryProjectPropertiesContext propertiesContext, Rule rule, IPropertyPagePropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreatePropertyPageValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, IProjectState cache, QueryProjectPropertiesContext propertiesContext, Rule rule, IPropertyPagePropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(parent); - Requires.NotNull(rule); - - var identity = new EntityIdentity( - ((IEntityWithId)parent).Id, - createKeys()); + Requires.NotNull(parent); + Requires.NotNull(rule); - return CreatePropertyPageValue(queryExecutionContext, identity, cache, propertiesContext, rule, requestedProperties); + var identity = new EntityIdentity( + ((IEntityWithId)parent).Id, + createKeys()); - IEnumerable> createKeys() - { - yield return new(ProjectModelIdentityKeys.PropertyPageName, rule.Name); - - if (propertiesContext.ItemType is not null) - { - yield return new(ProjectModelIdentityKeys.SourceItemType, propertiesContext.ItemType); - } - - if (propertiesContext.ItemName is not null) - { - yield return new(ProjectModelIdentityKeys.SourceItemName, propertiesContext.ItemName); - } - } - } + return CreatePropertyPageValue(queryExecutionContext, identity, cache, propertiesContext, rule, requestedProperties); - public static IEntityValue CreatePropertyPageValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, IProjectState cache, QueryProjectPropertiesContext propertiesContext, Rule rule, IPropertyPagePropertiesAvailableStatus requestedProperties) + IEnumerable> createKeys() { - Requires.NotNull(rule); - var newPropertyPage = new PropertyPageSnapshot(queryExecutionContext.EntityRuntime, id, new PropertyPagePropertiesAvailableStatus()); + yield return new(ProjectModelIdentityKeys.PropertyPageName, rule.Name); - if (requestedProperties.Name) + if (propertiesContext.ItemType is not null) { - newPropertyPage.Name = rule.Name; + yield return new(ProjectModelIdentityKeys.SourceItemType, propertiesContext.ItemType); } - if (requestedProperties.DisplayName) + if (propertiesContext.ItemName is not null) { - newPropertyPage.DisplayName = rule.DisplayName; + yield return new(ProjectModelIdentityKeys.SourceItemName, propertiesContext.ItemName); } + } + } - if (requestedProperties.Order) - { - newPropertyPage.Order = rule.Order; - } + public static IEntityValue CreatePropertyPageValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, IProjectState cache, QueryProjectPropertiesContext propertiesContext, Rule rule, IPropertyPagePropertiesAvailableStatus requestedProperties) + { + Requires.NotNull(rule); + var newPropertyPage = new PropertyPageSnapshot(queryExecutionContext.EntityRuntime, id, new PropertyPagePropertiesAvailableStatus()); - if (requestedProperties.Kind) - { - newPropertyPage.Kind = rule.PageTemplate; - } + if (requestedProperties.Name) + { + newPropertyPage.Name = rule.Name; + } - ((IEntityValueFromProvider)newPropertyPage).ProviderState = new ContextAndRuleProviderState(cache, propertiesContext, rule); + if (requestedProperties.DisplayName) + { + newPropertyPage.DisplayName = rule.DisplayName; + } - return newPropertyPage; + if (requestedProperties.Order) + { + newPropertyPage.Order = rule.Order; } - public static async Task CreatePropertyPageValueAsync( - IQueryExecutionContext queryExecutionContext, - EntityIdentity id, - IProjectService2 projectService, - QueryProjectPropertiesContext propertiesContext, - string propertyPageName, - IPropertyPagePropertiesAvailableStatus requestedProperties) + if (requestedProperties.Kind) { - if (projectService.GetLoadedProject(propertiesContext.File) is UnconfiguredProject project) - { - // TODO: Go through the IProjectState to get this - project.GetQueryDataVersion(out string versionKey, out long versionNumber); - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); + newPropertyPage.Kind = rule.PageTemplate; + } - if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog - && projectCatalog.GetSchema(propertyPageName) is { PropertyPagesHidden: false } rule) - { - IProjectState projectState = new PropertyPageProjectState(project); - IEntityValue propertyPageValue = CreatePropertyPageValue(queryExecutionContext, id, projectState, propertiesContext, rule, requestedProperties); - return propertyPageValue; - } - } + ((IEntityValueFromProvider)newPropertyPage).ProviderState = new ContextAndRuleProviderState(cache, propertiesContext, rule); - return null; - } + return newPropertyPage; + } - public static async Task> CreatePropertyPageValuesAsync( - IQueryExecutionContext queryExecutionContext, - IEntityValue parent, - UnconfiguredProject project, - IPropertyPagePropertiesAvailableStatus requestedProperties) + public static async Task CreatePropertyPageValueAsync( + IQueryExecutionContext queryExecutionContext, + EntityIdentity id, + IProjectService2 projectService, + QueryProjectPropertiesContext propertiesContext, + string propertyPageName, + IPropertyPagePropertiesAvailableStatus requestedProperties) + { + if (projectService.GetLoadedProject(propertiesContext.File) is UnconfiguredProject project) { - if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) + // TODO: Go through the IProjectState to get this + project.GetQueryDataVersion(out string versionKey, out long versionNumber); + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); + + if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog + && projectCatalog.GetSchema(propertyPageName) is { PropertyPagesHidden: false } rule) { - return createPropertyPageValues(); + IProjectState projectState = new PropertyPageProjectState(project); + IEntityValue propertyPageValue = CreatePropertyPageValue(queryExecutionContext, id, projectState, propertiesContext, rule, requestedProperties); + return propertyPageValue; } + } - return Enumerable.Empty(); + return null; + } - IEnumerable createPropertyPageValues() + public static async Task> CreatePropertyPageValuesAsync( + IQueryExecutionContext queryExecutionContext, + IEntityValue parent, + UnconfiguredProject project, + IPropertyPagePropertiesAvailableStatus requestedProperties) + { + if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog) + { + return createPropertyPageValues(); + } + + return Enumerable.Empty(); + + IEnumerable createPropertyPageValues() + { + IProjectState projectState = new PropertyPageProjectState(project); + QueryProjectPropertiesContext propertiesContext = new QueryProjectPropertiesContext(isProjectFile: true, project.FullPath, itemType: null, itemName: null); + foreach (string schemaName in projectCatalog.GetProjectLevelPropertyPagesSchemas()) { - IProjectState projectState = new PropertyPageProjectState(project); - QueryProjectPropertiesContext propertiesContext = new QueryProjectPropertiesContext(isProjectFile: true, project.FullPath, itemType: null, itemName: null); - foreach (string schemaName in projectCatalog.GetProjectLevelPropertyPagesSchemas()) + if (projectCatalog.GetSchema(schemaName) is { PropertyPagesHidden: false } rule) { - if (projectCatalog.GetSchema(schemaName) is { PropertyPagesHidden: false } rule) + if (rule.Name is "RazorGeneral" or "RazorExtension") { - if (rule.Name is "RazorGeneral" or "RazorExtension") - { - // Some versions of the .NET SDK include a Razor property page that appears - // in the UI. This page is not intended for display. - // - // We cannot remove this page from existing versions of the SDK, so have to - // explicitly exclude it from query results so that it doesn't appear in any - // UI. - - continue; - } - - IEntityValue propertyPageValue = CreatePropertyPageValue(queryExecutionContext, parent, projectState, propertiesContext, rule, requestedProperties: requestedProperties); - yield return propertyPageValue; + // Some versions of the .NET SDK include a Razor property page that appears + // in the UI. This page is not intended for display. + // + // We cannot remove this page from existing versions of the SDK, so have to + // explicitly exclude it from query results so that it doesn't appear in any + // UI. + + continue; } + + IEntityValue propertyPageValue = CreatePropertyPageValue(queryExecutionContext, parent, projectState, propertiesContext, rule, requestedProperties: requestedProperties); + yield return propertyPageValue; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProvider.cs index 9288df78fe..cc853306e1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProvider.cs @@ -6,37 +6,36 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve property page information +/// () for a project. +/// +/// +/// Responsible for populating . Can also retrieve a +/// based on its ID. +/// +[QueryDataProvider(PropertyPageType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectSystem.Query.Metadata.ProjectType.PropertyPagesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByIdDataProvider))] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class PropertyPageDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve property page information - /// () for a project. - /// - /// - /// Responsible for populating . Can also retrieve a - /// based on its ID. - /// - [QueryDataProvider(PropertyPageType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(ProjectSystem.Query.Metadata.ProjectType.TypeName, ProjectSystem.Query.Metadata.ProjectType.PropertyPagesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByIdDataProvider))] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class PropertyPageDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider + [ImportingConstructor] + public PropertyPageDataProvider(IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public PropertyPageDataProvider(IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new PropertyPageByIdDataProducer((IPropertyPagePropertiesAvailableStatus)properties, ProjectService); - } + public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new PropertyPageByIdDataProducer((IPropertyPagePropertiesAvailableStatus)properties, ProjectService); + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new PropertyPageFromProjectDataProducer((IPropertyPagePropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new PropertyPageFromProjectDataProducer((IPropertyPagePropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageFromProjectDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageFromProjectDataProducer.cs index 27f463e875..69a0c11333 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageFromProjectDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageFromProjectDataProducer.cs @@ -3,26 +3,25 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an . +/// +internal class PropertyPageFromProjectDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an . - /// - internal class PropertyPageFromProjectDataProducer : QueryDataFromProviderStateProducerBase - { - private readonly IPropertyPagePropertiesAvailableStatus _properties; + private readonly IPropertyPagePropertiesAvailableStatus _properties; - public PropertyPageFromProjectDataProducer(IPropertyPagePropertiesAvailableStatus properties) - { - _properties = properties; - } + public PropertyPageFromProjectDataProducer(IPropertyPagePropertiesAvailableStatus properties) + { + _properties = properties; + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, UnconfiguredProject providerState) - { - providerState.GetQueryDataVersion(out string versionKey, out long versionNumber); - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, UnconfiguredProject providerState) + { + providerState.GetQueryDataVersion(out string versionKey, out long versionNumber); + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); - return PropertyPageDataProducer.CreatePropertyPageValuesAsync(queryExecutionContext, parent, providerState, _properties); - } + return PropertyPageDataProducer.CreatePropertyPageValuesAsync(queryExecutionContext, parent, providerState, _properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageProjectState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageProjectState.cs index 120d393b2e..a9a822a589 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageProjectState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageProjectState.cs @@ -2,43 +2,42 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +internal class PropertyPageProjectState : AbstractProjectState { - internal class PropertyPageProjectState : AbstractProjectState + public PropertyPageProjectState(UnconfiguredProject project) + : base(project) { - public PropertyPageProjectState(UnconfiguredProject project) - : base(project) - { - } + } - public override async Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration) - { - ConfiguredProject configuredProject = await Project.LoadConfiguredProjectAsync(configuration); - configuredProject.GetQueryDataVersion(out string versionKey, out long versionNumber); - return (versionKey, versionNumber); - } + public override async Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration) + { + ConfiguredProject configuredProject = await Project.LoadConfiguredProjectAsync(configuration); + configuredProject.GetQueryDataVersion(out string versionKey, out long versionNumber); + return (versionKey, versionNumber); } +} - internal class LaunchProfileProjectState : AbstractProjectState +internal class LaunchProfileProjectState : AbstractProjectState +{ + private readonly ILaunchSettingsProvider _launchSettingsProvider; + private readonly LaunchSettingsTracker _launchSettingsTracker; + + public LaunchProfileProjectState(UnconfiguredProject project, ILaunchSettingsProvider launchSettingsProvider, LaunchSettingsTracker launchSettingsTracker) + : base(project) { - private readonly ILaunchSettingsProvider _launchSettingsProvider; - private readonly LaunchSettingsTracker _launchSettingsTracker; + _launchSettingsProvider = launchSettingsProvider; + _launchSettingsTracker = launchSettingsTracker; + } - public LaunchProfileProjectState(UnconfiguredProject project, ILaunchSettingsProvider launchSettingsProvider, LaunchSettingsTracker launchSettingsTracker) - : base(project) + public override Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration) + { + if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) { - _launchSettingsProvider = launchSettingsProvider; - _launchSettingsTracker = launchSettingsTracker; + return Task.FromResult<(string, long)?>((_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version)); } - public override Task<(string versionKey, long versionNumber)?> GetDataVersionAsync(ProjectConfiguration configuration) - { - if (_launchSettingsProvider.CurrentSnapshot is IVersionedLaunchSettings versionedLaunchSettings) - { - return Task.FromResult<(string, long)?>((_launchSettingsTracker.VersionKey, versionedLaunchSettings.Version)); - } - - return Task.FromResult<(string, long)?>(null); - } + return Task.FromResult<(string, long)?>(null); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageQueryExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageQueryExtensions.cs index 2abe0562d8..1eb4442a28 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageQueryExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyPageQueryExtensions.cs @@ -5,77 +5,76 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Simplifies some operations that are common across the property page query data providers. +/// +internal static class PropertyPageQueryExtensions { /// - /// Simplifies some operations that are common across the property page query data providers. + /// Retrieves the project-level for an . /// - internal static class PropertyPageQueryExtensions + public static async Task GetProjectLevelPropertyPagesCatalogAsync(this UnconfiguredProject project) { - /// - /// Retrieves the project-level for an . - /// - public static async Task GetProjectLevelPropertyPagesCatalogAsync(this UnconfiguredProject project) + if (await project.GetSuggestedConfiguredProjectAsync() is ConfiguredProject configuredProject) { - if (await project.GetSuggestedConfiguredProjectAsync() is ConfiguredProject configuredProject) - { - return await configuredProject.GetProjectLevelPropertyPagesCatalogAsync(); - } - - return null; + return await configuredProject.GetProjectLevelPropertyPagesCatalogAsync(); } - /// - /// Retrieves the project-level for a . - /// - public static async Task GetProjectLevelPropertyPagesCatalogAsync(this ConfiguredProject project) - { - if (project.Services.PropertyPagesCatalog is IPropertyPagesCatalogProvider catalogProvider) - { - return await catalogProvider.GetCatalogAsync(PropertyPageContexts.Project); - } + return null; + } - return null; + /// + /// Retrieves the project-level for a . + /// + public static async Task GetProjectLevelPropertyPagesCatalogAsync(this ConfiguredProject project) + { + if (project.Services.PropertyPagesCatalog is IPropertyPagesCatalogProvider catalogProvider) + { + return await catalogProvider.GetCatalogAsync(PropertyPageContexts.Project); } - /// - /// Retrieves the of the given , as well as the index - /// of the property within its . - /// - public static bool TryGetPropertyAndIndex(this Rule rule, string propertyName, [NotNullWhen(true)] out BaseProperty? property, out int index) + return null; + } + + /// + /// Retrieves the of the given , as well as the index + /// of the property within its . + /// + public static bool TryGetPropertyAndIndex(this Rule rule, string propertyName, [NotNullWhen(true)] out BaseProperty? property, out int index) + { + foreach ((int i, BaseProperty prop) in rule.Properties.WithIndices()) { - foreach ((int i, BaseProperty prop) in rule.Properties.WithIndices()) + if (StringComparers.PropertyNames.Equals(prop.Name, propertyName)) { - if (StringComparers.PropertyNames.Equals(prop.Name, propertyName)) - { - property = prop; - index = i; - return true; - } + property = prop; + index = i; + return true; } - - property = null; - index = default; - return false; } - /// - /// Checks if a given matches a set of configuration dimensions and values. - /// A is considered a match if it contains each of the supplied dimensions, - /// and the values match. All s match the empty set of dimensions and values. - /// - public static bool MatchesDimensions(this ProjectConfiguration configuration, IEnumerable<(string dimension, string value)> targetDimensionsAndValues) + property = null; + index = default; + return false; + } + + /// + /// Checks if a given matches a set of configuration dimensions and values. + /// A is considered a match if it contains each of the supplied dimensions, + /// and the values match. All s match the empty set of dimensions and values. + /// + public static bool MatchesDimensions(this ProjectConfiguration configuration, IEnumerable<(string dimension, string value)> targetDimensionsAndValues) + { + foreach ((string dimension, string value) in targetDimensionsAndValues) { - foreach ((string dimension, string value) in targetDimensionsAndValues) + if (!configuration.Dimensions.TryGetValue(dimension, out string projectDimensionValue) + || !StringComparer.Ordinal.Equals(value, projectDimensionValue)) { - if (!configuration.Dimensions.TryGetValue(dimension, out string projectDimensionValue) - || !StringComparer.Ordinal.Equals(value, projectDimensionValue)) - { - return false; - } + return false; } - - return true; } + + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyProviderState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyProviderState.cs index 3eaab579af..c2c1a06f36 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyProviderState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyProviderState.cs @@ -3,25 +3,24 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Holds the state we need to pass from producers of instances +/// to other producers that will create the s' child entities. +/// +internal sealed class PropertyProviderState { - /// - /// Holds the state we need to pass from producers of instances - /// to other producers that will create the s' child entities. - /// - internal sealed class PropertyProviderState + public PropertyProviderState(IProjectState projectState, Rule containingRule, QueryProjectPropertiesContext propertiesContext, string propertyName) { - public PropertyProviderState(IProjectState projectState, Rule containingRule, QueryProjectPropertiesContext propertiesContext, string propertyName) - { - ProjectState = projectState; - ContainingRule = containingRule; - PropertiesContext = propertiesContext; - PropertyName = propertyName; - } - - public IProjectState ProjectState { get; } - public Rule ContainingRule { get; } - public QueryProjectPropertiesContext PropertiesContext { get; } - public string PropertyName { get; } + ProjectState = projectState; + ContainingRule = containingRule; + PropertiesContext = propertiesContext; + PropertyName = propertyName; } + + public IProjectState ProjectState { get; } + public Rule ContainingRule { get; } + public QueryProjectPropertiesContext PropertiesContext { get; } + public string PropertyName { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyValueProviderState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyValueProviderState.cs index e58f17a8b5..1f6146e77b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyValueProviderState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/PropertyValueProviderState.cs @@ -2,21 +2,20 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Holds the state we need to pass from producers of instances +/// to other producers that will create the s' child entities. +/// +internal sealed class PropertyValueProviderState { - /// - /// Holds the state we need to pass from producers of instances - /// to other producers that will create the s' child entities. - /// - internal sealed class PropertyValueProviderState + public PropertyValueProviderState(ProjectConfiguration projectConfiguration, ProjectSystem.Properties.IProperty property) { - public PropertyValueProviderState(ProjectConfiguration projectConfiguration, ProjectSystem.Properties.IProperty property) - { - ProjectConfiguration = projectConfiguration; - Property = property; - } - - public ProjectConfiguration ProjectConfiguration { get; } - public ProjectSystem.Properties.IProperty Property { get; } + ProjectConfiguration = projectConfiguration; + Property = property; } + + public ProjectConfiguration ProjectConfiguration { get; } + public ProjectSystem.Properties.IProperty Property { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs index 37b965581c..6383525c0a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/QueryProjectPropertiesContext.cs @@ -5,97 +5,96 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// An implementation of for use in the +/// Project Query API implementation. +/// +/// +/// The point here is to capture the context for a part of a query execution in a +/// manner that can be passed from one provider to another and is also suitable as a +/// key into a cache (such as the ). +/// +internal sealed class QueryProjectPropertiesContext : IProjectPropertiesContext, IEquatable { /// - /// An implementation of for use in the - /// Project Query API implementation. + /// A well-known context representing the project file as a whole. /// /// - /// The point here is to capture the context for a part of a query execution in a - /// manner that can be passed from one provider to another and is also suitable as a - /// key into a cache (such as the ). + /// Note that if an has the + /// property is set to true and the + /// and properties are + /// then the properties system treats it as referring to the project file as a whole + /// regardless of the property. This + /// lets us get away with setting it to the empty string and re-using the same + /// instance across projects. /// - internal sealed class QueryProjectPropertiesContext : IProjectPropertiesContext, IEquatable + public static readonly QueryProjectPropertiesContext ProjectFile = new(isProjectFile: true, file: string.Empty, itemType: null, itemName: null); + + public QueryProjectPropertiesContext(bool isProjectFile, string file, string? itemType, string? itemName) { - /// - /// A well-known context representing the project file as a whole. - /// - /// - /// Note that if an has the - /// property is set to true and the - /// and properties are - /// then the properties system treats it as referring to the project file as a whole - /// regardless of the property. This - /// lets us get away with setting it to the empty string and re-using the same - /// instance across projects. - /// - public static readonly QueryProjectPropertiesContext ProjectFile = new(isProjectFile: true, file: string.Empty, itemType: null, itemName: null); - - public QueryProjectPropertiesContext(bool isProjectFile, string file, string? itemType, string? itemName) - { - IsProjectFile = isProjectFile; - File = file; - ItemType = itemType; - ItemName = itemName; - } + IsProjectFile = isProjectFile; + File = file; + ItemType = itemType; + ItemName = itemName; + } - public bool IsProjectFile { get; } + public bool IsProjectFile { get; } - public string File { get; } + public string File { get; } - public string? ItemType { get; } + public string? ItemType { get; } - public string? ItemName { get; } + public string? ItemName { get; } - public bool Equals(QueryProjectPropertiesContext? other) - { - return other is not null - && IsProjectFile == other.IsProjectFile - && StringComparers.Paths.Equals(File, other.File) - && StringComparers.ItemTypes.Equals(ItemType, other.ItemType) - && StringComparers.ItemNames.Equals(ItemName, other.ItemName); - } + public bool Equals(QueryProjectPropertiesContext? other) + { + return other is not null + && IsProjectFile == other.IsProjectFile + && StringComparers.Paths.Equals(File, other.File) + && StringComparers.ItemTypes.Equals(ItemType, other.ItemType) + && StringComparers.ItemNames.Equals(ItemName, other.ItemName); + } + + public override bool Equals(object obj) + { + return Equals(obj as QueryProjectPropertiesContext); + } - public override bool Equals(object obj) + public override int GetHashCode() + { + int hashCode = IsProjectFile.GetHashCode(); + hashCode = (hashCode * -1521134295) + StringComparers.Paths.GetHashCode(File); + + if (ItemType is not null) { - return Equals(obj as QueryProjectPropertiesContext); + hashCode = (hashCode * -1521134295) + StringComparers.ItemTypes.GetHashCode(ItemType); } - public override int GetHashCode() + if (ItemName is not null) { - int hashCode = IsProjectFile.GetHashCode(); - hashCode = (hashCode * -1521134295) + StringComparers.Paths.GetHashCode(File); - - if (ItemType is not null) - { - hashCode = (hashCode * -1521134295) + StringComparers.ItemTypes.GetHashCode(ItemType); - } - - if (ItemName is not null) - { - hashCode = (hashCode * -1521134295) + StringComparers.ItemNames.GetHashCode(ItemName); - } - - return hashCode; + hashCode = (hashCode * -1521134295) + StringComparers.ItemNames.GetHashCode(ItemName); } - /// - /// Creates a from a Project Query API - /// . - /// - public static bool TryCreateFromEntityId(EntityIdentity id, [NotNullWhen(true)] out QueryProjectPropertiesContext? propertiesContext) + return hashCode; + } + + /// + /// Creates a from a Project Query API + /// . + /// + public static bool TryCreateFromEntityId(EntityIdentity id, [NotNullWhen(true)] out QueryProjectPropertiesContext? propertiesContext) + { + if (id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath)) { - if (id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath)) - { - id.TryGetValue(ProjectModelIdentityKeys.SourceItemType, out string? itemType); - id.TryGetValue(ProjectModelIdentityKeys.SourceItemName, out string? itemName); - propertiesContext = new QueryProjectPropertiesContext(isProjectFile: true, projectPath, itemType, itemName); - return true; - } - - propertiesContext = null; - return false; + id.TryGetValue(ProjectModelIdentityKeys.SourceItemType, out string? itemType); + id.TryGetValue(ProjectModelIdentityKeys.SourceItemName, out string? itemName); + propertiesContext = new QueryProjectPropertiesContext(isProjectFile: true, projectPath, itemType, itemName); + return true; } + + propertiesContext = null; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducer.cs index d037d0d522..27cf7013a4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducer.cs @@ -4,48 +4,47 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class SupportedValueDataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class SupportedValueDataProducer + public static IEntityValue CreateSupportedValue(IEntityRuntimeModel runtimeModel, ProjectSystem.Properties.IEnumValue enumValue, ISupportedValuePropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreateSupportedValue(IEntityRuntimeModel runtimeModel, ProjectSystem.Properties.IEnumValue enumValue, ISupportedValuePropertiesAvailableStatus requestedProperties) - { - var newSupportedValue = new SupportedValueSnapshot(runtimeModel, new SupportedValuePropertiesAvailableStatus()); - - if (requestedProperties.DisplayName) - { - newSupportedValue.DisplayName = enumValue.DisplayName; - } + var newSupportedValue = new SupportedValueSnapshot(runtimeModel, new SupportedValuePropertiesAvailableStatus()); - if (requestedProperties.Value) - { - newSupportedValue.Value = enumValue.Name; - } + if (requestedProperties.DisplayName) + { + newSupportedValue.DisplayName = enumValue.DisplayName; + } - return newSupportedValue; + if (requestedProperties.Value) + { + newSupportedValue.Value = enumValue.Name; } - public static async Task> CreateSupportedValuesAsync(IEntityValue parent, ProjectSystem.Properties.IProperty property, ISupportedValuePropertiesAvailableStatus requestedProperties) + return newSupportedValue; + } + + public static async Task> CreateSupportedValuesAsync(IEntityValue parent, ProjectSystem.Properties.IProperty property, ISupportedValuePropertiesAvailableStatus requestedProperties) + { + if (property is ProjectSystem.Properties.IEnumProperty enumProperty) { - if (property is ProjectSystem.Properties.IEnumProperty enumProperty) - { - ReadOnlyCollection enumValues = await enumProperty.GetAdmissibleValuesAsync(); + ReadOnlyCollection enumValues = await enumProperty.GetAdmissibleValuesAsync(); - return createSupportedValues(enumValues); - } + return createSupportedValues(enumValues); + } - return Enumerable.Empty(); + return Enumerable.Empty(); - IEnumerable createSupportedValues(ReadOnlyCollection enumValues) + IEnumerable createSupportedValues(ReadOnlyCollection enumValues) + { + foreach (ProjectSystem.Properties.IEnumValue value in enumValues) { - foreach (ProjectSystem.Properties.IEnumValue value in enumValues) - { - IEntityValue supportedValue = CreateSupportedValue(parent.EntityRuntime, value, requestedProperties); - yield return supportedValue; - } + IEntityValue supportedValue = CreateSupportedValue(parent.EntityRuntime, value, requestedProperties); + yield return supportedValue; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProvider.cs index 428407afa3..775bf6c920 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProvider.cs @@ -6,24 +6,23 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve a property's supported +/// values (). +/// ' +/// +/// Responsible for populating . +/// +[QueryDataProvider(SupportedValueType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(UIPropertyValueType.TypeName, UIPropertyValueType.SupportedValuesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class SupportedValueDataProvider : IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve a property's supported - /// values (). - /// ' - /// - /// Responsible for populating . - /// - [QueryDataProvider(SupportedValueType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(UIPropertyValueType.TypeName, UIPropertyValueType.SupportedValuesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class SupportedValueDataProvider : IQueryByRelationshipDataProvider + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) { - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new SupportedValueFromPropertyDataProducer((ISupportedValuePropertiesAvailableStatus)properties); - } + return new SupportedValueFromPropertyDataProducer((ISupportedValuePropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueFromPropertyDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueFromPropertyDataProducer.cs index 202f0a7a21..930512640f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueFromPropertyDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/SupportedValueFromPropertyDataProducer.cs @@ -3,23 +3,22 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an . +/// +internal class SupportedValueFromPropertyDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an . - /// - internal class SupportedValueFromPropertyDataProducer : QueryDataFromProviderStateProducerBase - { - private readonly ISupportedValuePropertiesAvailableStatus _properties; + private readonly ISupportedValuePropertiesAvailableStatus _properties; - public SupportedValueFromPropertyDataProducer(ISupportedValuePropertiesAvailableStatus properties) - { - _properties = properties; - } + public SupportedValueFromPropertyDataProducer(ISupportedValuePropertiesAvailableStatus properties) + { + _properties = properties; + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyValueProviderState providerState) - { - return SupportedValueDataProducer.CreateSupportedValuesAsync(parent, providerState.Property, _properties); - } + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyValueProviderState providerState) + { + return SupportedValueDataProducer.CreateSupportedValuesAsync(parent, providerState.Property, _properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataDataProvider.cs index 7896194535..0d51f333a0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataDataProvider.cs @@ -6,26 +6,25 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// MEF entry point that creates instances to retrieve property +/// value editor metadata (see ). +/// +/// +/// Responsible for populating Metadata. See and for the important +/// logic. +/// +[QueryDataProvider(UIEditorMetadataType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(UIPropertyEditorType.TypeName, UIPropertyEditorType.MetadataPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class UIEditorMetadataDataProvider : IQueryByRelationshipDataProvider { - /// - /// MEF entry point that creates instances to retrieve property - /// value editor metadata (see ). - /// - /// - /// Responsible for populating Metadata. See and for the important - /// logic. - /// - [QueryDataProvider(UIEditorMetadataType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(UIPropertyEditorType.TypeName, UIPropertyEditorType.MetadataPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class UIEditorMetadataDataProvider : IQueryByRelationshipDataProvider + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) { - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIEditorMetadataFromValueEditorProducer((IUIEditorMetadataPropertiesAvailableStatus)properties); - } + return new UIEditorMetadataFromValueEditorProducer((IUIEditorMetadataPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataFromValueEditorProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataFromValueEditorProducer.cs index 6abbb7089b..f1b2cec570 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataFromValueEditorProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataFromValueEditorProducer.cs @@ -4,27 +4,26 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of from a and reporting the +/// results. +/// +/// +/// The comes from the parent +/// +internal class UIEditorMetadataFromValueEditorProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of from a and reporting the - /// results. - /// - /// - /// The comes from the parent - /// - internal class UIEditorMetadataFromValueEditorProducer : QueryDataFromProviderStateProducerBase - { - private readonly IUIEditorMetadataPropertiesAvailableStatus _properties; + private readonly IUIEditorMetadataPropertiesAvailableStatus _properties; - public UIEditorMetadataFromValueEditorProducer(IUIEditorMetadataPropertiesAvailableStatus properties) - { - _properties = properties; - } + public UIEditorMetadataFromValueEditorProducer(IUIEditorMetadataPropertiesAvailableStatus properties) + { + _properties = properties; + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ValueEditor providerState) - { - return Task.FromResult(UIEditorMetadataProducer.CreateMetadataValues(parent, providerState, _properties)); - } + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ValueEditor providerState) + { + return Task.FromResult(UIEditorMetadataProducer.CreateMetadataValues(parent, providerState, _properties)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducer.cs index d595488436..5f4affd8bb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducer.cs @@ -4,37 +4,36 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class UIEditorMetadataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class UIEditorMetadataProducer + public static IEntityValue CreateMetadataValue(IEntityRuntimeModel runtimeModel, NameValuePair metadata, IUIEditorMetadataPropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreateMetadataValue(IEntityRuntimeModel runtimeModel, NameValuePair metadata, IUIEditorMetadataPropertiesAvailableStatus requestedProperties) - { - var newMetadata = new UIEditorMetadataSnapshot(runtimeModel, new UIEditorMetadataPropertiesAvailableStatus()); - - if (requestedProperties.Name) - { - newMetadata.Name = metadata.Name; - } + var newMetadata = new UIEditorMetadataSnapshot(runtimeModel, new UIEditorMetadataPropertiesAvailableStatus()); - if (requestedProperties.Value) - { - newMetadata.Value = metadata.Value; - } + if (requestedProperties.Name) + { + newMetadata.Name = metadata.Name; + } - return newMetadata; + if (requestedProperties.Value) + { + newMetadata.Value = metadata.Value; } - public static IEnumerable CreateMetadataValues(IEntityValue parent, ValueEditor editor, IUIEditorMetadataPropertiesAvailableStatus requestedProperties) + return newMetadata; + } + + public static IEnumerable CreateMetadataValues(IEntityValue parent, ValueEditor editor, IUIEditorMetadataPropertiesAvailableStatus requestedProperties) + { + foreach (NameValuePair metadataPair in editor.Metadata) { - foreach (NameValuePair metadataPair in editor.Metadata) - { - IEntityValue metadataValue = CreateMetadataValue(parent.EntityRuntime, metadataPair, requestedProperties); - yield return metadataValue; - } + IEntityValue metadataValue = CreateMetadataValue(parent.EntityRuntime, metadataPair, requestedProperties); + yield return metadataValue; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyByIdProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyByIdProducer.cs index ab1083a3f3..a70668712c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyByIdProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyByIdProducer.cs @@ -4,41 +4,40 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving an based on an ID. +/// +internal class UIPropertyByIdProducer : QueryDataByIdProducerBase { - /// - /// Handles retrieving an based on an ID. - /// - internal class UIPropertyByIdProducer : QueryDataByIdProducerBase + private readonly IUIPropertyPropertiesAvailableStatus _properties; + private readonly IProjectService2 _projectService; + + public UIPropertyByIdProducer(IUIPropertyPropertiesAvailableStatus properties, IProjectService2 projectService) { - private readonly IUIPropertyPropertiesAvailableStatus _properties; - private readonly IProjectService2 _projectService; + Requires.NotNull(properties); + Requires.NotNull(projectService); + _properties = properties; + _projectService = projectService; + } - public UIPropertyByIdProducer(IUIPropertyPropertiesAvailableStatus properties, IProjectService2 projectService) + protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) + { + if (QueryProjectPropertiesContext.TryCreateFromEntityId(id, out QueryProjectPropertiesContext? propertiesContext) + && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName) + && id.TryGetValue(ProjectModelIdentityKeys.UIPropertyName, out string? propertyName)) { - Requires.NotNull(properties); - Requires.NotNull(projectService); - _properties = properties; - _projectService = projectService; + return UIPropertyDataProducer.CreateUIPropertyValueAsync( + queryExecutionContext, + id, + _projectService, + propertiesContext, + propertyPageName, + propertyName, + _properties); } - protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) - { - if (QueryProjectPropertiesContext.TryCreateFromEntityId(id, out QueryProjectPropertiesContext? propertiesContext) - && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName) - && id.TryGetValue(ProjectModelIdentityKeys.UIPropertyName, out string? propertyName)) - { - return UIPropertyDataProducer.CreateUIPropertyValueAsync( - queryExecutionContext, - id, - _projectService, - propertiesContext, - propertyPageName, - propertyName, - _properties); - } - - return NullEntityValue; - } + return NullEntityValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs index ae700c577f..d4102f5853 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducer.cs @@ -8,183 +8,182 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Framework; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class UIPropertyDataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class UIPropertyDataProducer + public static IEntityValue CreateUIPropertyValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, IProjectState cache, QueryProjectPropertiesContext propertiesContext, BaseProperty property, int order, IUIPropertyPropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreateUIPropertyValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, IProjectState cache, QueryProjectPropertiesContext propertiesContext, BaseProperty property, int order, IUIPropertyPropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(parent); - Requires.NotNull(property); + Requires.NotNull(parent); + Requires.NotNull(property); - var identity = new EntityIdentity( - ((IEntityWithId)parent).Id, - new KeyValuePair[] - { - new(ProjectModelIdentityKeys.UIPropertyName, property.Name) - }); + var identity = new EntityIdentity( + ((IEntityWithId)parent).Id, + new KeyValuePair[] + { + new(ProjectModelIdentityKeys.UIPropertyName, property.Name) + }); - return CreateUIPropertyValue(queryExecutionContext, identity, cache, propertiesContext, property, order, requestedProperties); - } + return CreateUIPropertyValue(queryExecutionContext, identity, cache, propertiesContext, property, order, requestedProperties); + } - public static IEntityValue CreateUIPropertyValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, IProjectState cache, QueryProjectPropertiesContext propertiesContext, BaseProperty property, int order, IUIPropertyPropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(property); - var newUIProperty = new UIPropertySnapshot(queryExecutionContext.EntityRuntime, id, new UIPropertyPropertiesAvailableStatus()); + public static IEntityValue CreateUIPropertyValue(IQueryExecutionContext queryExecutionContext, EntityIdentity id, IProjectState cache, QueryProjectPropertiesContext propertiesContext, BaseProperty property, int order, IUIPropertyPropertiesAvailableStatus requestedProperties) + { + Requires.NotNull(property); + var newUIProperty = new UIPropertySnapshot(queryExecutionContext.EntityRuntime, id, new UIPropertyPropertiesAvailableStatus()); - if (requestedProperties.Name) - { - newUIProperty.Name = property.Name; - } + if (requestedProperties.Name) + { + newUIProperty.Name = property.Name; + } - if (requestedProperties.DisplayName) - { - newUIProperty.DisplayName = property.DisplayName; - } + if (requestedProperties.DisplayName) + { + newUIProperty.DisplayName = property.DisplayName; + } - if (requestedProperties.Description) - { - newUIProperty.Description = property.Description; - } + if (requestedProperties.Description) + { + newUIProperty.Description = property.Description; + } - if (requestedProperties.ConfigurationIndependent) - { - newUIProperty.ConfigurationIndependent = !property.IsConfigurationDependent(); - } + if (requestedProperties.ConfigurationIndependent) + { + newUIProperty.ConfigurationIndependent = !property.IsConfigurationDependent(); + } - if (requestedProperties.IsReadOnly) - { - newUIProperty.IsReadOnly = property.ReadOnly; - } + if (requestedProperties.IsReadOnly) + { + newUIProperty.IsReadOnly = property.ReadOnly; + } - if (requestedProperties.IsVisible) - { - newUIProperty.IsVisible = property.Visible; - } + if (requestedProperties.IsVisible) + { + newUIProperty.IsVisible = property.Visible; + } - if (requestedProperties.HelpUrl) - { - newUIProperty.HelpUrl = property.HelpUrl; - } + if (requestedProperties.HelpUrl) + { + newUIProperty.HelpUrl = property.HelpUrl; + } - if (requestedProperties.CategoryName) - { - newUIProperty.CategoryName = property.Category; - } + if (requestedProperties.CategoryName) + { + newUIProperty.CategoryName = property.Category; + } - if (requestedProperties.Order) - { - newUIProperty.Order = order; - } + if (requestedProperties.Order) + { + newUIProperty.Order = order; + } - if (requestedProperties.Type) - { - newUIProperty.Type = property switch - { - IntProperty => "int", - BoolProperty => "bool", - EnumProperty => "enum", - DynamicEnumProperty => "enum", - StringListProperty => "list", - _ => "string" - }; - } + if (requestedProperties.Type) + { + newUIProperty.Type = property switch + { + IntProperty => "int", + BoolProperty => "bool", + EnumProperty => "enum", + DynamicEnumProperty => "enum", + StringListProperty => "list", + _ => "string" + }; + } - if (requestedProperties.SearchTerms) - { - string? searchTermsString = property.GetMetadataValueOrNull("SearchTerms"); - newUIProperty.SearchTerms = searchTermsString ?? string.Empty; - } + if (requestedProperties.SearchTerms) + { + string? searchTermsString = property.GetMetadataValueOrNull("SearchTerms"); + newUIProperty.SearchTerms = searchTermsString ?? string.Empty; + } - if (requestedProperties.DependsOn) - { - string? dependsOnString = property.GetMetadataValueOrNull("DependsOn"); - newUIProperty.DependsOn = dependsOnString ?? string.Empty; - } + if (requestedProperties.DependsOn) + { + string? dependsOnString = property.GetMetadataValueOrNull("DependsOn"); + newUIProperty.DependsOn = dependsOnString ?? string.Empty; + } - if (requestedProperties.VisibilityCondition) - { - string? visibilityCondition = property.GetMetadataValueOrNull("VisibilityCondition"); - newUIProperty.VisibilityCondition = visibilityCondition ?? string.Empty; - } + if (requestedProperties.VisibilityCondition) + { + string? visibilityCondition = property.GetMetadataValueOrNull("VisibilityCondition"); + newUIProperty.VisibilityCondition = visibilityCondition ?? string.Empty; + } - if (requestedProperties.DimensionVisibilityCondition) - { - string? dimensionVisibilityCondition = property.GetMetadataValueOrNull("DimensionVisibilityCondition"); - newUIProperty.DimensionVisibilityCondition = dimensionVisibilityCondition ?? string.Empty; - } + if (requestedProperties.DimensionVisibilityCondition) + { + string? dimensionVisibilityCondition = property.GetMetadataValueOrNull("DimensionVisibilityCondition"); + newUIProperty.DimensionVisibilityCondition = dimensionVisibilityCondition ?? string.Empty; + } - if (requestedProperties.ConfiguredValueVisibilityCondition) - { - string? configuredValueVisibilityCondition = property.GetMetadataValueOrNull("ConfiguredValueVisibilityCondition"); - newUIProperty.ConfiguredValueVisibilityCondition = configuredValueVisibilityCondition ?? string.Empty; - } + if (requestedProperties.ConfiguredValueVisibilityCondition) + { + string? configuredValueVisibilityCondition = property.GetMetadataValueOrNull("ConfiguredValueVisibilityCondition"); + newUIProperty.ConfiguredValueVisibilityCondition = configuredValueVisibilityCondition ?? string.Empty; + } - if (requestedProperties.IsReadOnlyCondition) - { - string? isReadOnlyCondition = property.GetMetadataValueOrNull("IsReadOnlyCondition"); - newUIProperty.IsReadOnlyCondition = isReadOnlyCondition ?? string.Empty; - } + if (requestedProperties.IsReadOnlyCondition) + { + string? isReadOnlyCondition = property.GetMetadataValueOrNull("IsReadOnlyCondition"); + newUIProperty.IsReadOnlyCondition = isReadOnlyCondition ?? string.Empty; + } - ((IEntityValueFromProvider)newUIProperty).ProviderState = new PropertyProviderState(cache, property.ContainingRule, propertiesContext, property.Name); + ((IEntityValueFromProvider)newUIProperty).ProviderState = new PropertyProviderState(cache, property.ContainingRule, propertiesContext, property.Name); - return newUIProperty; - } + return newUIProperty; + } - public static IEnumerable CreateUIPropertyValues(IQueryExecutionContext queryExecutionContext, IEntityValue parent, IProjectState cache, QueryProjectPropertiesContext propertiesContext, Rule rule, IUIPropertyPropertiesAvailableStatus properties) + public static IEnumerable CreateUIPropertyValues(IQueryExecutionContext queryExecutionContext, IEntityValue parent, IProjectState cache, QueryProjectPropertiesContext propertiesContext, Rule rule, IUIPropertyPropertiesAvailableStatus properties) + { + foreach ((int index, BaseProperty property) in rule.Properties.WithIndices()) { - foreach ((int index, BaseProperty property) in rule.Properties.WithIndices()) - { - IEntityValue propertyValue = CreateUIPropertyValue(queryExecutionContext, parent, cache, propertiesContext, property, index, properties); - yield return propertyValue; - } + IEntityValue propertyValue = CreateUIPropertyValue(queryExecutionContext, parent, cache, propertiesContext, property, index, properties); + yield return propertyValue; } + } - public static async Task CreateUIPropertyValueAsync( - IQueryExecutionContext queryExecutionContext, - EntityIdentity requestId, - IProjectService2 projectService, - QueryProjectPropertiesContext propertiesContext, - string propertyPageName, - string propertyName, - IUIPropertyPropertiesAvailableStatus requestedProperties) + public static async Task CreateUIPropertyValueAsync( + IQueryExecutionContext queryExecutionContext, + EntityIdentity requestId, + IProjectService2 projectService, + QueryProjectPropertiesContext propertiesContext, + string propertyPageName, + string propertyName, + IUIPropertyPropertiesAvailableStatus requestedProperties) + { + if (projectService.GetLoadedProject(propertiesContext.File) is UnconfiguredProject project) { - if (projectService.GetLoadedProject(propertiesContext.File) is UnconfiguredProject project) - { - project.GetQueryDataVersion(out string versionKey, out long versionNumber); - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); + project.GetQueryDataVersion(out string versionKey, out long versionNumber); + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); - if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog - && projectCatalog.GetSchema(propertyPageName) is Rule rule - && rule.TryGetPropertyAndIndex(propertyName, out BaseProperty? property, out int index) - && property.Visible) + if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog + && projectCatalog.GetSchema(propertyPageName) is Rule rule + && rule.TryGetPropertyAndIndex(propertyName, out BaseProperty? property, out int index) + && property.Visible) + { + IProjectState? projectState = null; + if (StringComparers.ItemTypes.Equals(propertiesContext.ItemType, "LaunchProfile")) { - IProjectState? projectState = null; - if (StringComparers.ItemTypes.Equals(propertiesContext.ItemType, "LaunchProfile")) - { - if (project.Services.ExportProvider.GetExportedValueOrDefault() is ILaunchSettingsProvider launchSettingsProvider - && project.Services.ExportProvider.GetExportedValueOrDefault() is LaunchSettingsTracker launchSettingsTracker) - { - projectState = new LaunchProfileProjectState(project, launchSettingsProvider, launchSettingsTracker); - } - } - else if (propertiesContext.IsProjectFile) + if (project.Services.ExportProvider.GetExportedValueOrDefault() is ILaunchSettingsProvider launchSettingsProvider + && project.Services.ExportProvider.GetExportedValueOrDefault() is LaunchSettingsTracker launchSettingsTracker) { - projectState = new PropertyPageProjectState(project); + projectState = new LaunchProfileProjectState(project, launchSettingsProvider, launchSettingsTracker); } + } + else if (propertiesContext.IsProjectFile) + { + projectState = new PropertyPageProjectState(project); + } - if (projectState is not null) - { - IEntityValue propertyValue = CreateUIPropertyValue(queryExecutionContext, requestId, projectState, propertiesContext, property, index, requestedProperties); - return propertyValue; - } + if (projectState is not null) + { + IEntityValue propertyValue = CreateUIPropertyValue(queryExecutionContext, requestId, projectState, propertiesContext, property, index, requestedProperties); + return propertyValue; } } - - return null; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProvider.cs index 6261645659..f6c31280f5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProvider.cs @@ -6,40 +6,39 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve property information (see +/// ). +/// +/// +/// Responsible for populating . Can also retrieve a +/// based on its ID. +/// Note this is almost identical to the ; the only reason we have +/// both is that cannot be applied multiple times to the same +/// type, so we can't have one type that handles multiple relationships. +/// +[QueryDataProvider(UIPropertyType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(PropertyPageType.TypeName, PropertyPageType.PropertiesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByIdDataProvider))] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class UIPropertyDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve property information (see - /// ). - /// - /// - /// Responsible for populating . Can also retrieve a - /// based on its ID. - /// Note this is almost identical to the ; the only reason we have - /// both is that cannot be applied multiple times to the same - /// type, so we can't have one type that handles multiple relationships. - /// - [QueryDataProvider(UIPropertyType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(PropertyPageType.TypeName, PropertyPageType.PropertiesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByIdDataProvider))] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class UIPropertyDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider + [ImportingConstructor] + public UIPropertyDataProvider(IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public UIPropertyDataProvider(IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIPropertyByIdProducer((IUIPropertyPropertiesAvailableStatus)properties, ProjectService); - } + public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new UIPropertyByIdProducer((IUIPropertyPropertiesAvailableStatus)properties, ProjectService); + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIPropertyFromRuleDataProducer((IUIPropertyPropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new UIPropertyFromRuleDataProducer((IUIPropertyPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorByIdDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorByIdDataProducer.cs index e321abbf26..0b7631c95e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorByIdDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorByIdDataProducer.cs @@ -4,44 +4,43 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving an base on an ID. +/// +internal class UIPropertyEditorByIdDataProducer : QueryDataByIdProducerBase { - /// - /// Handles retrieving an base on an ID. - /// - internal class UIPropertyEditorByIdDataProducer : QueryDataByIdProducerBase + private readonly IUIPropertyEditorPropertiesAvailableStatus _properties; + private readonly IProjectService2 _projectService; + + public UIPropertyEditorByIdDataProducer(IUIPropertyEditorPropertiesAvailableStatus properties, IProjectService2 projectService) { - private readonly IUIPropertyEditorPropertiesAvailableStatus _properties; - private readonly IProjectService2 _projectService; + Requires.NotNull(properties); + Requires.NotNull(projectService); + _properties = properties; + _projectService = projectService; + } - public UIPropertyEditorByIdDataProducer(IUIPropertyEditorPropertiesAvailableStatus properties, IProjectService2 projectService) + protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) + { + if (id.KeysCount == 4 + && id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath) + && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName) + && id.TryGetValue(ProjectModelIdentityKeys.UIPropertyName, out string? propertyName) + && id.TryGetValue(ProjectModelIdentityKeys.EditorName, out string? editorName)) { - Requires.NotNull(properties); - Requires.NotNull(projectService); - _properties = properties; - _projectService = projectService; + return UIPropertyEditorDataProducer.CreateEditorValueAsync( + queryExecutionContext, + id, + _projectService, + projectPath, + propertyPageName, + propertyName, + editorName, + _properties); } - protected override Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id) - { - if (id.KeysCount == 4 - && id.TryGetValue(ProjectModelIdentityKeys.ProjectPath, out string? projectPath) - && id.TryGetValue(ProjectModelIdentityKeys.PropertyPageName, out string? propertyPageName) - && id.TryGetValue(ProjectModelIdentityKeys.UIPropertyName, out string? propertyName) - && id.TryGetValue(ProjectModelIdentityKeys.EditorName, out string? editorName)) - { - return UIPropertyEditorDataProducer.CreateEditorValueAsync( - queryExecutionContext, - id, - _projectService, - projectPath, - propertyPageName, - propertyName, - editorName, - _properties); - } - - return NullEntityValue; - } + return NullEntityValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducer.cs index c9cfdf6523..434292f57f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducer.cs @@ -6,97 +6,96 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class UIPropertyEditorDataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class UIPropertyEditorDataProducer + public static IEntityValue CreateEditorValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ValueEditor editor, IUIPropertyEditorPropertiesAvailableStatus requestedProperties) { - public static IEntityValue CreateEditorValue(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ValueEditor editor, IUIPropertyEditorPropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(parent); - Requires.NotNull(editor); + Requires.NotNull(parent); + Requires.NotNull(editor); - var identity = new EntityIdentity( - ((IEntityWithId)parent).Id, - new KeyValuePair[] - { - new(ProjectModelIdentityKeys.EditorName, editor.EditorType) - }); + var identity = new EntityIdentity( + ((IEntityWithId)parent).Id, + new KeyValuePair[] + { + new(ProjectModelIdentityKeys.EditorName, editor.EditorType) + }); - return CreateEditorValue(queryExecutionContext, identity, editor, requestedProperties); - } + return CreateEditorValue(queryExecutionContext, identity, editor, requestedProperties); + } - public static IEntityValue CreateEditorValue(IQueryExecutionContext queryExecutionContext, EntityIdentity identity, ValueEditor editor, IUIPropertyEditorPropertiesAvailableStatus requestedProperties) + public static IEntityValue CreateEditorValue(IQueryExecutionContext queryExecutionContext, EntityIdentity identity, ValueEditor editor, IUIPropertyEditorPropertiesAvailableStatus requestedProperties) + { + Requires.NotNull(editor); + var newEditorValue = new UIPropertyEditorSnapshot(queryExecutionContext.EntityRuntime, identity, new UIPropertyEditorPropertiesAvailableStatus()); + + if (requestedProperties.Name) { - Requires.NotNull(editor); - var newEditorValue = new UIPropertyEditorSnapshot(queryExecutionContext.EntityRuntime, identity, new UIPropertyEditorPropertiesAvailableStatus()); + newEditorValue.Name = editor.EditorType; + } - if (requestedProperties.Name) - { - newEditorValue.Name = editor.EditorType; - } + ((IEntityValueFromProvider)newEditorValue).ProviderState = editor; - ((IEntityValueFromProvider)newEditorValue).ProviderState = editor; + return newEditorValue; + } - return newEditorValue; + public static IEnumerable CreateEditorValues(IQueryExecutionContext queryExecutionContext, IEntityValue parent, Rule schema, string propertyName, IUIPropertyEditorPropertiesAvailableStatus properties) + { + BaseProperty? property = schema.GetProperty(propertyName); + if (property is not null) + { + return createEditorValues(); } - public static IEnumerable CreateEditorValues(IQueryExecutionContext queryExecutionContext, IEntityValue parent, Rule schema, string propertyName, IUIPropertyEditorPropertiesAvailableStatus properties) + return Enumerable.Empty(); + + IEnumerable createEditorValues() { - BaseProperty? property = schema.GetProperty(propertyName); - if (property is not null) + foreach (ValueEditor editor in property.ValueEditors) { - return createEditorValues(); + IEntityValue editorValue = CreateEditorValue(queryExecutionContext, parent, editor, properties); + yield return editorValue; } - return Enumerable.Empty(); - - IEnumerable createEditorValues() + if (property is StringProperty stringProperty) { - foreach (ValueEditor editor in property.ValueEditors) + if (string.Equals(stringProperty.Subtype, "file", StringComparison.OrdinalIgnoreCase)) { - IEntityValue editorValue = CreateEditorValue(queryExecutionContext, parent, editor, properties); - yield return editorValue; + yield return CreateEditorValue(queryExecutionContext, parent, new ValueEditor { EditorType = "FilePath" }, properties); } - - if (property is StringProperty stringProperty) + else if ( + string.Equals(stringProperty.Subtype, "folder", StringComparison.OrdinalIgnoreCase) || + string.Equals(stringProperty.Subtype, "directory", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(stringProperty.Subtype, "file", StringComparison.OrdinalIgnoreCase)) - { - yield return CreateEditorValue(queryExecutionContext, parent, new ValueEditor { EditorType = "FilePath" }, properties); - } - else if ( - string.Equals(stringProperty.Subtype, "folder", StringComparison.OrdinalIgnoreCase) || - string.Equals(stringProperty.Subtype, "directory", StringComparison.OrdinalIgnoreCase)) - { - yield return CreateEditorValue(queryExecutionContext, parent, new ValueEditor { EditorType = "DirectoryPath" }, properties); - } + yield return CreateEditorValue(queryExecutionContext, parent, new ValueEditor { EditorType = "DirectoryPath" }, properties); } } } + } - public static async Task CreateEditorValueAsync( - IQueryExecutionContext queryExecutionContext, - EntityIdentity requestId, - IProjectService2 projectService, - string projectPath, - string propertyPageName, - string propertyName, - string editorName, - IUIPropertyEditorPropertiesAvailableStatus properties) + public static async Task CreateEditorValueAsync( + IQueryExecutionContext queryExecutionContext, + EntityIdentity requestId, + IProjectService2 projectService, + string projectPath, + string propertyPageName, + string propertyName, + string editorName, + IUIPropertyEditorPropertiesAvailableStatus properties) + { + if (projectService.GetLoadedProject(projectPath) is UnconfiguredProject project + && await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog + && projectCatalog.GetSchema(propertyPageName) is Rule rule + && rule.GetProperty(propertyName) is BaseProperty property + && property.ValueEditors.FirstOrDefault(ed => string.Equals(ed.EditorType, editorName, StringComparison.Ordinal)) is ValueEditor editor) { - if (projectService.GetLoadedProject(projectPath) is UnconfiguredProject project - && await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog - && projectCatalog.GetSchema(propertyPageName) is Rule rule - && rule.GetProperty(propertyName) is BaseProperty property - && property.ValueEditors.FirstOrDefault(ed => string.Equals(ed.EditorType, editorName, StringComparison.Ordinal)) is ValueEditor editor) - { - IEntityValue editorValue = CreateEditorValue(queryExecutionContext, requestId, editor, properties); - } - - return null; + IEntityValue editorValue = CreateEditorValue(queryExecutionContext, requestId, editor, properties); } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProvider.cs index 8271266fd4..317ecf9652 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProvider.cs @@ -6,37 +6,36 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve UI property value editors +/// (). +/// +/// +/// Responsible for populating . Can also retrieve a based on its ID. +/// +[QueryDataProvider(UIPropertyEditorType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(UIPropertyType.TypeName, UIPropertyType.EditorsPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByIdDataProvider))] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class UIPropertyEditorDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve UI property value editors - /// (). - /// - /// - /// Responsible for populating . Can also retrieve a based on its ID. - /// - [QueryDataProvider(UIPropertyEditorType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(UIPropertyType.TypeName, UIPropertyType.EditorsPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByIdDataProvider))] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class UIPropertyEditorDataProvider : QueryDataProviderBase, IQueryByIdDataProvider, IQueryByRelationshipDataProvider + [ImportingConstructor] + public UIPropertyEditorDataProvider(IProjectServiceAccessor projectServiceAccessor) + : base(projectServiceAccessor) { - [ImportingConstructor] - public UIPropertyEditorDataProvider(IProjectServiceAccessor projectServiceAccessor) - : base(projectServiceAccessor) - { - } + } - public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIPropertyEditorByIdDataProducer((IUIPropertyEditorPropertiesAvailableStatus)properties, ProjectService); - } + public IQueryDataProducer, IEntityValue> CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new UIPropertyEditorByIdDataProducer((IUIPropertyEditorPropertiesAvailableStatus)properties, ProjectService); + } - IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIPropertyEditorFromUIPropertyDataProducer((IUIPropertyEditorPropertiesAvailableStatus)properties); - } + IQueryDataProducer IQueryByRelationshipDataProvider.CreateQueryDataSource(IPropertiesAvailableStatus properties) + { + return new UIPropertyEditorFromUIPropertyDataProducer((IUIPropertyEditorPropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorFromUIPropertyDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorFromUIPropertyDataProducer.cs index 740daf639b..8ad2512310 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorFromUIPropertyDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorFromUIPropertyDataProducer.cs @@ -3,23 +3,22 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an . +/// +internal class UIPropertyEditorFromUIPropertyDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an . - /// - internal class UIPropertyEditorFromUIPropertyDataProducer : QueryDataFromProviderStateProducerBase - { - private readonly IUIPropertyEditorPropertiesAvailableStatus _properties; + private readonly IUIPropertyEditorPropertiesAvailableStatus _properties; - public UIPropertyEditorFromUIPropertyDataProducer(IUIPropertyEditorPropertiesAvailableStatus properties) - { - _properties = properties; - } + public UIPropertyEditorFromUIPropertyDataProducer(IUIPropertyEditorPropertiesAvailableStatus properties) + { + _properties = properties; + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyProviderState providerState) - { - return Task.FromResult(UIPropertyEditorDataProducer.CreateEditorValues(queryExecutionContext, parent, providerState.ContainingRule, providerState.PropertyName, _properties)); - } + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyProviderState providerState) + { + return Task.FromResult(UIPropertyEditorDataProducer.CreateEditorValues(queryExecutionContext, parent, providerState.ContainingRule, providerState.PropertyName, _properties)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyFromRuleDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyFromRuleDataProducer.cs index a959064054..7e7343e2ae 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyFromRuleDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyFromRuleDataProducer.cs @@ -4,29 +4,28 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an +/// or . +/// +internal class UIPropertyFromRuleDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an - /// or . - /// - internal class UIPropertyFromRuleDataProducer : QueryDataFromProviderStateProducerBase + private readonly IUIPropertyPropertiesAvailableStatus _properties; + + public UIPropertyFromRuleDataProducer(IUIPropertyPropertiesAvailableStatus properties) { - private readonly IUIPropertyPropertiesAvailableStatus _properties; + _properties = properties; + } - public UIPropertyFromRuleDataProducer(IUIPropertyPropertiesAvailableStatus properties) + protected override async Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ContextAndRuleProviderState providerState) + { + if (await providerState.ProjectState.GetMetadataVersionAsync() is (string versionKey, long versionNumber)) { - _properties = properties; + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); } - protected override async Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, ContextAndRuleProviderState providerState) - { - if (await providerState.ProjectState.GetMetadataVersionAsync() is (string versionKey, long versionNumber)) - { - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); - } - - return UIPropertyDataProducer.CreateUIPropertyValues(queryExecutionContext, parent, providerState.ProjectState, providerState.PropertiesContext, providerState.Rule, _properties); - } + return UIPropertyDataProducer.CreateUIPropertyValues(queryExecutionContext, parent, providerState.ProjectState, providerState.PropertiesContext, providerState.Rule, _properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducer.cs index cd19b22b4c..f08a439f08 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducer.cs @@ -6,124 +6,123 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the creation of instances and populating the requested members. +/// +internal static class UIPropertyValueDataProducer { - /// - /// Handles the creation of instances and populating the requested members. - /// - internal static class UIPropertyValueDataProducer + public static async Task CreateUIPropertyValueValueAsync(IEntityValue parent, ProjectConfiguration configuration, IProperty property, IUIPropertyValuePropertiesAvailableStatus requestedProperties) { - public static async Task CreateUIPropertyValueValueAsync(IEntityValue parent, ProjectConfiguration configuration, IProperty property, IUIPropertyValuePropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(parent); - Requires.NotNull(configuration); - Requires.NotNull(property); + Requires.NotNull(parent); + Requires.NotNull(configuration); + Requires.NotNull(property); - var identity = new EntityIdentity( - ((IEntityWithId)parent).Id, - Enumerable.Empty>()); + var identity = new EntityIdentity( + ((IEntityWithId)parent).Id, + Enumerable.Empty>()); - return await CreateUIPropertyValueValueAsync(parent.EntityRuntime, identity, configuration, property, requestedProperties); - } + return await CreateUIPropertyValueValueAsync(parent.EntityRuntime, identity, configuration, property, requestedProperties); + } - public static async Task CreateUIPropertyValueValueAsync(IEntityRuntimeModel runtimeModel, EntityIdentity id, ProjectConfiguration configuration, IProperty property, IUIPropertyValuePropertiesAvailableStatus requestedProperties) - { - Requires.NotNull(property); + public static async Task CreateUIPropertyValueValueAsync(IEntityRuntimeModel runtimeModel, EntityIdentity id, ProjectConfiguration configuration, IProperty property, IUIPropertyValuePropertiesAvailableStatus requestedProperties) + { + Requires.NotNull(property); - var newUIPropertyValue = new UIPropertyValueSnapshot(runtimeModel, id, new UIPropertyValuePropertiesAvailableStatus()); + var newUIPropertyValue = new UIPropertyValueSnapshot(runtimeModel, id, new UIPropertyValuePropertiesAvailableStatus()); - if (requestedProperties.UnevaluatedValue) + if (requestedProperties.UnevaluatedValue) + { + if (property is IEvaluatedProperty evaluatedProperty) { - if (property is IEvaluatedProperty evaluatedProperty) - { - newUIPropertyValue.UnevaluatedValue = await evaluatedProperty.GetUnevaluatedValueAsync(); - } - else - { - newUIPropertyValue.UnevaluatedValue = await property.GetValueAsync() as string; - } + newUIPropertyValue.UnevaluatedValue = await evaluatedProperty.GetUnevaluatedValueAsync(); } - - if (requestedProperties.EvaluatedValue) + else { - newUIPropertyValue.EvaluatedValue = property switch - { - IBoolProperty boolProperty => await boolProperty.GetValueAsBoolAsync(), - IStringProperty stringProperty => await stringProperty.GetValueAsStringAsync(), - IIntProperty intProperty => await intProperty.GetValueAsIntAsync(), - IEnumProperty enumProperty => (await enumProperty.GetValueAsIEnumValueAsync())?.Name, - IStringListProperty stringListProperty => await stringListProperty.GetValueAsStringCollectionAsync(), - _ => await property.GetValueAsync() - }; + newUIPropertyValue.UnevaluatedValue = await property.GetValueAsync() as string; } + } - if (requestedProperties.ValueDefinedInContext) + if (requestedProperties.EvaluatedValue) + { + newUIPropertyValue.EvaluatedValue = property switch { - newUIPropertyValue.ValueDefinedInContext = await property.IsDefinedInContextAsync(); - } - - ((IEntityValueFromProvider)newUIPropertyValue).ProviderState = new PropertyValueProviderState(configuration, property); + IBoolProperty boolProperty => await boolProperty.GetValueAsBoolAsync(), + IStringProperty stringProperty => await stringProperty.GetValueAsStringAsync(), + IIntProperty intProperty => await intProperty.GetValueAsIntAsync(), + IEnumProperty enumProperty => (await enumProperty.GetValueAsIEnumValueAsync())?.Name, + IStringListProperty stringListProperty => await stringListProperty.GetValueAsStringCollectionAsync(), + _ => await property.GetValueAsync() + }; + } - return newUIPropertyValue; + if (requestedProperties.ValueDefinedInContext) + { + newUIPropertyValue.ValueDefinedInContext = await property.IsDefinedInContextAsync(); } - public static async Task> CreateUIPropertyValueValuesAsync( - IQueryExecutionContext queryExecutionContext, - IEntityValue parent, - IProjectState cache, - Rule schema, - QueryProjectPropertiesContext propertiesContext, - string propertyName, - IUIPropertyValuePropertiesAvailableStatus requestedProperties) + ((IEntityValueFromProvider)newUIPropertyValue).ProviderState = new PropertyValueProviderState(configuration, property); + + return newUIPropertyValue; + } + + public static async Task> CreateUIPropertyValueValuesAsync( + IQueryExecutionContext queryExecutionContext, + IEntityValue parent, + IProjectState cache, + Rule schema, + QueryProjectPropertiesContext propertiesContext, + string propertyName, + IUIPropertyValuePropertiesAvailableStatus requestedProperties) + { + BaseProperty? unboundProperty = schema.GetProperty(propertyName); + if (unboundProperty is null) { - BaseProperty? unboundProperty = schema.GetProperty(propertyName); - if (unboundProperty is null) - { - return ImmutableList.Empty; - } + return ImmutableList.Empty; + } - ImmutableList.Builder builder = ImmutableList.CreateBuilder(); + ImmutableList.Builder builder = ImmutableList.CreateBuilder(); - IEnumerable configurations; - if (unboundProperty.IsConfigurationDependent()) + IEnumerable configurations; + if (unboundProperty.IsConfigurationDependent()) + { + // Return the values across all configurations. + configurations = await cache.GetKnownConfigurationsAsync() ?? Enumerable.Empty(); + } + else + { + // Return the value from any one configuration. + if (await cache.GetSuggestedConfigurationAsync() is ProjectConfiguration defaultConfiguration) { - // Return the values across all configurations. - configurations = await cache.GetKnownConfigurationsAsync() ?? Enumerable.Empty(); + configurations = CreateSingleItemEnumerable(defaultConfiguration); } else { - // Return the value from any one configuration. - if (await cache.GetSuggestedConfigurationAsync() is ProjectConfiguration defaultConfiguration) - { - configurations = CreateSingleItemEnumerable(defaultConfiguration); - } - else - { - configurations = Enumerable.Empty(); - } + configurations = Enumerable.Empty(); } + } - foreach (ProjectConfiguration configuration in configurations) + foreach (ProjectConfiguration configuration in configurations) + { + if (await cache.GetDataVersionAsync(configuration) is (string versionKey, long versionNumber)) { - if (await cache.GetDataVersionAsync(configuration) is (string versionKey, long versionNumber)) - { - queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); - } - - if (await cache.BindToRuleAsync(configuration, schema.Name, propertiesContext) is IRule rule - && rule.GetProperty(propertyName) is IProperty property) - { - IEntityValue propertyValue = await CreateUIPropertyValueValueAsync(parent, configuration, property, requestedProperties); - builder.Add(propertyValue); - } + queryExecutionContext.ReportInputDataVersion(versionKey, versionNumber); } - return builder.ToImmutable(); - - static IEnumerable CreateSingleItemEnumerable(ProjectConfiguration singleProjectConfiguration) + if (await cache.BindToRuleAsync(configuration, schema.Name, propertiesContext) is IRule rule + && rule.GetProperty(propertyName) is IProperty property) { - yield return singleProjectConfiguration; + IEntityValue propertyValue = await CreateUIPropertyValueValueAsync(parent, configuration, property, requestedProperties); + builder.Add(propertyValue); } } + + return builder.ToImmutable(); + + static IEnumerable CreateSingleItemEnumerable(ProjectConfiguration singleProjectConfiguration) + { + yield return singleProjectConfiguration; + } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProvider.cs index a2c24328d6..dbbe6ad5d9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProvider.cs @@ -6,24 +6,23 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Metadata; using Microsoft.VisualStudio.ProjectSystem.Query.Providers; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Creates instances that retrieve property value information +/// (). +/// +/// +/// Responsible for populating . +/// +[QueryDataProvider(UIPropertyValueType.TypeName, ProjectModel.ModelName)] +[RelationshipQueryDataProvider(UIPropertyType.TypeName, UIPropertyType.ValuesPropertyName)] +[QueryDataProviderZone(ProjectModelZones.Cps)] +[Export(typeof(IQueryByRelationshipDataProvider))] +internal class UIPropertyValueDataProvider : IQueryByRelationshipDataProvider { - /// - /// Creates instances that retrieve property value information - /// (). - /// - /// - /// Responsible for populating . - /// - [QueryDataProvider(UIPropertyValueType.TypeName, ProjectModel.ModelName)] - [RelationshipQueryDataProvider(UIPropertyType.TypeName, UIPropertyType.ValuesPropertyName)] - [QueryDataProviderZone(ProjectModelZones.Cps)] - [Export(typeof(IQueryByRelationshipDataProvider))] - internal class UIPropertyValueDataProvider : IQueryByRelationshipDataProvider + public IQueryDataProducer CreateQueryDataSource(IPropertiesAvailableStatus properties) { - public IQueryDataProducer CreateQueryDataSource(IPropertiesAvailableStatus properties) - { - return new UIPropertyValueFromUIPropertyDataProducer((IUIPropertyValuePropertiesAvailableStatus)properties); - } + return new UIPropertyValueFromUIPropertyDataProducer((IUIPropertyValuePropertiesAvailableStatus)properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueFromUIPropertyDataProducer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueFromUIPropertyDataProducer.cs index 7412568d3c..623672c8ec 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueFromUIPropertyDataProducer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueFromUIPropertyDataProducer.cs @@ -3,30 +3,29 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles retrieving a set of s from an . +/// +internal class UIPropertyValueFromUIPropertyDataProducer : QueryDataFromProviderStateProducerBase { - /// - /// Handles retrieving a set of s from an . - /// - internal class UIPropertyValueFromUIPropertyDataProducer : QueryDataFromProviderStateProducerBase - { - private readonly IUIPropertyValuePropertiesAvailableStatus _properties; + private readonly IUIPropertyValuePropertiesAvailableStatus _properties; - public UIPropertyValueFromUIPropertyDataProducer(IUIPropertyValuePropertiesAvailableStatus properties) - { - _properties = properties; - } + public UIPropertyValueFromUIPropertyDataProducer(IUIPropertyValuePropertiesAvailableStatus properties) + { + _properties = properties; + } - protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyProviderState providerState) - { - return UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync( - queryExecutionContext, - parent, - providerState.ProjectState, - providerState.ContainingRule, - providerState.PropertiesContext, - providerState.PropertyName, - _properties); - } + protected override Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, PropertyProviderState providerState) + { + return UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync( + queryExecutionContext, + parent, + providerState.ProjectState, + providerState.ContainingRule, + providerState.PropertiesContext, + providerState.PropertyName, + _properties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataByIdProducerBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataByIdProducerBase.cs index 127478d584..7df5773239 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataByIdProducerBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataByIdProducerBase.cs @@ -4,39 +4,38 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the boilerplate of retrieving an based on an ID. +/// +internal abstract class QueryDataByIdProducerBase : QueryDataProducerBase, IQueryDataProducer, IEntityValue> { - /// - /// Handles the boilerplate of retrieving an based on an ID. - /// - internal abstract class QueryDataByIdProducerBase : QueryDataProducerBase, IQueryDataProducer, IEntityValue> - { - protected static readonly Task NullEntityValue = Task.FromResult(null); + protected static readonly Task NullEntityValue = Task.FromResult(null); - public async Task SendRequestAsync(QueryProcessRequest> request) + public async Task SendRequestAsync(QueryProcessRequest> request) + { + if (request.RequestData is not null) { - if (request.RequestData is not null) + foreach (EntityIdentity requestId in request.RequestData) { - foreach (EntityIdentity requestId in request.RequestData) + try { - try - { - IEntityValue? entityValue = await TryCreateEntityOrNullAsync(request.QueryExecutionContext, requestId); - if (entityValue is not null) - { - await ResultReceiver.ReceiveResultAsync(new QueryProcessResult(entityValue, request, ProjectModelZones.Cps)); - } - } - catch (Exception ex) + IEntityValue? entityValue = await TryCreateEntityOrNullAsync(request.QueryExecutionContext, requestId); + if (entityValue is not null) { - request.QueryExecutionContext.ReportError(ex); + await ResultReceiver.ReceiveResultAsync(new QueryProcessResult(entityValue, request, ProjectModelZones.Cps)); } } + catch (Exception ex) + { + request.QueryExecutionContext.ReportError(ex); + } } - - await ResultReceiver.OnRequestProcessFinishedAsync(request); } - protected abstract Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id); + await ResultReceiver.OnRequestProcessFinishedAsync(request); } + + protected abstract Task TryCreateEntityOrNullAsync(IQueryExecutionContext queryExecutionContext, EntityIdentity id); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs index 4f1d09e2c4..68310e7b49 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataFromProviderStateProducerBase.cs @@ -4,38 +4,37 @@ using Microsoft.VisualStudio.ProjectSystem.Query.Execution; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +/// +/// Handles the boilerplate of retrieving a set of s based on the +/// state associated with the parent . +/// +internal abstract class QueryDataFromProviderStateProducerBase : QueryDataProducerBase, IQueryDataProducer { - /// - /// Handles the boilerplate of retrieving a set of s based on the - /// state associated with the parent . - /// - internal abstract class QueryDataFromProviderStateProducerBase : QueryDataProducerBase, IQueryDataProducer + public async Task SendRequestAsync(QueryProcessRequest request) { - public async Task SendRequestAsync(QueryProcessRequest request) + if (request.RequestData is IEntityValueFromProvider { ProviderState: T providerState }) { - if (request.RequestData is IEntityValueFromProvider { ProviderState: T providerState }) + try { - try + foreach (IEntityValue categoryValue in await CreateValuesAsync(request.QueryExecutionContext, request.RequestData, providerState)) { - foreach (IEntityValue categoryValue in await CreateValuesAsync(request.QueryExecutionContext, request.RequestData, providerState)) - { - await ResultReceiver.ReceiveResultAsync(new QueryProcessResult(categoryValue, request, ProjectModelZones.Cps)); - } - } - catch (Exception ex) - { - request.QueryExecutionContext.ReportError(ex); + await ResultReceiver.ReceiveResultAsync(new QueryProcessResult(categoryValue, request, ProjectModelZones.Cps)); } } - - await ResultReceiver.OnRequestProcessFinishedAsync(request); + catch (Exception ex) + { + request.QueryExecutionContext.ReportError(ex); + } } - /// - /// Given the entity and the associated , - /// returns a set of child entities. - /// - protected abstract Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, T providerState); + await ResultReceiver.OnRequestProcessFinishedAsync(request); } + + /// + /// Given the entity and the associated , + /// returns a set of child entities. + /// + protected abstract Task> CreateValuesAsync(IQueryExecutionContext queryExecutionContext, IEntityValue parent, T providerState); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataProviderBase.cs index cb30d107a4..9fa652bb00 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Query/QueryDataProviderBase.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS -{ - internal abstract class QueryDataProviderBase - { - private readonly Lazy _projectService; +namespace Microsoft.VisualStudio.ProjectSystem.VS; - protected QueryDataProviderBase(IProjectServiceAccessor projectServiceAccessor) - { - _projectService = new Lazy( - () => (IProjectService2)projectServiceAccessor.GetProjectService(), - LazyThreadSafetyMode.PublicationOnly); - } +internal abstract class QueryDataProviderBase +{ + private readonly Lazy _projectService; - protected IProjectService2 ProjectService => _projectService.Value; + protected QueryDataProviderBase(IProjectServiceAccessor projectServiceAccessor) + { + _projectService = new Lazy( + () => (IProjectService2)projectServiceAccessor.GetProjectService(), + LazyThreadSafetyMode.PublicationOnly); } + + protected IProjectService2 ProjectService => _projectService.Value; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Refactor/VisualStudioRefactorNotifyService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Refactor/VisualStudioRefactorNotifyService.cs index 17933844d1..323f46a6e6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Refactor/VisualStudioRefactorNotifyService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Refactor/VisualStudioRefactorNotifyService.cs @@ -4,120 +4,119 @@ using Microsoft.VisualStudio.ProjectSystem.Refactor; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Refactor +namespace Microsoft.VisualStudio.ProjectSystem.VS.Refactor; + +[Export(typeof(IRefactorNotifyService))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +internal class VisualStudioRefactorNotifyService : IRefactorNotifyService { - [Export(typeof(IRefactorNotifyService))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - internal class VisualStudioRefactorNotifyService : IRefactorNotifyService + private readonly IVsUIService _dte; + private readonly IVsUIService _solution; + + [ImportingConstructor] + public VisualStudioRefactorNotifyService(IVsUIService dte, IVsUIService solution) { - private readonly IVsUIService _dte; - private readonly IVsUIService _solution; + _dte = dte; + _solution = solution; + } - [ImportingConstructor] - public VisualStudioRefactorNotifyService(IVsUIService dte, IVsUIService solution) + public void OnBeforeGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName) + { + IVsHierarchy? projectHierarchy = GetProjectHierarchy(projectPath); + if (projectHierarchy is null) { - _dte = dte; - _solution = solution; + return; } - public void OnBeforeGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName) + if (projectHierarchy is not IVsHierarchyRefactorNotify refactorNotify) { - IVsHierarchy? projectHierarchy = GetProjectHierarchy(projectPath); - if (projectHierarchy is null) - { - return; - } + return; + } - if (projectHierarchy is not IVsHierarchyRefactorNotify refactorNotify) - { - return; - } + uint[] ids = GetIdsForFiles(projectHierarchy, filePaths).ToArray(); - uint[] ids = GetIdsForFiles(projectHierarchy, filePaths).ToArray(); + refactorNotify.OnBeforeGlobalSymbolRenamed(cItemsAffected: (uint)ids.Length, + rgItemsAffected: ids, + cRQNames: 1, + rglpszRQName: new[] { rqName }, + lpszNewName: newName, + promptContinueOnFail: 1); + } - refactorNotify.OnBeforeGlobalSymbolRenamed(cItemsAffected: (uint)ids.Length, - rgItemsAffected: ids, - cRQNames: 1, - rglpszRQName: new[] { rqName }, - lpszNewName: newName, - promptContinueOnFail: 1); + public void OnAfterGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName) + { + IVsHierarchy? projectHierarchy = GetProjectHierarchy(projectPath); + if (projectHierarchy is null) + { + return; } - public void OnAfterGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName) + if (projectHierarchy is not IVsHierarchyRefactorNotify refactorNotify) { - IVsHierarchy? projectHierarchy = GetProjectHierarchy(projectPath); - if (projectHierarchy is null) - { - return; - } + return; + } - if (projectHierarchy is not IVsHierarchyRefactorNotify refactorNotify) - { - return; - } + uint[] ids = GetIdsForFiles(projectHierarchy, filePaths).ToArray(); - uint[] ids = GetIdsForFiles(projectHierarchy, filePaths).ToArray(); + refactorNotify.OnGlobalSymbolRenamed(cItemsAffected: (uint)ids.Length, + rgItemsAffected: ids, + cRQNames: 1, + rglpszRQName: new[] { rqName }, + lpszNewName: newName); + } - refactorNotify.OnGlobalSymbolRenamed(cItemsAffected: (uint)ids.Length, - rgItemsAffected: ids, - cRQNames: 1, - rglpszRQName: new[] { rqName }, - lpszNewName: newName); + private IVsHierarchy? GetProjectHierarchy(string projectPath) + { + Project? project = TryGetProjectFromPath(projectPath); + if (project is null) + { + return null; } - private IVsHierarchy? GetProjectHierarchy(string projectPath) + return TryGetIVsHierarchy(project); + } + + private Project? TryGetProjectFromPath(string projectPath) + { + foreach (Project project in _dte.Value.Solution.Projects.OfType()) { - Project? project = TryGetProjectFromPath(projectPath); - if (project is null) + string? fullName; + try { - return null; + fullName = project.FullName; } - - return TryGetIVsHierarchy(project); - } - - private Project? TryGetProjectFromPath(string projectPath) - { - foreach (Project project in _dte.Value.Solution.Projects.OfType()) + catch (Exception) { - string? fullName; - try - { - fullName = project.FullName; - } - catch (Exception) - { - // DTE COM calls can fail for any number of valid reasons. - continue; - } - - if (StringComparers.Paths.Equals(fullName, projectPath)) - { - return project; - } + // DTE COM calls can fail for any number of valid reasons. + continue; } - return null; - } - - private IVsHierarchy? TryGetIVsHierarchy(Project project) - { - if (_solution.Value.GetProjectOfUniqueName(project.UniqueName, out IVsHierarchy projectHierarchy) == HResult.OK) + if (StringComparers.Paths.Equals(fullName, projectPath)) { - return projectHierarchy; + return project; } + } - return null; + return null; + } + + private IVsHierarchy? TryGetIVsHierarchy(Project project) + { + if (_solution.Value.GetProjectOfUniqueName(project.UniqueName, out IVsHierarchy projectHierarchy) == HResult.OK) + { + return projectHierarchy; } - private static IEnumerable GetIdsForFiles(IVsHierarchy projectHierarchy, IEnumerable filePaths) + return null; + } + + private static IEnumerable GetIdsForFiles(IVsHierarchy projectHierarchy, IEnumerable filePaths) + { + foreach (string filePath in filePaths) { - foreach (string filePath in filePaths) + if (projectHierarchy.ParseCanonicalName(filePath, out uint id) == HResult.OK) { - if (projectHierarchy.ParseCanonicalName(filePath, out uint id) == HResult.OK) - { - yield return id; - } + yield return id; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs index 3beed6abdc..9fcbdb9fdc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AbstractReferenceHandler.cs @@ -3,135 +3,134 @@ using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References -{ - internal abstract class AbstractReferenceHandler - { - private readonly ProjectSystemReferenceType _referenceType; +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; - protected AbstractReferenceHandler(ProjectSystemReferenceType referenceType) - => _referenceType = referenceType; - - internal Task RemoveReferenceAsync(ConfiguredProject configuredProject, - string itemSpecification) - { - Requires.NotNull(configuredProject); - Assumes.Present(configuredProject.Services); - - return RemoveReferenceAsync(configuredProject.Services, itemSpecification); - } +internal abstract class AbstractReferenceHandler +{ + private readonly ProjectSystemReferenceType _referenceType; - protected abstract Task RemoveReferenceAsync(ConfiguredProjectServices services, - string itemSpecification); + protected AbstractReferenceHandler(ProjectSystemReferenceType referenceType) + => _referenceType = referenceType; - internal Task AddReferenceAsync(ConfiguredProject configuredProject, - string itemSpecification) - { - Requires.NotNull(configuredProject); - Assumes.Present(configuredProject.Services); + internal Task RemoveReferenceAsync(ConfiguredProject configuredProject, + string itemSpecification) + { + Requires.NotNull(configuredProject); + Assumes.Present(configuredProject.Services); - return AddReferenceAsync(configuredProject.Services, itemSpecification); - } + return RemoveReferenceAsync(configuredProject.Services, itemSpecification); + } - protected abstract Task AddReferenceAsync(ConfiguredProjectServices services, - string itemSpecification); + protected abstract Task RemoveReferenceAsync(ConfiguredProjectServices services, + string itemSpecification); - public Task> GetUnresolvedReferencesAsync(ConfiguredProject selectedConfiguredProject) - { - Requires.NotNull(selectedConfiguredProject); - Assumes.Present(selectedConfiguredProject.Services); + internal Task AddReferenceAsync(ConfiguredProject configuredProject, + string itemSpecification) + { + Requires.NotNull(configuredProject); + Assumes.Present(configuredProject.Services); - return GetUnresolvedReferencesAsync(selectedConfiguredProject.Services); - } + return AddReferenceAsync(configuredProject.Services, itemSpecification); + } - protected abstract Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services); + protected abstract Task AddReferenceAsync(ConfiguredProjectServices services, + string itemSpecification); - internal async Task> GetReferencesAsync(ConfiguredProject selectedConfiguredProject, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + public Task> GetUnresolvedReferencesAsync(ConfiguredProject selectedConfiguredProject) + { + Requires.NotNull(selectedConfiguredProject); + Assumes.Present(selectedConfiguredProject.Services); - var references = new List(); + return GetUnresolvedReferencesAsync(selectedConfiguredProject.Services); + } - var projectItems = await GetUnresolvedReferencesAsync(selectedConfiguredProject); + protected abstract Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services); - foreach (var item in projectItems) - { - bool treatAsUsed = await GetAttributeTreatAsUsedAsync(item.Metadata); - string itemSpecification = item.EvaluatedInclude; + internal async Task> GetReferencesAsync(ConfiguredProject selectedConfiguredProject, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - references.Add(new ProjectSystemReferenceInfo(_referenceType, itemSpecification, treatAsUsed)); - } + var references = new List(); - return references; - } + var projectItems = await GetUnresolvedReferencesAsync(selectedConfiguredProject); - private static async Task GetAttributeTreatAsUsedAsync(IProjectProperties metadata) + foreach (var item in projectItems) { - string? value = await metadata.GetEvaluatedPropertyValueAsync(ProjectReference.TreatAsUsedProperty); + bool treatAsUsed = await GetAttributeTreatAsUsedAsync(item.Metadata); + string itemSpecification = item.EvaluatedInclude; - return value is not null && PropertySerializer.SimpleTypes.ToValue(value); + references.Add(new ProjectSystemReferenceInfo(_referenceType, itemSpecification, treatAsUsed)); } - private async Task GetProjectItemsAsync(ConfiguredProject selectedConfiguredProject, - string itemSpecification) - { - var projectItems = await GetUnresolvedReferencesAsync(selectedConfiguredProject); + return references; + } - var item = projectItems - .FirstOrDefault(c => c.EvaluatedInclude == itemSpecification); - return item; - } + private static async Task GetAttributeTreatAsUsedAsync(IProjectProperties metadata) + { + string? value = await metadata.GetEvaluatedPropertyValueAsync(ProjectReference.TreatAsUsedProperty); - internal IProjectSystemUpdateReferenceOperation CreateSetAttributeCommand(ConfiguredProject selectedConfiguredProject, - ProjectSystemReferenceUpdate referenceUpdate) - { - return new SetAttributeCommand(this, selectedConfiguredProject, referenceUpdate.ReferenceInfo.ItemSpecification); - } + return value is not null && PropertySerializer.SimpleTypes.ToValue(value); + } - internal IProjectSystemUpdateReferenceOperation CreateUnsetAttributeCommand(ConfiguredProject selectedConfiguredProject, - ProjectSystemReferenceUpdate referenceUpdate) - { - return new UnsetAttributeCommand(this, selectedConfiguredProject, referenceUpdate.ReferenceInfo.ItemSpecification); - } + private async Task GetProjectItemsAsync(ConfiguredProject selectedConfiguredProject, + string itemSpecification) + { + var projectItems = await GetUnresolvedReferencesAsync(selectedConfiguredProject); - internal IProjectSystemUpdateReferenceOperation? CreateRemoveReferenceCommand(ConfiguredProject selectedConfiguredProject, - ProjectSystemReferenceUpdate referenceUpdate) - { - return new RemoveReferenceCommand(this, selectedConfiguredProject, referenceUpdate); - } + var item = projectItems + .FirstOrDefault(c => c.EvaluatedInclude == itemSpecification); + return item; + } - public async Task> GetAttributesAsync(ConfiguredProject selectedConfiguredProject, string itemSpecification) - { - Dictionary propertyValues = new(); + internal IProjectSystemUpdateReferenceOperation CreateSetAttributeCommand(ConfiguredProject selectedConfiguredProject, + ProjectSystemReferenceUpdate referenceUpdate) + { + return new SetAttributeCommand(this, selectedConfiguredProject, referenceUpdate.ReferenceInfo.ItemSpecification); + } - IProjectItem? items = await GetProjectItemsAsync(selectedConfiguredProject, itemSpecification); + internal IProjectSystemUpdateReferenceOperation CreateUnsetAttributeCommand(ConfiguredProject selectedConfiguredProject, + ProjectSystemReferenceUpdate referenceUpdate) + { + return new UnsetAttributeCommand(this, selectedConfiguredProject, referenceUpdate.ReferenceInfo.ItemSpecification); + } - if (items is null) - { - return propertyValues; - } + internal IProjectSystemUpdateReferenceOperation? CreateRemoveReferenceCommand(ConfiguredProject selectedConfiguredProject, + ProjectSystemReferenceUpdate referenceUpdate) + { + return new RemoveReferenceCommand(this, selectedConfiguredProject, referenceUpdate); + } - var propertyNames = await items.Metadata.GetPropertyNamesAsync(); + public async Task> GetAttributesAsync(ConfiguredProject selectedConfiguredProject, string itemSpecification) + { + Dictionary propertyValues = new(); - foreach (var property in propertyNames) - { - var value = await items.Metadata.GetEvaluatedPropertyValueAsync(property); - propertyValues.Add(property, value); - } + IProjectItem? items = await GetProjectItemsAsync(selectedConfiguredProject, itemSpecification); + if (items is null) + { return propertyValues; } - public async Task SetAttributesAsync(ConfiguredProject selectedConfiguredProject, string itemSpecification, Dictionary projectPropertiesValues) + var propertyNames = await items.Metadata.GetPropertyNamesAsync(); + + foreach (var property in propertyNames) { - IProjectItem? items = await GetProjectItemsAsync(selectedConfiguredProject, itemSpecification); + var value = await items.Metadata.GetEvaluatedPropertyValueAsync(property); + propertyValues.Add(property, value); + } + + return propertyValues; + } - if (items is not null) + public async Task SetAttributesAsync(ConfiguredProject selectedConfiguredProject, string itemSpecification, Dictionary projectPropertiesValues) + { + IProjectItem? items = await GetProjectItemsAsync(selectedConfiguredProject, itemSpecification); + + if (items is not null) + { + foreach ((string propertyName, string propertyValue) in projectPropertiesValues) { - foreach ((string propertyName, string propertyValue) in projectPropertiesValues) - { - await items.Metadata.SetPropertyValueAsync(propertyName, propertyValue, null); - } + await items.Metadata.SetPropertyValueAsync(propertyName, propertyValue, null); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AssemblyReferenceHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AssemblyReferenceHandler.cs index fcb0647e15..207e7329bb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AssemblyReferenceHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/AssemblyReferenceHandler.cs @@ -3,59 +3,58 @@ using System.Reflection; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal class AssemblyReferenceHandler : AbstractReferenceHandler { - internal class AssemblyReferenceHandler : AbstractReferenceHandler + internal AssemblyReferenceHandler() + : base(ProjectSystemReferenceType.Assembly) + { } + + protected override Task RemoveReferenceAsync(ConfiguredProjectServices services, + string itemSpecification) { - internal AssemblyReferenceHandler() - : base(ProjectSystemReferenceType.Assembly) - { } + Assumes.Present(services.AssemblyReferences); - protected override Task RemoveReferenceAsync(ConfiguredProjectServices services, - string itemSpecification) + AssemblyName? assemblyName = null; + string? assemblyPath = null; + + if (Path.IsPathRooted(itemSpecification)) + { + assemblyPath = itemSpecification; + } + else { - Assumes.Present(services.AssemblyReferences); + assemblyName = new AssemblyName(itemSpecification); + } + + return services.AssemblyReferences.RemoveAsync(assemblyName, assemblyPath); + } - AssemblyName? assemblyName = null; - string? assemblyPath = null; + protected override Task AddReferenceAsync(ConfiguredProjectServices services, string itemSpecification) + { + Assumes.Present(services.AssemblyReferences); - if (Path.IsPathRooted(itemSpecification)) - { - assemblyPath = itemSpecification; - } - else - { - assemblyName = new AssemblyName(itemSpecification); - } + AssemblyName? assemblyName = null; + string? assemblyPath = null; - return services.AssemblyReferences.RemoveAsync(assemblyName, assemblyPath); + if (Path.IsPathRooted(itemSpecification)) + { + assemblyPath = itemSpecification; } - - protected override Task AddReferenceAsync(ConfiguredProjectServices services, string itemSpecification) + else { - Assumes.Present(services.AssemblyReferences); - - AssemblyName? assemblyName = null; - string? assemblyPath = null; - - if (Path.IsPathRooted(itemSpecification)) - { - assemblyPath = itemSpecification; - } - else - { - assemblyName = new AssemblyName(itemSpecification); - } - - // todo: get path from the Remove Command - return services.AssemblyReferences.AddAsync(assemblyName, assemblyPath); + assemblyName = new AssemblyName(itemSpecification); } - protected override async Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services) - { - Assumes.Present(services.AssemblyReferences); + // todo: get path from the Remove Command + return services.AssemblyReferences.AddAsync(assemblyName, assemblyPath); + } - return (await services.AssemblyReferences.GetUnresolvedReferencesAsync()).Cast(); - } + protected override async Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services) + { + Assumes.Present(services.AssemblyReferences); + + return (await services.AssemblyReferences.GetUnresolvedReferencesAsync()).Cast(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.ResolvedReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.ResolvedReference.cs index f982f01e2e..16a22bc091 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.ResolvedReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.ResolvedReference.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal partial class DesignTimeAssemblyResolution { - internal partial class DesignTimeAssemblyResolution + private readonly struct ResolvedReference { - private readonly struct ResolvedReference + public ResolvedReference(string resolvedPath, Version? version) { - public ResolvedReference(string resolvedPath, Version? version) - { - Assumes.NotNull(resolvedPath); + Assumes.NotNull(resolvedPath); - ResolvedPath = resolvedPath; - Version = version; - } + ResolvedPath = resolvedPath; + Version = version; + } - public string ResolvedPath { get; } + public string ResolvedPath { get; } - public Version? Version { get; } - } + public Version? Version { get; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs index bc995eaafc..3a4aa3d7d6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/DesignTimeAssemblyResolution.cs @@ -8,186 +8,185 @@ using VSLangProj; using VSLangProj80; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +/// +/// Provides an implementation of that sits over the top of VSLangProj.References. +/// +[ExportProjectNodeComService(typeof(IVsDesignTimeAssemblyResolution))] // Need to override CPS's version, which it implements on the project node as IVsDesignTimeAssemblyResolution +[ExportVsProfferedProjectService(typeof(SVsDesignTimeAssemblyResolution))] +[AppliesTo(ProjectCapability.DotNet)] +[Order(Order.Default)] // Before CPS's version +internal partial class DesignTimeAssemblyResolution : IVsDesignTimeAssemblyResolution, IDisposable { - /// - /// Provides an implementation of that sits over the top of VSLangProj.References. - /// - [ExportProjectNodeComService(typeof(IVsDesignTimeAssemblyResolution))] // Need to override CPS's version, which it implements on the project node as IVsDesignTimeAssemblyResolution - [ExportVsProfferedProjectService(typeof(SVsDesignTimeAssemblyResolution))] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] // Before CPS's version - internal partial class DesignTimeAssemblyResolution : IVsDesignTimeAssemblyResolution, IDisposable + // NOTE: Unlike the legacy project system, this implementation does resolve only "framework" assemblies. In .NET Core and other project types, framework assemblies + // are not treated specially - they just come through as normal references from packages. We also do not have a static registration of what assemblies would make up + // a framework, so we assume that what the project is referencing represents the "framework" of accessible types. This is the same as what the legacy project system + // does under the UWP flavor (when the DTARUseReferencesFromProject MSBuild property is set). + // + // This implementation will work for .NET Core based projects, but we might run into unexpected behavior when bringing up legacy projects where designers/components + // expect to ask for/use types that are not currently referenced by the project. We should revisit at that time. + // + // Ideally this would sit on a simple wrapper over the top of project subscription service, however, CPS's internal ReferencesHostBridge, which populates VSLangProj.References, + // already does the work to listen to the project subscription for reference adds/removes/changes and makes sure to publish the results in sync with the solution tree. + // We just use its results. + private IUnconfiguredProjectVsServices? _projectVsServices; + + [ImportingConstructor] + public DesignTimeAssemblyResolution(IUnconfiguredProjectVsServices projectVsServices) { - // NOTE: Unlike the legacy project system, this implementation does resolve only "framework" assemblies. In .NET Core and other project types, framework assemblies - // are not treated specially - they just come through as normal references from packages. We also do not have a static registration of what assemblies would make up - // a framework, so we assume that what the project is referencing represents the "framework" of accessible types. This is the same as what the legacy project system - // does under the UWP flavor (when the DTARUseReferencesFromProject MSBuild property is set). - // - // This implementation will work for .NET Core based projects, but we might run into unexpected behavior when bringing up legacy projects where designers/components - // expect to ask for/use types that are not currently referenced by the project. We should revisit at that time. - // - // Ideally this would sit on a simple wrapper over the top of project subscription service, however, CPS's internal ReferencesHostBridge, which populates VSLangProj.References, - // already does the work to listen to the project subscription for reference adds/removes/changes and makes sure to publish the results in sync with the solution tree. - // We just use its results. - private IUnconfiguredProjectVsServices? _projectVsServices; + Requires.NotNull(projectVsServices); - [ImportingConstructor] - public DesignTimeAssemblyResolution(IUnconfiguredProjectVsServices projectVsServices) - { - Requires.NotNull(projectVsServices); + _projectVsServices = projectVsServices; + } - _projectVsServices = projectVsServices; + public int GetTargetFramework(out string? ppTargetFramework) + { + if (_projectVsServices is null) + { + ppTargetFramework = null; + return HResult.Unexpected; } - public int GetTargetFramework(out string? ppTargetFramework) + return _projectVsServices.VsHierarchy.GetProperty(VsHierarchyPropID.TargetFrameworkMoniker, defaultValue: null, result: out ppTargetFramework); + } + + public int ResolveAssemblyPathInTargetFx(string?[]? prgAssemblySpecs, uint cAssembliesToResolve, VsResolvedAssemblyPath[]? prgResolvedAssemblyPaths, out uint pcResolvedAssemblyPaths) + { + if (prgAssemblySpecs is null || cAssembliesToResolve == 0 || prgResolvedAssemblyPaths is null || cAssembliesToResolve != prgAssemblySpecs.Length || cAssembliesToResolve != prgResolvedAssemblyPaths.Length) { - if (_projectVsServices is null) - { - ppTargetFramework = null; - return HResult.Unexpected; - } + pcResolvedAssemblyPaths = 0; + return HResult.InvalidArg; + } - return _projectVsServices.VsHierarchy.GetProperty(VsHierarchyPropID.TargetFrameworkMoniker, defaultValue: null, result: out ppTargetFramework); + if (!TryParseAssemblyNames(prgAssemblySpecs, out AssemblyName[] assemblyNames)) + { + pcResolvedAssemblyPaths = 0; + return HResult.InvalidArg; } - public int ResolveAssemblyPathInTargetFx(string?[]? prgAssemblySpecs, uint cAssembliesToResolve, VsResolvedAssemblyPath[]? prgResolvedAssemblyPaths, out uint pcResolvedAssemblyPaths) + if (_projectVsServices is null) { - if (prgAssemblySpecs is null || cAssembliesToResolve == 0 || prgResolvedAssemblyPaths is null || cAssembliesToResolve != prgAssemblySpecs.Length || cAssembliesToResolve != prgResolvedAssemblyPaths.Length) - { - pcResolvedAssemblyPaths = 0; - return HResult.InvalidArg; - } + pcResolvedAssemblyPaths = 0; + return HResult.Unexpected; + } - if (!TryParseAssemblyNames(prgAssemblySpecs, out AssemblyName[] assemblyNames)) - { - pcResolvedAssemblyPaths = 0; - return HResult.InvalidArg; - } + pcResolvedAssemblyPaths = ResolveReferences(prgAssemblySpecs, assemblyNames, prgResolvedAssemblyPaths); + return HResult.OK; + } - if (_projectVsServices is null) - { - pcResolvedAssemblyPaths = 0; - return HResult.Unexpected; - } + private uint ResolveReferences(string?[] originalNames, AssemblyName[] assemblyName, [In, Out] VsResolvedAssemblyPath[] assemblyPaths) + { + Assumes.True(originalNames.Length == assemblyName.Length && originalNames.Length == assemblyPaths.Length); - pcResolvedAssemblyPaths = ResolveReferences(prgAssemblySpecs, assemblyNames, prgResolvedAssemblyPaths); - return HResult.OK; - } + uint resolvedReferencesCount = 0; + IDictionary references = GetAllResolvedReferences(); - private uint ResolveReferences(string?[] originalNames, AssemblyName[] assemblyName, [In, Out] VsResolvedAssemblyPath[] assemblyPaths) + for (int i = 0; i < assemblyName.Length; i++) { - Assumes.True(originalNames.Length == assemblyName.Length && originalNames.Length == assemblyPaths.Length); - - uint resolvedReferencesCount = 0; - IDictionary references = GetAllResolvedReferences(); - - for (int i = 0; i < assemblyName.Length; i++) + string? resolvedPath = FindResolvedAssemblyPath(references, assemblyName[i]); + if (resolvedPath is not null) { - string? resolvedPath = FindResolvedAssemblyPath(references, assemblyName[i]); - if (resolvedPath is not null) + assemblyPaths[resolvedReferencesCount] = new VsResolvedAssemblyPath() { - assemblyPaths[resolvedReferencesCount] = new VsResolvedAssemblyPath() - { - bstrOrigAssemblySpec = originalNames[i], // Note we use the original name, not the parsed name, as they could be different - bstrResolvedAssemblyPath = resolvedPath - }; + bstrOrigAssemblySpec = originalNames[i], // Note we use the original name, not the parsed name, as they could be different + bstrResolvedAssemblyPath = resolvedPath + }; - resolvedReferencesCount++; - } + resolvedReferencesCount++; } - - return resolvedReferencesCount; } - private static string? FindResolvedAssemblyPath(IDictionary references, AssemblyName assemblyName) - { - // NOTE: We mimic the behavior of the legacy project system when in "DTARUseReferencesFromProject" mode, it matches - // only on version, and only against currently referenced assemblies, nothing more. - // - // See ResolveAssemblyReference in vs\env\vscore\package\MSBuild\ToolLocationHelperShim.cs - // - // - if (references.TryGetValue(assemblyName.Name, out ResolvedReference reference)) - { - // If the caller didn't specify a version, than they only want to match on name - if (assemblyName.Version is null) - return reference.ResolvedPath; + return resolvedReferencesCount; + } - // If the reference is the same or higher than the requested version, then we consider it a match - if (reference.Version is not null && reference.Version >= assemblyName.Version) - return reference.ResolvedPath; - } + private static string? FindResolvedAssemblyPath(IDictionary references, AssemblyName assemblyName) + { + // NOTE: We mimic the behavior of the legacy project system when in "DTARUseReferencesFromProject" mode, it matches + // only on version, and only against currently referenced assemblies, nothing more. + // + // See ResolveAssemblyReference in vs\env\vscore\package\MSBuild\ToolLocationHelperShim.cs + // + // + if (references.TryGetValue(assemblyName.Name, out ResolvedReference reference)) + { + // If the caller didn't specify a version, than they only want to match on name + if (assemblyName.Version is null) + return reference.ResolvedPath; - return null; + // If the reference is the same or higher than the requested version, then we consider it a match + if (reference.Version is not null && reference.Version >= assemblyName.Version) + return reference.ResolvedPath; } - private IDictionary GetAllResolvedReferences() - { - var resolvedReferences = new Dictionary(StringComparer.Ordinal); + return null; + } + + private IDictionary GetAllResolvedReferences() + { + var resolvedReferences = new Dictionary(StringComparer.Ordinal); - VSProject? project = GetVSProject(); - if (project is not null) + VSProject? project = GetVSProject(); + if (project is not null) + { + foreach (Reference3 reference in project.References.OfType()) { - foreach (Reference3 reference in project.References.OfType()) + // We only want resolved assembly references + if (reference.RefType == (uint)__PROJECTREFERENCETYPE.PROJREFTYPE_ASSEMBLY && reference.Resolved) { - // We only want resolved assembly references - if (reference.RefType == (uint)__PROJECTREFERENCETYPE.PROJREFTYPE_ASSEMBLY && reference.Resolved) - { - resolvedReferences[reference.Name] = new ResolvedReference(reference.Path, TryParseVersionOrNull(reference.Version)); - } + resolvedReferences[reference.Name] = new ResolvedReference(reference.Path, TryParseVersionOrNull(reference.Version)); } } - - return resolvedReferences; } - private VSProject? GetVSProject() - { - Project? project = _projectVsServices?.VsHierarchy.GetProperty(VsHierarchyPropID.ExtObject); - - return project?.Object as VSProject; - } + return resolvedReferences; + } - private static bool TryParseAssemblyNames(string?[] assemblyNames, out AssemblyName[] result) - { - result = new AssemblyName[assemblyNames.Length]; + private VSProject? GetVSProject() + { + Project? project = _projectVsServices?.VsHierarchy.GetProperty(VsHierarchyPropID.ExtObject); - for (int i = 0; i < assemblyNames.Length; i++) - { - string? assemblyName = assemblyNames[i]; + return project?.Object as VSProject; + } - if (string.IsNullOrEmpty(assemblyName)) - return false; + private static bool TryParseAssemblyNames(string?[] assemblyNames, out AssemblyName[] result) + { + result = new AssemblyName[assemblyNames.Length]; - try - { - result[i] = new AssemblyName(assemblyName); - } - catch (FileLoadException) - { - return false; - } - } + for (int i = 0; i < assemblyNames.Length; i++) + { + string? assemblyName = assemblyNames[i]; - return true; - } + if (string.IsNullOrEmpty(assemblyName)) + return false; - private static Version? TryParseVersionOrNull(string version) - { - if (Version.TryParse(version, out Version result)) + try { - return result; + result[i] = new AssemblyName(assemblyName); + } + catch (FileLoadException) + { + return false; } - - return null; } - public void Dispose() + return true; + } + + private static Version? TryParseVersionOrNull(string version) + { + if (Version.TryParse(version, out Version result)) { - // Important for ProjectNodeComServices to null out fields to reduce the amount - // of data we leak when extensions incorrectly holds onto the IVsHierarchy. - _projectVsServices = null; + return result; } + + return null; + } + + public void Dispose() + { + // Important for ProjectNodeComServices to null out fields to reduce the amount + // of data we leak when extensions incorrectly holds onto the IVsHierarchy. + _projectVsServices = null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/IReferenceCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/IReferenceCommand.cs index 20c17f3770..20ce6a07b6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/IReferenceCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/IReferenceCommand.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +/// +/// This is to implement the command design pattern to make changes +/// to references in the csproj file. +/// +internal interface IReferenceCommand { - /// - /// This is to implement the command design pattern to make changes - /// to references in the csproj file. - /// - internal interface IReferenceCommand - { - Task ExecuteAsync(); + Task ExecuteAsync(); - Task UndoAsync(); + Task UndoAsync(); - Task RedoAsync(); - } + Task RedoAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs index 37a64343ea..c0996af60f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/NullCommand.cs @@ -3,18 +3,17 @@ using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal class NullCommand : IProjectSystemUpdateReferenceOperation { - internal class NullCommand : IProjectSystemUpdateReferenceOperation + public Task ApplyAsync(CancellationToken cancellationToken) { - public Task ApplyAsync(CancellationToken cancellationToken) - { - return TaskResult.False; - } + return TaskResult.False; + } - public Task RevertAsync(CancellationToken cancellationToken) - { - return TaskResult.False; - } + public Task RevertAsync(CancellationToken cancellationToken) + { + return TaskResult.False; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/PackageReferenceHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/PackageReferenceHandler.cs index ef0bba2e00..b3bb869835 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/PackageReferenceHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/PackageReferenceHandler.cs @@ -2,35 +2,34 @@ using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal class PackageReferenceHandler : AbstractReferenceHandler { - internal class PackageReferenceHandler : AbstractReferenceHandler - { - internal PackageReferenceHandler() - : base(ProjectSystemReferenceType.Package) - { } + internal PackageReferenceHandler() + : base(ProjectSystemReferenceType.Package) + { } - protected override Task RemoveReferenceAsync(ConfiguredProjectServices services, - string itemSpecification) - { - Assumes.Present(services.PackageReferences); + protected override Task RemoveReferenceAsync(ConfiguredProjectServices services, + string itemSpecification) + { + Assumes.Present(services.PackageReferences); - return services.PackageReferences.RemoveAsync(itemSpecification); - } + return services.PackageReferences.RemoveAsync(itemSpecification); + } - protected override Task AddReferenceAsync(ConfiguredProjectServices services, string itemSpecification) - { - Assumes.Present(services.PackageReferences); + protected override Task AddReferenceAsync(ConfiguredProjectServices services, string itemSpecification) + { + Assumes.Present(services.PackageReferences); - // todo: Get the Version from the Remove Command - return services.PackageReferences.AddAsync(itemSpecification, ""); - } + // todo: Get the Version from the Remove Command + return services.PackageReferences.AddAsync(itemSpecification, ""); + } - protected override async Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services) - { - Assumes.Present(services.PackageReferences); + protected override async Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services) + { + Assumes.Present(services.PackageReferences); - return (await services.PackageReferences.GetUnresolvedReferencesAsync()).Cast(); - } + return (await services.PackageReferences.GetUnresolvedReferencesAsync()).Cast(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ProjectReferenceHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ProjectReferenceHandler.cs index 02f7f8495a..6262d1181e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ProjectReferenceHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ProjectReferenceHandler.cs @@ -2,34 +2,33 @@ using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal class ProjectReferenceHandler : AbstractReferenceHandler { - internal class ProjectReferenceHandler : AbstractReferenceHandler - { - internal ProjectReferenceHandler() - : base(ProjectSystemReferenceType.Project) - { } + internal ProjectReferenceHandler() + : base(ProjectSystemReferenceType.Project) + { } - protected override Task RemoveReferenceAsync(ConfiguredProjectServices services, - string itemSpecification) - { - Assumes.Present(services.ProjectReferences); + protected override Task RemoveReferenceAsync(ConfiguredProjectServices services, + string itemSpecification) + { + Assumes.Present(services.ProjectReferences); - return services.ProjectReferences.RemoveAsync(itemSpecification); - } + return services.ProjectReferences.RemoveAsync(itemSpecification); + } - protected override Task AddReferenceAsync(ConfiguredProjectServices services, string itemSpecification) - { - Assumes.Present(services.ProjectReferences); + protected override Task AddReferenceAsync(ConfiguredProjectServices services, string itemSpecification) + { + Assumes.Present(services.ProjectReferences); - return services.ProjectReferences.AddAsync(itemSpecification); - } + return services.ProjectReferences.AddAsync(itemSpecification); + } - protected override async Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services) - { - Assumes.Present(services.ProjectReferences); + protected override async Task> GetUnresolvedReferencesAsync(ConfiguredProjectServices services) + { + Assumes.Present(services.ProjectReferences); - return (await services.ProjectReferences.GetUnresolvedReferencesAsync()).Cast(); - } + return (await services.ProjectReferences.GetUnresolvedReferencesAsync()).Cast(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ReferenceCleanupService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ReferenceCleanupService.cs index cd0a0b20b6..7d91b23dd2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ReferenceCleanupService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/ReferenceCleanupService.cs @@ -3,129 +3,128 @@ using Microsoft.Build.Exceptions; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References -{ - [Export(typeof(IProjectSystemReferenceCleanupService))] - internal class ReferenceCleanupService : IProjectSystemReferenceCleanupService2 - { - private static readonly Dictionary s_mapReferenceTypeToHandler = - new() - { - { ProjectSystemReferenceType.Project, new ProjectReferenceHandler() }, - { ProjectSystemReferenceType.Package, new PackageReferenceHandler() }, - { ProjectSystemReferenceType.Assembly, new AssemblyReferenceHandler() } - }; - - public static readonly IProjectSystemUpdateReferenceOperation NullCommand = new NullCommand(); +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; - private readonly Lazy _projectService; - protected IProjectService2 ProjectService => _projectService.Value; - - [ImportingConstructor] - public ReferenceCleanupService(IProjectServiceAccessor projectServiceAccessor) +[Export(typeof(IProjectSystemReferenceCleanupService))] +internal class ReferenceCleanupService : IProjectSystemReferenceCleanupService2 +{ + private static readonly Dictionary s_mapReferenceTypeToHandler = + new() { - _projectService = new Lazy( - () => (IProjectService2)projectServiceAccessor.GetProjectService(), - LazyThreadSafetyMode.PublicationOnly); - } + { ProjectSystemReferenceType.Project, new ProjectReferenceHandler() }, + { ProjectSystemReferenceType.Package, new PackageReferenceHandler() }, + { ProjectSystemReferenceType.Assembly, new AssemblyReferenceHandler() } + }; - /// - /// Return the set of direct Project and Package References for the given project. This - /// is used to get the initial state of the TreatAsUsed attribute for each reference. - /// - public async Task> GetProjectReferencesAsync(string projectPath, CancellationToken cancellationToken) - { - List references; + public static readonly IProjectSystemUpdateReferenceOperation NullCommand = new NullCommand(); - try - { - var activeConfiguredProject = await GetActiveConfiguredProjectByPathAsync(projectPath, cancellationToken); + private readonly Lazy _projectService; + protected IProjectService2 ProjectService => _projectService.Value; - references = await GetAllReferencesInConfiguredProjectAsync(activeConfiguredProject, cancellationToken); - } - catch - { - throw new InvalidProjectFileException(); - } + [ImportingConstructor] + public ReferenceCleanupService(IProjectServiceAccessor projectServiceAccessor) + { + _projectService = new Lazy( + () => (IProjectService2)projectServiceAccessor.GetProjectService(), + LazyThreadSafetyMode.PublicationOnly); + } - return references.ToImmutableArray(); - } + /// + /// Return the set of direct Project and Package References for the given project. This + /// is used to get the initial state of the TreatAsUsed attribute for each reference. + /// + public async Task> GetProjectReferencesAsync(string projectPath, CancellationToken cancellationToken) + { + List references; - private async Task GetActiveConfiguredProjectByPathAsync(string projectPath, CancellationToken cancellationToken) + try { - cancellationToken.ThrowIfCancellationRequested(); + var activeConfiguredProject = await GetActiveConfiguredProjectByPathAsync(projectPath, cancellationToken); - var unconfiguredProjectPath = ProjectService.GetLoadedProject(projectPath); + references = await GetAllReferencesInConfiguredProjectAsync(activeConfiguredProject, cancellationToken); + } + catch + { + throw new InvalidProjectFileException(); + } - if (unconfiguredProjectPath is null) - { - throw new InvalidProjectFileException(); - } + return references.ToImmutableArray(); + } - var activeConfiguredProject = await unconfiguredProjectPath.GetSuggestedConfiguredProjectAsync(); + private async Task GetActiveConfiguredProjectByPathAsync(string projectPath, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - if (activeConfiguredProject is null) - { - throw new InvalidProjectFileException(); - } + var unconfiguredProjectPath = ProjectService.GetLoadedProject(projectPath); - return activeConfiguredProject; + if (unconfiguredProjectPath is null) + { + throw new InvalidProjectFileException(); } - private static async Task> GetAllReferencesInConfiguredProjectAsync(ConfiguredProject selectedConfiguredProject, CancellationToken cancellationToken) + var activeConfiguredProject = await unconfiguredProjectPath.GetSuggestedConfiguredProjectAsync(); + + if (activeConfiguredProject is null) { - cancellationToken.ThrowIfCancellationRequested(); + throw new InvalidProjectFileException(); + } - var references = new List(); + return activeConfiguredProject; + } - foreach (var keyValuePair in s_mapReferenceTypeToHandler.Where(h => h.Value is not null)) - { - references.AddRange(await keyValuePair.Value.GetReferencesAsync(selectedConfiguredProject, cancellationToken)); - } + private static async Task> GetAllReferencesInConfiguredProjectAsync(ConfiguredProject selectedConfiguredProject, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - return references; - } + var references = new List(); - public Task TryUpdateReferenceAsync(string projectPath, - ProjectSystemReferenceUpdate referenceUpdate, CancellationToken cancellationToken) + foreach (var keyValuePair in s_mapReferenceTypeToHandler.Where(h => h.Value is not null)) { - throw new NotImplementedException(); + references.AddRange(await keyValuePair.Value.GetReferencesAsync(selectedConfiguredProject, cancellationToken)); } - /// - /// Gets an operation that can update the project’s references by removing or marking references as - /// TreatAsUsed in the project file. - /// - public async Task GetUpdateReferenceOperationAsync(string projectPath, ProjectSystemReferenceUpdate referenceUpdate, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + return references; + } - var activeConfiguredProject = await GetActiveConfiguredProjectByPathAsync(projectPath, cancellationToken); + public Task TryUpdateReferenceAsync(string projectPath, + ProjectSystemReferenceUpdate referenceUpdate, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// + /// Gets an operation that can update the project’s references by removing or marking references as + /// TreatAsUsed in the project file. + /// + public async Task GetUpdateReferenceOperationAsync(string projectPath, ProjectSystemReferenceUpdate referenceUpdate, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - var referenceHandler = s_mapReferenceTypeToHandler[referenceUpdate.ReferenceInfo.ReferenceType]; + var activeConfiguredProject = await GetActiveConfiguredProjectByPathAsync(projectPath, cancellationToken); - IProjectSystemUpdateReferenceOperation? command = null; - if (referenceHandler is not null) - { - command = CreateCommand(referenceUpdate, referenceHandler, activeConfiguredProject, cancellationToken); - } + var referenceHandler = s_mapReferenceTypeToHandler[referenceUpdate.ReferenceInfo.ReferenceType]; - return command ?? NullCommand; + IProjectSystemUpdateReferenceOperation? command = null; + if (referenceHandler is not null) + { + command = CreateCommand(referenceUpdate, referenceHandler, activeConfiguredProject, cancellationToken); } - private static IProjectSystemUpdateReferenceOperation? CreateCommand(ProjectSystemReferenceUpdate referenceUpdate, - AbstractReferenceHandler referenceHandler, - ConfiguredProject selectedConfiguredProject, CancellationToken cancellationToken) - => referenceUpdate.Action switch - { - ProjectSystemUpdateAction.SetTreatAsUsed => referenceHandler.CreateSetAttributeCommand( - selectedConfiguredProject, referenceUpdate), - ProjectSystemUpdateAction.UnsetTreatAsUsed => referenceHandler.CreateUnsetAttributeCommand( - selectedConfiguredProject, referenceUpdate), - ProjectSystemUpdateAction.Remove => referenceHandler.CreateRemoveReferenceCommand( - selectedConfiguredProject, referenceUpdate), - _ => null - }; + return command ?? NullCommand; } + + private static IProjectSystemUpdateReferenceOperation? CreateCommand(ProjectSystemReferenceUpdate referenceUpdate, + AbstractReferenceHandler referenceHandler, + ConfiguredProject selectedConfiguredProject, CancellationToken cancellationToken) + => referenceUpdate.Action switch + { + ProjectSystemUpdateAction.SetTreatAsUsed => referenceHandler.CreateSetAttributeCommand( + selectedConfiguredProject, referenceUpdate), + ProjectSystemUpdateAction.UnsetTreatAsUsed => referenceHandler.CreateUnsetAttributeCommand( + selectedConfiguredProject, referenceUpdate), + ProjectSystemUpdateAction.Remove => referenceHandler.CreateRemoveReferenceCommand( + selectedConfiguredProject, referenceUpdate), + _ => null + }; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs index 53310223fc..197caf3a21 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/RemoveReferenceCommand.cs @@ -2,41 +2,40 @@ using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References -{ - internal class RemoveReferenceCommand : IProjectSystemUpdateReferenceOperation - { - private readonly AbstractReferenceHandler _referenceHandler; - private readonly ConfiguredProject _selectedConfiguredProject; - private readonly string _itemSpecification; +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; - private Dictionary? _projectPropertiesValues; +internal class RemoveReferenceCommand : IProjectSystemUpdateReferenceOperation +{ + private readonly AbstractReferenceHandler _referenceHandler; + private readonly ConfiguredProject _selectedConfiguredProject; + private readonly string _itemSpecification; - public RemoveReferenceCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, ProjectSystemReferenceUpdate referenceUpdate) - { - _referenceHandler = abstractReferenceHandler; - _selectedConfiguredProject = selectedConfiguredProject; - _itemSpecification = referenceUpdate.ReferenceInfo.ItemSpecification; - } + private Dictionary? _projectPropertiesValues; - public async Task ApplyAsync(CancellationToken cancellationToken) - { - _projectPropertiesValues = await _referenceHandler.GetAttributesAsync(_selectedConfiguredProject, _itemSpecification); + public RemoveReferenceCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, ProjectSystemReferenceUpdate referenceUpdate) + { + _referenceHandler = abstractReferenceHandler; + _selectedConfiguredProject = selectedConfiguredProject; + _itemSpecification = referenceUpdate.ReferenceInfo.ItemSpecification; + } - await _referenceHandler.RemoveReferenceAsync(_selectedConfiguredProject, _itemSpecification); - return true; - } + public async Task ApplyAsync(CancellationToken cancellationToken) + { + _projectPropertiesValues = await _referenceHandler.GetAttributesAsync(_selectedConfiguredProject, _itemSpecification); - public async Task RevertAsync(CancellationToken cancellationToken) - { - await _referenceHandler.AddReferenceAsync(_selectedConfiguredProject, _itemSpecification); + await _referenceHandler.RemoveReferenceAsync(_selectedConfiguredProject, _itemSpecification); + return true; + } - if (_projectPropertiesValues is not null) - { - await _referenceHandler.SetAttributesAsync(_selectedConfiguredProject, _itemSpecification, _projectPropertiesValues); - } + public async Task RevertAsync(CancellationToken cancellationToken) + { + await _referenceHandler.AddReferenceAsync(_selectedConfiguredProject, _itemSpecification); - return true; + if (_projectPropertiesValues is not null) + { + await _referenceHandler.SetAttributesAsync(_selectedConfiguredProject, _itemSpecification, _projectPropertiesValues); } + + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetAttributeCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetAttributeCommand.cs index 156be30f0d..0c64db66a6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetAttributeCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetAttributeCommand.cs @@ -2,15 +2,14 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal class SetAttributeCommand : SetTreatAsUsedAttributeCommand { - internal class SetAttributeCommand : SetTreatAsUsedAttributeCommand + public SetAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) + : base(abstractReferenceHandler, selectedConfiguredProject, itemSpecification) { - public SetAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) - : base(abstractReferenceHandler, selectedConfiguredProject, itemSpecification) - { - UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); - SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); - } + UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); + SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs index ed2b723778..83fe46ad10 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/SetTreatAsUsedAttributeCommand.cs @@ -3,58 +3,57 @@ using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal abstract class SetTreatAsUsedAttributeCommand : IProjectSystemUpdateReferenceOperation { - internal abstract class SetTreatAsUsedAttributeCommand : IProjectSystemUpdateReferenceOperation + private readonly ConfiguredProject _selectedConfiguredProject; + private readonly string _itemSpecification; + private readonly AbstractReferenceHandler _referenceHandler; + protected string SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); + protected string UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); + + public SetTreatAsUsedAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) { - private readonly ConfiguredProject _selectedConfiguredProject; - private readonly string _itemSpecification; - private readonly AbstractReferenceHandler _referenceHandler; - protected string SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); - protected string UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); + _referenceHandler = abstractReferenceHandler; + _selectedConfiguredProject = selectedConfiguredProject; + _itemSpecification = itemSpecification; + } - public SetTreatAsUsedAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) - { - _referenceHandler = abstractReferenceHandler; - _selectedConfiguredProject = selectedConfiguredProject; - _itemSpecification = itemSpecification; - } + public async Task ApplyAsync(CancellationToken cancellationToken) + { + IProjectItem item = await GetProjectItemAsync(); - public async Task ApplyAsync(CancellationToken cancellationToken) + if (item is null) { - IProjectItem item = await GetProjectItemAsync(); + return false; + } - if (item is null) - { - return false; - } + await item.Metadata.SetPropertyValueAsync(ProjectReference.TreatAsUsedProperty, SetTreatAsUsed); - await item.Metadata.SetPropertyValueAsync(ProjectReference.TreatAsUsedProperty, SetTreatAsUsed); + return true; + } - return true; - } + public async Task RevertAsync(CancellationToken cancellationToken) + { + IProjectItem item = await GetProjectItemAsync(); - public async Task RevertAsync(CancellationToken cancellationToken) + if (item is null) { - IProjectItem item = await GetProjectItemAsync(); - - if (item is null) - { - return false; - } + return false; + } - await item.Metadata.SetPropertyValueAsync(ProjectReference.TreatAsUsedProperty, UnsetTreatAsUsed); + await item.Metadata.SetPropertyValueAsync(ProjectReference.TreatAsUsedProperty, UnsetTreatAsUsed); - return true; - } + return true; + } - private async Task GetProjectItemAsync() - { - var projectItems = await _referenceHandler.GetUnresolvedReferencesAsync(_selectedConfiguredProject); + private async Task GetProjectItemAsync() + { + var projectItems = await _referenceHandler.GetUnresolvedReferencesAsync(_selectedConfiguredProject); - var item = projectItems - .FirstOrDefault(c => string.CompareOrdinal(c.EvaluatedInclude, _itemSpecification) == 0); - return item; - } + var item = projectItems + .FirstOrDefault(c => string.CompareOrdinal(c.EvaluatedInclude, _itemSpecification) == 0); + return item; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs index 64246463fc..4117e2db10 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/References/UnSetAttributeCommand.cs @@ -2,16 +2,15 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +internal class UnsetAttributeCommand : SetTreatAsUsedAttributeCommand { - internal class UnsetAttributeCommand : SetTreatAsUsedAttributeCommand + public UnsetAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) + : base(abstractReferenceHandler, selectedConfiguredProject, itemSpecification) { - public UnsetAttributeCommand(AbstractReferenceHandler abstractReferenceHandler, ConfiguredProject selectedConfiguredProject, string itemSpecification) - : base(abstractReferenceHandler, selectedConfiguredProject, itemSpecification) - { - UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); - SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); - } + UnsetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(false); + SetTreatAsUsed = PropertySerializer.SimpleTypes.ToString(true); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs index b93b34f9fc..d46edf61fc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/FileMoveNotificationListener.cs @@ -14,188 +14,187 @@ using Path = System.IO.Path; using static Microsoft.CodeAnalysis.Rename.Renamer; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename; + +[Order(Order.Default)] +[Export(typeof(IFileMoveNotificationListener))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal class FileMoveNotificationListener : IFileMoveNotificationListener { - [Order(Order.Default)] - [Export(typeof(IFileMoveNotificationListener))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal class FileMoveNotificationListener : IFileMoveNotificationListener + private static readonly DocumentRenameOptions s_renameOptions = new(); + + private readonly UnconfiguredProject _unconfiguredProject; + private readonly IUserNotificationServices _userNotificationServices; + private readonly Workspace _workspace; + private readonly IProjectThreadingService _threadingService; + private readonly IVsService _operationProgressService; + private readonly IWaitIndicator _waitService; + private readonly IRoslynServices _roslynServices; + private readonly IVsService _settingsManagerService; + + // The file-paths are the full disk path of the source file (path prior to moving the item). + private readonly Dictionary _renameActionSetByFilePath = new(); + private string? _renameMessage; + + [ImportingConstructor] + public FileMoveNotificationListener( + UnconfiguredProject unconfiguredProject, + IUserNotificationServices userNotificationServices, + IUnconfiguredProjectVsServices projectVsServices, + [Import(typeof(VisualStudioWorkspace))] Workspace workspace, + IProjectThreadingService threadingService, + IVsService operationProgressService, + IWaitIndicator waitService, + IRoslynServices roslynServices, + IVsService settingsManagerService) + { + _unconfiguredProject = unconfiguredProject; + _userNotificationServices = userNotificationServices; + _workspace = workspace; + _threadingService = threadingService; + _operationProgressService = operationProgressService; + _waitService = waitService; + _roslynServices = roslynServices; + _settingsManagerService = settingsManagerService; + } + + public async Task OnBeforeFilesMovedAsync(IReadOnlyCollection items) { - private static readonly DocumentRenameOptions s_renameOptions = new(); - - private readonly UnconfiguredProject _unconfiguredProject; - private readonly IUserNotificationServices _userNotificationServices; - private readonly Workspace _workspace; - private readonly IProjectThreadingService _threadingService; - private readonly IVsService _operationProgressService; - private readonly IWaitIndicator _waitService; - private readonly IRoslynServices _roslynServices; - private readonly IVsService _settingsManagerService; - - // The file-paths are the full disk path of the source file (path prior to moving the item). - private readonly Dictionary _renameActionSetByFilePath = new(); - private string? _renameMessage; - - [ImportingConstructor] - public FileMoveNotificationListener( - UnconfiguredProject unconfiguredProject, - IUserNotificationServices userNotificationServices, - IUnconfiguredProjectVsServices projectVsServices, - [Import(typeof(VisualStudioWorkspace))] Workspace workspace, - IProjectThreadingService threadingService, - IVsService operationProgressService, - IWaitIndicator waitService, - IRoslynServices roslynServices, - IVsService settingsManagerService) + Project? project = _workspace.CurrentSolution.Projects.FirstOrDefault(p => StringComparers.Paths.Equals(p.FilePath, _unconfiguredProject.FullPath)); + if (project is null) { - _unconfiguredProject = unconfiguredProject; - _userNotificationServices = userNotificationServices; - _workspace = workspace; - _threadingService = threadingService; - _operationProgressService = operationProgressService; - _waitService = waitService; - _roslynServices = roslynServices; - _settingsManagerService = settingsManagerService; + return; } - public async Task OnBeforeFilesMovedAsync(IReadOnlyCollection items) + foreach (IFileMoveItem itemToMove in GetFilesToMove(items)) { - Project? project = _workspace.CurrentSolution.Projects.FirstOrDefault(p => StringComparers.Paths.Equals(p.FilePath, _unconfiguredProject.FullPath)); - if (project is null) + Document? currentDocument = project.Documents.FirstOrDefault(d => StringComparers.Paths.Equals(d.FilePath, itemToMove.Source)); + if (currentDocument is null) { - return; + continue; } - foreach (IFileMoveItem itemToMove in GetFilesToMove(items)) - { - Document? currentDocument = project.Documents.FirstOrDefault(d => StringComparers.Paths.Equals(d.FilePath, itemToMove.Source)); - if (currentDocument is null) - { - continue; - } - - // Get the relative folder path from the project to the destination. - string destinationFolderPath = Path.GetDirectoryName(_unconfiguredProject.MakeRelative(itemToMove.Destination)); - string[] destinationFolders = destinationFolderPath.Split(Delimiter.Path, StringSplitOptions.RemoveEmptyEntries); + // Get the relative folder path from the project to the destination. + string destinationFolderPath = Path.GetDirectoryName(_unconfiguredProject.MakeRelative(itemToMove.Destination)); + string[] destinationFolders = destinationFolderPath.Split(Delimiter.Path, StringSplitOptions.RemoveEmptyEntries); - // Since this rename only moves the location of the file to another directory, it will use the SyncNamespaceDocumentAction in Roslyn as the rename action within this set. - // The logic for selecting this rename action can be found here: https://github.com/dotnet/roslyn/blob/960f375f4825a189937d4bfd9fea8162ecc63177/src/Workspaces/Core/Portable/Rename/Renamer.cs#L133-L136 - RenameDocumentActionSet renameActionSet = await RenameDocumentAsync(currentDocument, s_renameOptions, null, destinationFolders); - if (renameActionSet.ApplicableActions.IsEmpty || renameActionSet.ApplicableActions.Any(aa => aa.GetErrors().Any())) - { - continue; - } + // Since this rename only moves the location of the file to another directory, it will use the SyncNamespaceDocumentAction in Roslyn as the rename action within this set. + // The logic for selecting this rename action can be found here: https://github.com/dotnet/roslyn/blob/960f375f4825a189937d4bfd9fea8162ecc63177/src/Workspaces/Core/Portable/Rename/Renamer.cs#L133-L136 + RenameDocumentActionSet renameActionSet = await RenameDocumentAsync(currentDocument, s_renameOptions, null, destinationFolders); + if (renameActionSet.ApplicableActions.IsEmpty || renameActionSet.ApplicableActions.Any(aa => aa.GetErrors().Any())) + { + continue; + } - // Getting the rename message requires an instance of RenameDocumentAction. - // We only need to set this message text once for the lifetime of the class, since it isn't dynamic. - // Even though it isn't dynamic, it does get localized appropriately in Roslyn. - // The text in English is "Sync namespace to folder structure". - _renameMessage ??= renameActionSet.ApplicableActions.First().GetDescription(); + // Getting the rename message requires an instance of RenameDocumentAction. + // We only need to set this message text once for the lifetime of the class, since it isn't dynamic. + // Even though it isn't dynamic, it does get localized appropriately in Roslyn. + // The text in English is "Sync namespace to folder structure". + _renameMessage ??= renameActionSet.ApplicableActions.First().GetDescription(); - // Add the full source file-path of the item as the key for the rename action set. - _renameActionSetByFilePath.Add(itemToMove.Source, renameActionSet); - } + // Add the full source file-path of the item as the key for the rename action set. + _renameActionSetByFilePath.Add(itemToMove.Source, renameActionSet); + } - return; + return; - static IEnumerable GetFilesToMove(IEnumerable items) + static IEnumerable GetFilesToMove(IEnumerable items) + { + var itemQueue = new Queue(items); + while (itemQueue.Count > 0) { - var itemQueue = new Queue(items); - while (itemQueue.Count > 0) - { - IFileMoveItem item = itemQueue.Dequeue(); + IFileMoveItem item = itemQueue.Dequeue(); - // Termination condition - if (item is { WithinProject: true, IsFolder: false, IsLinked: false } && - StringComparers.ItemTypes.Equals(item.ItemType, Compile.SchemaName)) - { - yield return item; - continue; - } + // Termination condition + if (item is { WithinProject: true, IsFolder: false, IsLinked: false } && + StringComparers.ItemTypes.Equals(item.ItemType, Compile.SchemaName)) + { + yield return item; + continue; + } - // Folder navigation - if (item is { IsFolder: true } and ICopyPasteItem copyPasteItem) + // Folder navigation + if (item is { IsFolder: true } and ICopyPasteItem copyPasteItem) + { + IEnumerable children = copyPasteItem.Children.Select(c => c as IFileMoveItem).WhereNotNull(); + foreach (IFileMoveItem child in children) { - IEnumerable children = copyPasteItem.Children.Select(c => c as IFileMoveItem).WhereNotNull(); - foreach (IFileMoveItem child in children) - { - itemQueue.Enqueue(child); - } + itemQueue.Enqueue(child); } } } } + } - public async Task OnAfterFileMoveAsync() + public async Task OnAfterFileMoveAsync() + { + if (!_renameActionSetByFilePath.Any() || !await IsEnabledOrConfirmedAsync()) { - if (!_renameActionSetByFilePath.Any() || !await IsEnabledOrConfirmedAsync()) - { - // Clear the collection since the user declined (or has disabled) the rename namespace option. - _renameActionSetByFilePath.Clear(); - return; - } + // Clear the collection since the user declined (or has disabled) the rename namespace option. + _renameActionSetByFilePath.Clear(); + return; + } - // Display a dialog showing the progress of updating the namespaces in the files. - _ = _waitService.RunAsync( - title: string.Empty, - message: _renameMessage!, - allowCancel: true, - asyncMethod: ApplyRenamesAsync, - totalSteps: _renameActionSetByFilePath.Count); + // Display a dialog showing the progress of updating the namespaces in the files. + _ = _waitService.RunAsync( + title: string.Empty, + message: _renameMessage!, + allowCancel: true, + asyncMethod: ApplyRenamesAsync, + totalSteps: _renameActionSetByFilePath.Count); - return; + return; - async Task ApplyRenamesAsync(IWaitContext context) + async Task ApplyRenamesAsync(IWaitContext context) + { + CancellationToken token = context.CancellationToken; + await TaskScheduler.Default; + // WORKAROUND: We don't yet have a way to wait for the changes to propagate to Roslyn, tracked by https://github.com/dotnet/project-system/issues/3425 + // Instead, we wait for the IntelliSense stage to finish for the entire solution. + IVsOperationProgressStatusService statusService = await _operationProgressService.GetValueAsync(token); + await statusService.GetStageStatus(CommonOperationProgressStageIds.Intellisense).WaitForCompletionAsync().WithCancellation(token); + // After waiting, a "new" published Solution is available. + Solution solution = _workspace.CurrentSolution; + + int currentStep = 1; + foreach ((string filePath, RenameDocumentActionSet renameActionSet) in _renameActionSetByFilePath) { - CancellationToken token = context.CancellationToken; - await TaskScheduler.Default; - // WORKAROUND: We don't yet have a way to wait for the changes to propagate to Roslyn, tracked by https://github.com/dotnet/project-system/issues/3425 - // Instead, we wait for the IntelliSense stage to finish for the entire solution. - IVsOperationProgressStatusService statusService = await _operationProgressService.GetValueAsync(token); - await statusService.GetStageStatus(CommonOperationProgressStageIds.Intellisense).WaitForCompletionAsync().WithCancellation(token); - // After waiting, a "new" published Solution is available. - Solution solution = _workspace.CurrentSolution; - - int currentStep = 1; - foreach ((string filePath, RenameDocumentActionSet renameActionSet) in _renameActionSetByFilePath) - { - // Display the filename being updated to the user in the progress dialog. - context.Update(currentStep: currentStep++, progressText: Path.GetFileName(filePath)); + // Display the filename being updated to the user in the progress dialog. + context.Update(currentStep: currentStep++, progressText: Path.GetFileName(filePath)); - solution = await renameActionSet.UpdateSolutionAsync(solution, token); - } - - await _threadingService.SwitchToUIThread(token); - bool areChangesApplied = _roslynServices.ApplyChangesToSolution(_workspace, solution); - DiagDebug.Assert(areChangesApplied, "ApplyChangesToSolution returned false"); - // Clear the collection after it has been processed. - _renameActionSetByFilePath.Clear(); + solution = await renameActionSet.UpdateSolutionAsync(solution, token); } - async Task IsEnabledOrConfirmedAsync() + await _threadingService.SwitchToUIThread(token); + bool areChangesApplied = _roslynServices.ApplyChangesToSolution(_workspace, solution); + DiagDebug.Assert(areChangesApplied, "ApplyChangesToSolution returned false"); + // Clear the collection after it has been processed. + _renameActionSetByFilePath.Clear(); + } + + async Task IsEnabledOrConfirmedAsync() + { + ISettingsManager settings = await _settingsManagerService.GetValueAsync(); + + bool isEnabled = settings.GetValueOrDefault(VsToolsOptions.OptionEnableNamespaceUpdate, defaultValue: true); + bool isPromptEnabled = settings.GetValueOrDefault(VsToolsOptions.OptionPromptNamespaceUpdate, defaultValue: true); + // If not enabled, returns false. + // If enabled but prompt is not enabled, returns true. + // Otherwise, we display the prompt to the user. + if (!isEnabled || !isPromptEnabled) { - ISettingsManager settings = await _settingsManagerService.GetValueAsync(); - - bool isEnabled = settings.GetValueOrDefault(VsToolsOptions.OptionEnableNamespaceUpdate, defaultValue: true); - bool isPromptEnabled = settings.GetValueOrDefault(VsToolsOptions.OptionPromptNamespaceUpdate, defaultValue: true); - // If not enabled, returns false. - // If enabled but prompt is not enabled, returns true. - // Otherwise, we display the prompt to the user. - if (!isEnabled || !isPromptEnabled) - { - return isEnabled; - } + return isEnabled; + } - await _threadingService.SwitchToUIThread(); - bool isConfirmed = _userNotificationServices.Confirm(VSResources.UpdateNamespacePromptMessage, out bool disablePromptMessage); - await settings.SetValueAsync(VsToolsOptions.OptionPromptNamespaceUpdate, !disablePromptMessage, isMachineLocal: true); - // If the user checked the "Don't show again" checkbox, we need to set the namespace enable state based on their selection of Yes/No in the dialog. - if (disablePromptMessage) - { - await settings.SetValueAsync(VsToolsOptions.OptionEnableNamespaceUpdate, isConfirmed, isMachineLocal: true); - } - return isConfirmed; + await _threadingService.SwitchToUIThread(); + bool isConfirmed = _userNotificationServices.Confirm(VSResources.UpdateNamespacePromptMessage, out bool disablePromptMessage); + await settings.SetValueAsync(VsToolsOptions.OptionPromptNamespaceUpdate, !disablePromptMessage, isMachineLocal: true); + // If the user checked the "Don't show again" checkbox, we need to set the namespace enable state based on their selection of Yes/No in the dialog. + if (disablePromptMessage) + { + await settings.SetValueAsync(VsToolsOptions.OptionEnableNamespaceUpdate, isConfirmed, isMachineLocal: true); } + return isConfirmed; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.UndoTransaction.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.UndoTransaction.cs index 872c677656..6a978a132b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.UndoTransaction.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.UndoTransaction.cs @@ -2,45 +2,44 @@ using EnvDTE; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename; + +internal partial class RenamerProjectTreeActionHandler { - internal partial class RenamerProjectTreeActionHandler + private sealed class UndoScope : IDisposable { - private sealed class UndoScope : IDisposable + private readonly string _renameOperationName; + private readonly DTE _dte; + private bool _shouldClose = true; + + private UndoScope(string renameOperationName, DTE dte) { - private readonly string _renameOperationName; - private readonly DTE _dte; - private bool _shouldClose = true; + _renameOperationName = renameOperationName; + _dte = dte; + } - private UndoScope(string renameOperationName, DTE dte) - { - _renameOperationName = renameOperationName; - _dte = dte; - } + internal static UndoScope Create(DTE dte, string renameOperationName) + { + var undo = new UndoScope(renameOperationName, dte); + undo.StartUndo(); + return undo; + } - internal static UndoScope Create(DTE dte, string renameOperationName) + private void StartUndo() + { + if (_dte.UndoContext.IsOpen) { - var undo = new UndoScope(renameOperationName, dte); - undo.StartUndo(); - return undo; + _shouldClose = false; } - private void StartUndo() - { - if (_dte.UndoContext.IsOpen) - { - _shouldClose = false; - } - - _dte.UndoContext.Open(_renameOperationName, false); - } + _dte.UndoContext.Open(_renameOperationName, false); + } - public void Dispose() + public void Dispose() + { + if (_shouldClose) { - if (_shouldClose) - { - _dte.UndoContext.Close(); - } + _dte.UndoContext.Close(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs index 69ca2440ee..38c1c36986 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rename/RenamerProjectTreeActionHandler.cs @@ -12,246 +12,245 @@ using Microsoft.VisualStudio.Threading; using Solution = Microsoft.CodeAnalysis.Solution; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename; + +[Order(Order.Default)] +[Export(typeof(IProjectTreeActionHandler))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal partial class RenamerProjectTreeActionHandler : ProjectTreeActionHandlerBase { - [Order(Order.Default)] - [Export(typeof(IProjectTreeActionHandler))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal partial class RenamerProjectTreeActionHandler : ProjectTreeActionHandlerBase + private static readonly DocumentRenameOptions s_renameOptions = new(); + + private readonly IEnvironmentOptions _environmentOptions; + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IProjectThreadingService _threadingService; + private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; + private readonly UnconfiguredProject _unconfiguredProject; + private readonly IVsUIService _extensibility; + private readonly IVsOnlineServices _vsOnlineServices; + private readonly IUserNotificationServices _userNotificationServices; + private readonly IWaitIndicator _waitService; + private readonly IRoslynServices _roslynServices; + private readonly Lazy _workspace; + private readonly IVsService _operationProgressService; + private readonly IVsService _settingsManagerService; + + [ImportingConstructor] + public RenamerProjectTreeActionHandler( + UnconfiguredProject unconfiguredProject, + IUnconfiguredProjectVsServices projectVsServices, + [Import(typeof(VisualStudioWorkspace))] Lazy workspace, + IEnvironmentOptions environmentOptions, + IUserNotificationServices userNotificationServices, + IRoslynServices roslynServices, + IWaitIndicator waitService, + IVsOnlineServices vsOnlineServices, + [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, + IProjectThreadingService threadingService, + IVsUIService extensibility, + IVsService operationProgressService, + IVsService settingsManagerService) + { + _unconfiguredProject = unconfiguredProject; + _projectVsServices = projectVsServices; + _workspace = workspace; + _environmentOptions = environmentOptions; + _userNotificationServices = userNotificationServices; + _roslynServices = roslynServices; + _waitService = waitService; + _vsOnlineServices = vsOnlineServices; + _projectAsynchronousTasksService = projectAsynchronousTasksService; + _threadingService = threadingService; + _extensibility = extensibility; + _operationProgressService = operationProgressService; + _settingsManagerService = settingsManagerService; + } + + protected virtual Task CpsFileRenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) { - private static readonly DocumentRenameOptions s_renameOptions = new(); - - private readonly IEnvironmentOptions _environmentOptions; - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IProjectThreadingService _threadingService; - private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; - private readonly UnconfiguredProject _unconfiguredProject; - private readonly IVsUIService _extensibility; - private readonly IVsOnlineServices _vsOnlineServices; - private readonly IUserNotificationServices _userNotificationServices; - private readonly IWaitIndicator _waitService; - private readonly IRoslynServices _roslynServices; - private readonly Lazy _workspace; - private readonly IVsService _operationProgressService; - private readonly IVsService _settingsManagerService; - - [ImportingConstructor] - public RenamerProjectTreeActionHandler( - UnconfiguredProject unconfiguredProject, - IUnconfiguredProjectVsServices projectVsServices, - [Import(typeof(VisualStudioWorkspace))] Lazy workspace, - IEnvironmentOptions environmentOptions, - IUserNotificationServices userNotificationServices, - IRoslynServices roslynServices, - IWaitIndicator waitService, - IVsOnlineServices vsOnlineServices, - [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, - IProjectThreadingService threadingService, - IVsUIService extensibility, - IVsService operationProgressService, - IVsService settingsManagerService) + return base.RenameAsync(context, node, value); + } + + public override async Task RenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) + { + Requires.NotNull(context); + Requires.NotNull(node); + Requires.NotNullOrEmpty(value); + + string? oldFilePath = node.FilePath; + string oldName = Path.GetFileNameWithoutExtension(oldFilePath); + string newFileWithExtension = value; + CodeAnalysis.Project? project = GetCurrentProject(); + + await CpsFileRenameAsync(context, node, value); + + if (project is null || + await IsAutomationFunctionAsync() || + node.IsFolder || + _vsOnlineServices.ConnectedToVSOnline || + FileChangedExtension(oldFilePath, newFileWithExtension)) { - _unconfiguredProject = unconfiguredProject; - _projectVsServices = projectVsServices; - _workspace = workspace; - _environmentOptions = environmentOptions; - _userNotificationServices = userNotificationServices; - _roslynServices = roslynServices; - _waitService = waitService; - _vsOnlineServices = vsOnlineServices; - _projectAsynchronousTasksService = projectAsynchronousTasksService; - _threadingService = threadingService; - _extensibility = extensibility; - _operationProgressService = operationProgressService; - _settingsManagerService = settingsManagerService; + // Do not display rename Prompt + return; } - protected virtual Task CpsFileRenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) + string newName = Path.GetFileNameWithoutExtension(newFileWithExtension); + if (!await CanRenameTypeAsync(project, oldName, newName)) { - return base.RenameAsync(context, node, value); + return; } - public override async Task RenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) + (bool result, Renamer.RenameDocumentActionSet? documentRenameResult) = await GetRenameSymbolsActionsAsync(project, oldFilePath, newFileWithExtension); + if (!result || documentRenameResult is null) { - Requires.NotNull(context); - Requires.NotNull(node); - Requires.NotNullOrEmpty(value); - - string? oldFilePath = node.FilePath; - string oldName = Path.GetFileNameWithoutExtension(oldFilePath); - string newFileWithExtension = value; - CodeAnalysis.Project? project = GetCurrentProject(); - - await CpsFileRenameAsync(context, node, value); - - if (project is null || - await IsAutomationFunctionAsync() || - node.IsFolder || - _vsOnlineServices.ConnectedToVSOnline || - FileChangedExtension(oldFilePath, newFileWithExtension)) - { - // Do not display rename Prompt - return; - } + return; + } - string newName = Path.GetFileNameWithoutExtension(newFileWithExtension); - if (!await CanRenameTypeAsync(project, oldName, newName)) - { - return; - } + // Ask if the user wants to rename the symbol + bool userWantsToRenameSymbol = await CheckUserConfirmationAsync(oldName); + if (!userWantsToRenameSymbol) + { + return; + } - (bool result, Renamer.RenameDocumentActionSet? documentRenameResult) = await GetRenameSymbolsActionsAsync(project, oldFilePath, newFileWithExtension); - if (!result || documentRenameResult is null) + _threadingService.RunAndForget(async () => + { + Solution currentSolution = await PublishLatestSolutionAsync(_projectAsynchronousTasksService.UnloadCancellationToken); + + string renameOperationName = string.Format(CultureInfo.CurrentCulture, VSResources.Renaming_Type_from_0_to_1, oldName, value); + WaitIndicatorResult indicatorResult = await _waitService.RunAsync( + title: VSResources.Renaming_Type, + message: renameOperationName, + allowCancel: true, + context => documentRenameResult.UpdateSolutionAsync(currentSolution, context.CancellationToken)); + + // Do not warn the user if the rename was cancelled by the user + if (indicatorResult.IsCancelled) { return; } - // Ask if the user wants to rename the symbol - bool userWantsToRenameSymbol = await CheckUserConfirmationAsync(oldName); - if (!userWantsToRenameSymbol) + await _projectVsServices.ThreadingService.SwitchToUIThread(); + if (_roslynServices.ApplyChangesToSolution(currentSolution.Workspace, indicatorResult.Result)) { return; } - _threadingService.RunAndForget(async () => - { - Solution currentSolution = await PublishLatestSolutionAsync(_projectAsynchronousTasksService.UnloadCancellationToken); - - string renameOperationName = string.Format(CultureInfo.CurrentCulture, VSResources.Renaming_Type_from_0_to_1, oldName, value); - WaitIndicatorResult indicatorResult = await _waitService.RunAsync( - title: VSResources.Renaming_Type, - message: renameOperationName, - allowCancel: true, - context => documentRenameResult.UpdateSolutionAsync(currentSolution, context.CancellationToken)); - - // Do not warn the user if the rename was cancelled by the user - if (indicatorResult.IsCancelled) - { - return; - } - - await _projectVsServices.ThreadingService.SwitchToUIThread(); - if (_roslynServices.ApplyChangesToSolution(currentSolution.Workspace, indicatorResult.Result)) - { - return; - } - - string failureMessage = string.Format(CultureInfo.CurrentCulture, VSResources.RenameSymbolFailed, oldName); - _userNotificationServices.ShowWarning(failureMessage); - }, _unconfiguredProject); - } + string failureMessage = string.Format(CultureInfo.CurrentCulture, VSResources.RenameSymbolFailed, oldName); + _userNotificationServices.ShowWarning(failureMessage); + }, _unconfiguredProject); + } - private static bool FileChangedExtension(string? oldFilePath, string newFileWithExtension) - => !StringComparers.Paths.Equals(Path.GetExtension(oldFilePath), Path.GetExtension(newFileWithExtension)); + private static bool FileChangedExtension(string? oldFilePath, string newFileWithExtension) + => !StringComparers.Paths.Equals(Path.GetExtension(oldFilePath), Path.GetExtension(newFileWithExtension)); - private async Task PublishLatestSolutionAsync(CancellationToken cancellationToken) - { - // WORKAROUND: We don't yet have a way to wait for the rename changes to propagate - // to Roslyn (tracked by https://github.com/dotnet/project-system/issues/3425), so - // instead we wait for the IntelliSense stage to finish for the entire solution - IVsOperationProgressStatusService operationProgressStatusService = await _operationProgressService.GetValueAsync(cancellationToken); - IVsOperationProgressStageStatus stageStatus = operationProgressStatusService.GetStageStatus(CommonOperationProgressStageIds.Intellisense); + private async Task PublishLatestSolutionAsync(CancellationToken cancellationToken) + { + // WORKAROUND: We don't yet have a way to wait for the rename changes to propagate + // to Roslyn (tracked by https://github.com/dotnet/project-system/issues/3425), so + // instead we wait for the IntelliSense stage to finish for the entire solution + IVsOperationProgressStatusService operationProgressStatusService = await _operationProgressService.GetValueAsync(cancellationToken); + IVsOperationProgressStageStatus stageStatus = operationProgressStatusService.GetStageStatus(CommonOperationProgressStageIds.Intellisense); - await stageStatus.WaitForCompletionAsync().WithCancellation(cancellationToken); + await stageStatus.WaitForCompletionAsync().WithCancellation(cancellationToken); - // The result of that wait, is basically a "new" published Solution, so grab it - return _workspace.Value.CurrentSolution; - } + // The result of that wait, is basically a "new" published Solution, so grab it + return _workspace.Value.CurrentSolution; + } - private static async Task<(bool, Renamer.RenameDocumentActionSet?)> GetRenameSymbolsActionsAsync(CodeAnalysis.Project project, string? oldFilePath, string newFileWithExtension) + private static async Task<(bool, Renamer.RenameDocumentActionSet?)> GetRenameSymbolsActionsAsync(CodeAnalysis.Project project, string? oldFilePath, string newFileWithExtension) + { + CodeAnalysis.Document? oldDocument = GetDocument(project, oldFilePath); + if (oldDocument is null) { - CodeAnalysis.Document? oldDocument = GetDocument(project, oldFilePath); - if (oldDocument is null) - { - return (false, null); - } - - // Get the list of possible actions to execute - Renamer.RenameDocumentActionSet documentRenameResult = await Renamer.RenameDocumentAsync(oldDocument, s_renameOptions, newFileWithExtension); - - // Check if there are any symbols that need to be renamed - if (documentRenameResult.ApplicableActions.IsEmpty) - { - return (false, documentRenameResult); - } + return (false, null); + } - // Check errors before applying changes - if (documentRenameResult.ApplicableActions.Any(a => !a.GetErrors().IsEmpty)) - { - return (false, documentRenameResult); - } + // Get the list of possible actions to execute + Renamer.RenameDocumentActionSet documentRenameResult = await Renamer.RenameDocumentAsync(oldDocument, s_renameOptions, newFileWithExtension); - return (true, documentRenameResult); + // Check if there are any symbols that need to be renamed + if (documentRenameResult.ApplicableActions.IsEmpty) + { + return (false, documentRenameResult); } - private async Task CanRenameTypeAsync(CodeAnalysis.Project? project, string oldName, string newName) + // Check errors before applying changes + if (documentRenameResult.ApplicableActions.Any(a => !a.GetErrors().IsEmpty)) { - // see if the current project contains a compilation - (bool success, bool isCaseSensitive) = await TryDetermineIfCompilationIsCaseSensitiveAsync(project); - - return success && CanHandleRename(oldName, newName, isCaseSensitive); + return (false, documentRenameResult); } - private bool CanHandleRename(string oldName, string newName, bool isCaseSensitive) - => _roslynServices.IsValidIdentifier(oldName) && - _roslynServices.IsValidIdentifier(newName) && - (!string.Equals( - oldName, - newName, - isCaseSensitive - ? StringComparisons.LanguageIdentifiers - : StringComparisons.LanguageIdentifiersIgnoreCase)); - - private static async Task<(bool success, bool isCaseSensitive)> TryDetermineIfCompilationIsCaseSensitiveAsync(CodeAnalysis.Project? project) - { - if (project is null) - return (false, false); + return (true, documentRenameResult); + } - Compilation? compilation = await project.GetCompilationAsync(); - return compilation is null ? (false, false) : (true, compilation.IsCaseSensitive); - } + private async Task CanRenameTypeAsync(CodeAnalysis.Project? project, string oldName, string newName) + { + // see if the current project contains a compilation + (bool success, bool isCaseSensitive) = await TryDetermineIfCompilationIsCaseSensitiveAsync(project); - protected virtual async Task IsAutomationFunctionAsync() - { - await _threadingService.SwitchToUIThread(); + return success && CanHandleRename(oldName, newName, isCaseSensitive); + } - _extensibility.Value.IsInAutomationFunction(out int isInAutomationFunction); - return isInAutomationFunction != 0; - } + private bool CanHandleRename(string oldName, string newName, bool isCaseSensitive) + => _roslynServices.IsValidIdentifier(oldName) && + _roslynServices.IsValidIdentifier(newName) && + (!string.Equals( + oldName, + newName, + isCaseSensitive + ? StringComparisons.LanguageIdentifiers + : StringComparisons.LanguageIdentifiersIgnoreCase)); + + private static async Task<(bool success, bool isCaseSensitive)> TryDetermineIfCompilationIsCaseSensitiveAsync(CodeAnalysis.Project? project) + { + if (project is null) + return (false, false); - private CodeAnalysis.Project? GetCurrentProject() - => _workspace.Value.CurrentSolution.Projects.FirstOrDefault(proj => StringComparers.Paths.Equals(proj.FilePath, _projectVsServices.Project.FullPath)); + Compilation? compilation = await project.GetCompilationAsync(); + return compilation is null ? (false, false) : (true, compilation.IsCaseSensitive); + } - private static CodeAnalysis.Document GetDocument(CodeAnalysis.Project project, string? filePath) - => project.Documents.FirstOrDefault(d => StringComparers.Paths.Equals(d.FilePath, filePath)); + protected virtual async Task IsAutomationFunctionAsync() + { + await _threadingService.SwitchToUIThread(); - private async Task CheckUserConfirmationAsync(string oldFileName) - { - ISettingsManager settings = await _settingsManagerService.GetValueAsync(); + _extensibility.Value.IsInAutomationFunction(out int isInAutomationFunction); + return isInAutomationFunction != 0; + } - // Default value needs to match the default value in the checkbox Tools|Options|Project and Solutions|Enable symbolic renaming. - bool enableSymbolicRename = settings.GetValueOrDefault(VsToolsOptions.OptionEnableSymbolicRename, true); + private CodeAnalysis.Project? GetCurrentProject() + => _workspace.Value.CurrentSolution.Projects.FirstOrDefault(proj => StringComparers.Paths.Equals(proj.FilePath, _projectVsServices.Project.FullPath)); - await _projectVsServices.ThreadingService.SwitchToUIThread(); + private static CodeAnalysis.Document GetDocument(CodeAnalysis.Project project, string? filePath) + => project.Documents.FirstOrDefault(d => StringComparers.Paths.Equals(d.FilePath, filePath)); - bool userNeedPrompt = _environmentOptions.GetOption(VsToolsOptions.CategoryEnvironment, VsToolsOptions.PageProjectsAndSolution, VsToolsOptions.OptionPromptRenameSymbol, false); + private async Task CheckUserConfirmationAsync(string oldFileName) + { + ISettingsManager settings = await _settingsManagerService.GetValueAsync(); - if (!enableSymbolicRename || !userNeedPrompt) - { - return enableSymbolicRename; - } + // Default value needs to match the default value in the checkbox Tools|Options|Project and Solutions|Enable symbolic renaming. + bool enableSymbolicRename = settings.GetValueOrDefault(VsToolsOptions.OptionEnableSymbolicRename, true); - string renamePromptMessage = string.Format(CultureInfo.CurrentCulture, VSResources.RenameSymbolPrompt, oldFileName); + await _projectVsServices.ThreadingService.SwitchToUIThread(); - bool shouldRename = _userNotificationServices.Confirm(renamePromptMessage, out bool disablePromptMessage); + bool userNeedPrompt = _environmentOptions.GetOption(VsToolsOptions.CategoryEnvironment, VsToolsOptions.PageProjectsAndSolution, VsToolsOptions.OptionPromptRenameSymbol, false); - if (disablePromptMessage) - { - await settings.SetValueAsync(VsToolsOptions.OptionEnableSymbolicRename, shouldRename, isMachineLocal: true); - _environmentOptions.SetOption(VsToolsOptions.CategoryEnvironment, VsToolsOptions.PageProjectsAndSolution, VsToolsOptions.OptionPromptRenameSymbol, !disablePromptMessage); - } + if (!enableSymbolicRename || !userNeedPrompt) + { + return enableSymbolicRename; + } + + string renamePromptMessage = string.Format(CultureInfo.CurrentCulture, VSResources.RenameSymbolPrompt, oldFileName); - return shouldRename; + bool shouldRename = _userNotificationServices.Confirm(renamePromptMessage, out bool disablePromptMessage); + + if (disablePromptMessage) + { + await settings.SetValueAsync(VsToolsOptions.OptionEnableSymbolicRename, shouldRename, isMachineLocal: true); + _environmentOptions.SetOption(VsToolsOptions.CategoryEnvironment, VsToolsOptions.PageProjectsAndSolution, VsToolsOptions.OptionPromptRenameSymbol, !disablePromptMessage); } + + return shouldRename; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/RoslynServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/RoslynServices.cs index 38c6b63501..087079e6e7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/RoslynServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/RoslynServices.cs @@ -5,38 +5,37 @@ using Workspace = Microsoft.CodeAnalysis.Workspace; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[Export(typeof(IRoslynServices))] +internal class RoslynServices : IRoslynServices { - [Export(typeof(IRoslynServices))] - internal class RoslynServices : IRoslynServices + private readonly IProjectThreadingService _threadingService; + + [ImportingConstructor] + public RoslynServices( + IProjectThreadingService threadingService, + UnconfiguredProject project) + { + _threadingService = threadingService; + SyntaxFactsServicesImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } + + [ImportMany] + protected OrderPrecedenceImportCollection SyntaxFactsServicesImpl { get; } + + private ISyntaxFactsService? SyntaxFactsService => SyntaxFactsServicesImpl.FirstOrDefault()?.Value; + + public bool ApplyChangesToSolution(Workspace ws, Solution renamedSolution) + { + _threadingService.VerifyOnUIThread(); + + // Always make sure TryApplyChanges is called from an UI thread. + return ws.TryApplyChanges(renamedSolution); + } + + public bool IsValidIdentifier(string identifierName) { - private readonly IProjectThreadingService _threadingService; - - [ImportingConstructor] - public RoslynServices( - IProjectThreadingService threadingService, - UnconfiguredProject project) - { - _threadingService = threadingService; - SyntaxFactsServicesImpl = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } - - [ImportMany] - protected OrderPrecedenceImportCollection SyntaxFactsServicesImpl { get; } - - private ISyntaxFactsService? SyntaxFactsService => SyntaxFactsServicesImpl.FirstOrDefault()?.Value; - - public bool ApplyChangesToSolution(Workspace ws, Solution renamedSolution) - { - _threadingService.VerifyOnUIThread(); - - // Always make sure TryApplyChanges is called from an UI thread. - return ws.TryApplyChanges(renamedSolution); - } - - public bool IsValidIdentifier(string identifierName) - { - return SyntaxFactsService?.IsValidIdentifier(identifierName) ?? false; - } + return SyntaxFactsService?.IsValidIdentifier(identifierName) ?? false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/BuildAccelerationIncompatiblePackage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/BuildAccelerationIncompatiblePackage.cs index 67352f5155..937c2cc524 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/BuildAccelerationIncompatiblePackage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/BuildAccelerationIncompatiblePackage.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class BuildAccelerationIncompatiblePackage { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class BuildAccelerationIncompatiblePackage - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyToOutputDirectoryItem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyToOutputDirectoryItem.cs index 668092743b..abe3f455d5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyToOutputDirectoryItem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyToOutputDirectoryItem.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CopyToOutputDirectoryItem { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CopyToOutputDirectoryItem - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyUpToDateMarker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyUpToDateMarker.cs index a4b9d17627..7ea22737a3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyUpToDateMarker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/CopyUpToDateMarker.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CopyUpToDateMarker { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CopyUpToDateMarker - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ExportVSRuleAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ExportVSRuleAttribute.cs index 54704e9dbe..21303480f0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ExportVSRuleAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ExportVSRuleAttribute.cs @@ -2,38 +2,37 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +/// +/// Exports a XAML-based embedded rule. +/// +[AttributeUsage(AttributeTargets.Field)] +internal sealed class ExportVSRuleAttribute : ExportPropertyXamlRuleDefinitionAttribute { + // TODO: If reflection is insufficient, this will also work. + //private const string AssemblyFullName = $"{ThisAssembly.AssemblyName}, Version = {ThisAssembly.AssemblyVersion}, Culture = neutral, PublicKeyToken = {ThisAssembly.PublicKeyToken}"; + /// - /// Exports a XAML-based embedded rule. + /// Initializes the class with the specified rule name and context. /// - [AttributeUsage(AttributeTargets.Field)] - internal sealed class ExportVSRuleAttribute : ExportPropertyXamlRuleDefinitionAttribute + /// + /// The name of the rule without '.xaml', for example, 'ConfigurationGeneral'. + /// + /// + /// One or more of . + /// + public ExportVSRuleAttribute(string ruleName, params string[] contexts) + : base(typeof(ExportVSRuleAttribute).Assembly.FullName, $"XamlRuleToCode:{ruleName}.xaml", string.Join(";", contexts)) { - // TODO: If reflection is insufficient, this will also work. - //private const string AssemblyFullName = $"{ThisAssembly.AssemblyName}, Version = {ThisAssembly.AssemblyVersion}, Culture = neutral, PublicKeyToken = {ThisAssembly.PublicKeyToken}"; - - /// - /// Initializes the class with the specified rule name and context. - /// - /// - /// The name of the rule without '.xaml', for example, 'ConfigurationGeneral'. - /// - /// - /// One or more of . - /// - public ExportVSRuleAttribute(string ruleName, params string[] contexts) - : base(typeof(ExportVSRuleAttribute).Assembly.FullName, $"XamlRuleToCode:{ruleName}.xaml", string.Join(";", contexts)) - { - RuleName = ruleName; - } + RuleName = ruleName; + } - /// - /// Gets the name of the rule without '.xaml', for example, 'ConfigurationGeneral'. - /// - public string RuleName - { - get; - } + /// + /// Gets the name of the rule without '.xaml', for example, 'ConfigurationGeneral'. + /// + public string RuleName + { + get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ResolvedCompilationReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ResolvedCompilationReference.cs index 3fc5e258f4..1a8e4504e0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ResolvedCompilationReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/ResolvedCompilationReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedCompilationReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedCompilationReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/RuleExporter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/RuleExporter.cs index 7706476d80..ace5423f2e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/RuleExporter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/RuleExporter.cs @@ -5,73 +5,72 @@ #pragma warning disable 0649 -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +/// +/// Responsible for exporting our embedded rules so that CPS can pick them. +/// +internal static class VSRuleExporter { /// - /// Responsible for exporting our embedded rules so that CPS can pick them. + /// Contains rules for the component. /// - internal static class VSRuleExporter + private static class BuildUpToDateCheckRules { /// - /// Contains rules for the component. + /// Represents evaluation items containing marker files indicating that reference projects have out of date references. /// - private static class BuildUpToDateCheckRules - { - /// - /// Represents evaluation items containing marker files indicating that reference projects have out of date references. - /// - [ExportVSRule(nameof(CopyUpToDateMarker), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int CopyUpToDateMarkerRule; + [ExportVSRule(nameof(CopyUpToDateMarker), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int CopyUpToDateMarkerRule; - /// - /// Represents the design-time build items containing resolved references path. - /// - [ExportVSRule(nameof(ResolvedCompilationReference), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int ResolvedCompilationReferencedRule; + /// + /// Represents the design-time build items containing resolved references path. + /// + [ExportVSRule(nameof(ResolvedCompilationReference), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int ResolvedCompilationReferencedRule; - /// - /// Represents design-time build items containing the input files into the build. - /// - [ExportVSRule(nameof(UpToDateCheckInput), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int UpToDateCheckInputRule; + /// + /// Represents design-time build items containing the input files into the build. + /// + [ExportVSRule(nameof(UpToDateCheckInput), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int UpToDateCheckInputRule; - /// - /// Represents design-time build items containing the output files of the build. - /// - [ExportVSRule(nameof(UpToDateCheckOutput), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int UpToDateCheckOutputRule; + /// + /// Represents design-time build items containing the output files of the build. + /// + [ExportVSRule(nameof(UpToDateCheckOutput), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int UpToDateCheckOutputRule; - /// - /// Represents design-time build items containing a mapping between input and the output files of the build. - /// - [ExportVSRule(nameof(UpToDateCheckBuilt), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int UpToDateCheckBuiltRule; + /// + /// Represents design-time build items containing a mapping between input and the output files of the build. + /// + [ExportVSRule(nameof(UpToDateCheckBuilt), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int UpToDateCheckBuiltRule; - /// - /// Represents design-time build items containing items this project contributes to the output directory. - /// - [ExportVSRule(nameof(CopyToOutputDirectoryItem), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int CopyToOutputDirectoryItemRule; + /// + /// Represents design-time build items containing items this project contributes to the output directory. + /// + [ExportVSRule(nameof(CopyToOutputDirectoryItem), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int CopyToOutputDirectoryItemRule; - /// - /// Represents design-time build items containing the identities of packages known to be incompatible with Build Acceleration. - /// - [ExportVSRule(nameof(BuildAccelerationIncompatiblePackage), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [Order(Order.Default)] - public static int BuildAccelerationIncompatiblePackageRule; - } + /// + /// Represents design-time build items containing the identities of packages known to be incompatible with Build Acceleration. + /// + [ExportVSRule(nameof(BuildAccelerationIncompatiblePackage), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] + [Order(Order.Default)] + public static int BuildAccelerationIncompatiblePackageRule; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckBuilt.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckBuilt.cs index fca37b7225..2e59941bf8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckBuilt.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckBuilt.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class UpToDateCheckBuilt { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class UpToDateCheckBuilt - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckInput.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckInput.cs index 1742487a0e..fcfe576c12 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckInput.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckInput.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class UpToDateCheckInput { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class UpToDateCheckInput - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckOutput.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckOutput.cs index f0a8ef4e42..600ebccf8a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckOutput.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Rules/UpToDateCheckOutput.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class UpToDateCheckOutput { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class UpToDateCheckOutput - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SVsSettingsPersistenceManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SVsSettingsPersistenceManager.cs index 35d4e78385..c6c80b165d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SVsSettingsPersistenceManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SVsSettingsPersistenceManager.cs @@ -2,10 +2,9 @@ using System.Runtime.InteropServices; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[Guid("9B164E40-C3A2-4363-9BC5-EB4039DEF653")] +internal class SVsSettingsPersistenceManager { - [Guid("9B164E40-C3A2-4363-9BC5-EB4039DEF653")] - internal class SVsSettingsPersistenceManager - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SpecialFilesProviders/VsProjectSpecialFilesManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SpecialFilesProviders/VsProjectSpecialFilesManager.cs index 3c894b9632..ea74307c00 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SpecialFilesProviders/VsProjectSpecialFilesManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/SpecialFilesProviders/VsProjectSpecialFilesManager.cs @@ -3,35 +3,34 @@ using Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.SpecialFilesProviders +namespace Microsoft.VisualStudio.ProjectSystem.VS.SpecialFilesProviders; + +/// +/// Provides an implementation of that wraps +/// +[Export(typeof(ISpecialFilesManager))] +internal class VsProjectSpecialFilesManager : ISpecialFilesManager { - /// - /// Provides an implementation of that wraps - /// - [Export(typeof(ISpecialFilesManager))] - internal class VsProjectSpecialFilesManager : ISpecialFilesManager - { - private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IUnconfiguredProjectVsServices _projectVsServices; - [ImportingConstructor] - public VsProjectSpecialFilesManager(IUnconfiguredProjectVsServices projectVsServices) - { - _projectVsServices = projectVsServices; - } + [ImportingConstructor] + public VsProjectSpecialFilesManager(IUnconfiguredProjectVsServices projectVsServices) + { + _projectVsServices = projectVsServices; + } - public async Task GetFileAsync(SpecialFiles fileId, SpecialFileFlags flags) - { - await _projectVsServices.ThreadingService.SwitchToUIThread(); + public async Task GetFileAsync(SpecialFiles fileId, SpecialFileFlags flags) + { + await _projectVsServices.ThreadingService.SwitchToUIThread(); - var files = (IVsProjectSpecialFiles)_projectVsServices.VsHierarchy; - HResult result = files.GetFile((int)fileId, (uint)flags, out _, out string fileName); - if (result.IsOK) - return fileName; + var files = (IVsProjectSpecialFiles)_projectVsServices.VsHierarchy; + HResult result = files.GetFile((int)fileId, (uint)flags, out _, out string fileName); + if (result.IsOK) + return fileName; - if (result.IsNotImplemented) - return null; // Not handled + if (result.IsNotImplemented) + return null; // Not handled - throw result.Exception!; - } + throw result.Exception!; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputFileChange.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputFileChange.cs index 8e713080c6..bb0f01e008 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputFileChange.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputFileChange.cs @@ -2,18 +2,17 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +[DebuggerDisplay("{File}, ignore: {IgnoreFileWriteTime}")] +internal class DesignTimeInputFileChange { - [DebuggerDisplay("{File}, ignore: {IgnoreFileWriteTime}")] - internal class DesignTimeInputFileChange - { - public string File { get; } - public bool IgnoreFileWriteTime { get; } + public string File { get; } + public bool IgnoreFileWriteTime { get; } - public DesignTimeInputFileChange(string file, bool ignoreFileWriteTime) - { - File = file; - IgnoreFileWriteTime = ignoreFileWriteTime; - } + public DesignTimeInputFileChange(string file, bool ignoreFileWriteTime) + { + File = file; + IgnoreFileWriteTime = ignoreFileWriteTime; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputSnapshot.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputSnapshot.cs index 6f2c435dcb..ed0cd9f6cf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputSnapshot.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputSnapshot.cs @@ -2,29 +2,28 @@ using EmptyCollections = Microsoft.VisualStudio.ProjectSystem.Empty; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +internal class DesignTimeInputSnapshot { - internal class DesignTimeInputSnapshot - { - public static readonly DesignTimeInputSnapshot Empty = new( - EmptyCollections.OrdinalStringSet, - EmptyCollections.OrdinalStringSet, - Enumerable.Empty(), string.Empty); + public static readonly DesignTimeInputSnapshot Empty = new( + EmptyCollections.OrdinalStringSet, + EmptyCollections.OrdinalStringSet, + Enumerable.Empty(), string.Empty); - public DesignTimeInputSnapshot(ImmutableHashSet inputs, ImmutableHashSet sharedInputs, IEnumerable changedInputs, string tempPEOutputPath) - { - Inputs = inputs; - SharedInputs = sharedInputs; - ChangedInputs = ImmutableArray.CreateRange(changedInputs); - TempPEOutputPath = tempPEOutputPath; - } + public DesignTimeInputSnapshot(ImmutableHashSet inputs, ImmutableHashSet sharedInputs, IEnumerable changedInputs, string tempPEOutputPath) + { + Inputs = inputs; + SharedInputs = sharedInputs; + ChangedInputs = ImmutableArray.CreateRange(changedInputs); + TempPEOutputPath = tempPEOutputPath; + } - public ImmutableHashSet Inputs { get; } + public ImmutableHashSet Inputs { get; } - public ImmutableHashSet SharedInputs { get; } + public ImmutableHashSet SharedInputs { get; } - public ImmutableArray ChangedInputs { get; } + public ImmutableArray ChangedInputs { get; } - public string TempPEOutputPath { get; } - } + public string TempPEOutputPath { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputs.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputs.cs index 2fdd73d789..3a76ecf179 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputs.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputs.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +internal class DesignTimeInputs { - internal class DesignTimeInputs - { - public ImmutableHashSet Inputs { get; } - public ImmutableHashSet SharedInputs { get; } + public ImmutableHashSet Inputs { get; } + public ImmutableHashSet SharedInputs { get; } - public DesignTimeInputs(IEnumerable inputs, IEnumerable sharedInputs) - { - Inputs = ImmutableHashSet.CreateRange(StringComparers.Paths, inputs); - SharedInputs = ImmutableHashSet.CreateRange(StringComparers.Paths, sharedInputs); - } + public DesignTimeInputs(IEnumerable inputs, IEnumerable sharedInputs) + { + Inputs = ImmutableHashSet.CreateRange(StringComparers.Paths, inputs); + SharedInputs = ImmutableHashSet.CreateRange(StringComparers.Paths, sharedInputs); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridge.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridge.cs index 91b9b52de3..241b1b96eb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridge.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridge.cs @@ -3,126 +3,125 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.VS.Automation; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +[Export(typeof(IDesignTimeInputsBuildManagerBridge))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal class DesignTimeInputsBuildManagerBridge : UnconfiguredProjectHostBridge, IProjectVersionedValue, IProjectVersionedValue>, IDesignTimeInputsBuildManagerBridge { - [Export(typeof(IDesignTimeInputsBuildManagerBridge))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal class DesignTimeInputsBuildManagerBridge : UnconfiguredProjectHostBridge, IProjectVersionedValue, IProjectVersionedValue>, IDesignTimeInputsBuildManagerBridge + private readonly UnconfiguredProject _project; + private readonly IDesignTimeInputsChangeTracker _designTimeInputsChangeTracker; + private readonly IDesignTimeInputsCompiler _designTimeInputsCompiler; + private readonly VSBuildManager _buildManager; + + /// + /// For unit testing purposes, to avoid having to mock all of CPS + /// + internal bool SkipInitialization { get; set; } + + [ImportingConstructor] + public DesignTimeInputsBuildManagerBridge(UnconfiguredProject project, + IProjectThreadingService threadingService, + IDesignTimeInputsChangeTracker designTimeInputsChangeTracker, + IDesignTimeInputsCompiler designTimeInputsCompiler, + VSBuildManager buildManager) + : base(threadingService.JoinableTaskContext) { - private readonly UnconfiguredProject _project; - private readonly IDesignTimeInputsChangeTracker _designTimeInputsChangeTracker; - private readonly IDesignTimeInputsCompiler _designTimeInputsCompiler; - private readonly VSBuildManager _buildManager; - - /// - /// For unit testing purposes, to avoid having to mock all of CPS - /// - internal bool SkipInitialization { get; set; } - - [ImportingConstructor] - public DesignTimeInputsBuildManagerBridge(UnconfiguredProject project, - IProjectThreadingService threadingService, - IDesignTimeInputsChangeTracker designTimeInputsChangeTracker, - IDesignTimeInputsCompiler designTimeInputsCompiler, - VSBuildManager buildManager) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _designTimeInputsChangeTracker = designTimeInputsChangeTracker; - _designTimeInputsCompiler = designTimeInputsCompiler; - _buildManager = buildManager; - } + _project = project; + _designTimeInputsChangeTracker = designTimeInputsChangeTracker; + _designTimeInputsCompiler = designTimeInputsCompiler; + _buildManager = buildManager; + } - public async Task GetDesignTimeOutputMonikersAsync() - { - await InitializeAsync(); + public async Task GetDesignTimeOutputMonikersAsync() + { + await InitializeAsync(); - Assumes.NotNull(AppliedValue); + Assumes.NotNull(AppliedValue); - DesignTimeInputSnapshot value = AppliedValue.Value; + DesignTimeInputSnapshot value = AppliedValue.Value; - return value.Inputs.Select(_project.MakeRelative).ToArray(); - } + return value.Inputs.Select(_project.MakeRelative).ToArray(); + } - public async Task BuildDesignTimeOutputAsync(string outputMoniker) + public async Task BuildDesignTimeOutputAsync(string outputMoniker) + { + if (!SkipInitialization) { - if (!SkipInitialization) - { - await InitializeAsync(); - } + await InitializeAsync(); + } - Assumes.NotNull(AppliedValue); + Assumes.NotNull(AppliedValue); - DesignTimeInputSnapshot value = AppliedValue.Value; + DesignTimeInputSnapshot value = AppliedValue.Value; - return string.IsNullOrEmpty(value.TempPEOutputPath) ? string.Empty : - await _designTimeInputsCompiler.BuildDesignTimeOutputAsync(outputMoniker, value.TempPEOutputPath, value.SharedInputs); - } + return string.IsNullOrEmpty(value.TempPEOutputPath) ? string.Empty : + await _designTimeInputsCompiler.BuildDesignTimeOutputAsync(outputMoniker, value.TempPEOutputPath, value.SharedInputs); + } - /// - /// ApplyAsync is called on the UI thread and its job is to update AppliedValue to be correct based on the changes that have come through data flow after being processed - /// - protected override async Task ApplyAsync(IProjectVersionedValue value) - { - // Not using use the ThreadingService property because unit tests - await JoinableFactory.SwitchToMainThreadAsync(); + /// + /// ApplyAsync is called on the UI thread and its job is to update AppliedValue to be correct based on the changes that have come through data flow after being processed + /// + protected override async Task ApplyAsync(IProjectVersionedValue value) + { + // Not using use the ThreadingService property because unit tests + await JoinableFactory.SwitchToMainThreadAsync(); - IProjectVersionedValue? previous = AppliedValue; + IProjectVersionedValue? previous = AppliedValue; - AppliedValue = value; + AppliedValue = value; - // To avoid callers seeing an inconsistent state where there are no monikers, - // we use BlockInitializeOnFirstAppliedValue to block on the first value - // being applied. - // - // Due to that, and to avoid a deadlock when event handlers call back into us - // while we're still initializing, we avoid firing the events the first time - // a value is applied. - if (previous is not null) + // To avoid callers seeing an inconsistent state where there are no monikers, + // we use BlockInitializeOnFirstAppliedValue to block on the first value + // being applied. + // + // Due to that, and to avoid a deadlock when event handlers call back into us + // while we're still initializing, we avoid firing the events the first time + // a value is applied. + if (previous is not null) + { + DesignTimeInputSnapshot currentValue = value.Value; + DesignTimeInputSnapshot previousValue = previous.Value; + + foreach (DesignTimeInputFileChange change in currentValue.ChangedInputs) { - DesignTimeInputSnapshot currentValue = value.Value; - DesignTimeInputSnapshot previousValue = previous.Value; - - foreach (DesignTimeInputFileChange change in currentValue.ChangedInputs) - { - _buildManager.OnDesignTimeOutputDirty(_project.MakeRelative(change.File)); - } - - foreach (string item in previousValue.Inputs.Except(currentValue.Inputs)) - { - _buildManager.OnDesignTimeOutputDeleted(_project.MakeRelative(item)); - } + _buildManager.OnDesignTimeOutputDirty(_project.MakeRelative(change.File)); } - } - /// - /// InitializeInnerCoreAsync is responsible for setting an initial AppliedValue. This value will be used by any UI thread calls that may happen - /// before the first data flow blocks have been processed. If this method doesn't set a value then the system will block until the first blocks - /// have been applied. - /// - protected override Task InitializeInnerCoreAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; + foreach (string item in previousValue.Inputs.Except(currentValue.Inputs)) + { + _buildManager.OnDesignTimeOutputDeleted(_project.MakeRelative(item)); + } } + } - /// - /// This method is where we tell data flow which blocks we're interested in receiving updates for - /// - protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) - { - JoinUpstreamDataSources(_designTimeInputsChangeTracker); + /// + /// InitializeInnerCoreAsync is responsible for setting an initial AppliedValue. This value will be used by any UI thread calls that may happen + /// before the first data flow blocks have been processed. If this method doesn't set a value then the system will block until the first blocks + /// have been applied. + /// + protected override Task InitializeInnerCoreAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } - return _designTimeInputsChangeTracker.SourceBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - } + /// + /// This method is where we tell data flow which blocks we're interested in receiving updates for + /// + protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) + { + JoinUpstreamDataSources(_designTimeInputsChangeTracker); - /// - /// Preprocess gets called as each data flow block updates and its job is to take the input from those blocks and do whatever work needed - /// so that ApplyAsync has all of the info it needs to do its job. - /// - protected override Task?> PreprocessAsync(IProjectVersionedValue input, IProjectVersionedValue? previousOutput) - { - // No need to manipulate the data - return Task.FromResult?>(input); - } + return _designTimeInputsChangeTracker.SourceBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + } + + /// + /// Preprocess gets called as each data flow block updates and its job is to take the input from those blocks and do whatever work needed + /// so that ApplyAsync has all of the info it needs to do its job. + /// + protected override Task?> PreprocessAsync(IProjectVersionedValue input, IProjectVersionedValue? previousOutput) + { + // No need to manipulate the data + return Task.FromResult?>(input); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTracker.cs index 129097d8ea..da7a3b4139 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTracker.cs @@ -3,229 +3,228 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +[Export(typeof(IDesignTimeInputsChangeTracker))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal class DesignTimeInputsChangeTracker : ProjectValueDataSourceBase, IDesignTimeInputsChangeTracker { - [Export(typeof(IDesignTimeInputsChangeTracker))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal class DesignTimeInputsChangeTracker : ProjectValueDataSourceBase, IDesignTimeInputsChangeTracker + private readonly UnconfiguredProject _project; + private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; + private readonly IDesignTimeInputsDataSource _inputsDataSource; + private readonly IDesignTimeInputsFileWatcher _fileWatcher; + + private readonly DisposableBag _disposables = new(); + + private DesignTimeInputSnapshot? _currentState; + private int _version; + + private IBroadcastBlock>? _broadcastBlock; + + /// + /// The public facade for the broadcast block. We don't expose the broadcast block directly because we don't want to allow consumers to complete or fault us + /// + private IReceivableSourceBlock>? _publicBlock; + + [ImportingConstructor] + public DesignTimeInputsChangeTracker(UnconfiguredProject project, + IUnconfiguredProjectServices unconfiguredProjectServices, + IProjectThreadingService threadingService, + IActiveConfiguredProjectSubscriptionService projectSubscriptionService, + IDesignTimeInputsDataSource inputsDataSource, + IDesignTimeInputsFileWatcher fileWatcher) + : base(unconfiguredProjectServices, synchronousDisposal: false, registerDataSource: false) { - private readonly UnconfiguredProject _project; - private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; - private readonly IDesignTimeInputsDataSource _inputsDataSource; - private readonly IDesignTimeInputsFileWatcher _fileWatcher; - - private readonly DisposableBag _disposables = new(); - - private DesignTimeInputSnapshot? _currentState; - private int _version; - - private IBroadcastBlock>? _broadcastBlock; - - /// - /// The public facade for the broadcast block. We don't expose the broadcast block directly because we don't want to allow consumers to complete or fault us - /// - private IReceivableSourceBlock>? _publicBlock; - - [ImportingConstructor] - public DesignTimeInputsChangeTracker(UnconfiguredProject project, - IUnconfiguredProjectServices unconfiguredProjectServices, - IProjectThreadingService threadingService, - IActiveConfiguredProjectSubscriptionService projectSubscriptionService, - IDesignTimeInputsDataSource inputsDataSource, - IDesignTimeInputsFileWatcher fileWatcher) - : base(unconfiguredProjectServices, synchronousDisposal: false, registerDataSource: false) - { - _project = project; - _projectSubscriptionService = projectSubscriptionService; - _inputsDataSource = inputsDataSource; - _fileWatcher = fileWatcher; - } + _project = project; + _projectSubscriptionService = projectSubscriptionService; + _inputsDataSource = inputsDataSource; + _fileWatcher = fileWatcher; + } - /// - /// This is to allow unit tests to force completion of our source block rather than waiting for async work to complete - /// - internal bool AllowSourceBlockCompletion { get; set; } + /// + /// This is to allow unit tests to force completion of our source block rather than waiting for async work to complete + /// + internal bool AllowSourceBlockCompletion { get; set; } - public override NamedIdentity DataSourceKey { get; } = new NamedIdentity(nameof(DesignTimeInputsChangeTracker)); + public override NamedIdentity DataSourceKey { get; } = new NamedIdentity(nameof(DesignTimeInputsChangeTracker)); - public override IComparable DataSourceVersion => _version; + public override IComparable DataSourceVersion => _version; - public override IReceivableSourceBlock> SourceBlock + public override IReceivableSourceBlock> SourceBlock + { + get { - get - { - EnsureInitialized(); + EnsureInitialized(); - return _publicBlock!; - } + return _publicBlock!; } + } - protected override void Initialize() - { - base.Initialize(); + protected override void Initialize() + { + base.Initialize(); + + // Create an action block to process the design time inputs and configuration general changes + ITargetBlock>> inputsAction = DataflowBlockFactory.CreateActionBlock>>(ProcessDataflowChanges, _project); + + _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(nameFormat: nameof(DesignTimeInputsChangeTracker) + " Broadcast: {1}"); + _publicBlock = AllowSourceBlockCompletion ? _broadcastBlock : _broadcastBlock.SafePublicize(); - // Create an action block to process the design time inputs and configuration general changes - ITargetBlock>> inputsAction = DataflowBlockFactory.CreateActionBlock>>(ProcessDataflowChanges, _project); + Assumes.Present(_project.Services.ProjectAsynchronousTasks); - _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(nameFormat: nameof(DesignTimeInputsChangeTracker) + " Broadcast: {1}"); - _publicBlock = AllowSourceBlockCompletion ? _broadcastBlock : _broadcastBlock.SafePublicize(); + IDisposable projectLink = ProjectDataSources.SyncLinkTo( + _inputsDataSource.SourceBlock.SyncLinkOptions( + linkOptions: DataflowOption.PropagateCompletion), + _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions( + linkOptions: DataflowOption.WithRuleNames(ConfigurationGeneral.SchemaName)), + inputsAction, + DataflowOption.PropagateCompletion, + cancellationToken: _project.Services.ProjectAsynchronousTasks.UnloadCancellationToken); - Assumes.Present(_project.Services.ProjectAsynchronousTasks); + // Create an action block to process file change notifications + ITargetBlock> fileWatcherAction = DataflowBlockFactory.CreateActionBlock>(ProcessFileChangeNotification, _project); + IDisposable watcherLink = _fileWatcher.SourceBlock.LinkTo(fileWatcherAction, DataflowOption.PropagateCompletion); - IDisposable projectLink = ProjectDataSources.SyncLinkTo( - _inputsDataSource.SourceBlock.SyncLinkOptions( - linkOptions: DataflowOption.PropagateCompletion), - _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions( - linkOptions: DataflowOption.WithRuleNames(ConfigurationGeneral.SchemaName)), - inputsAction, - DataflowOption.PropagateCompletion, - cancellationToken: _project.Services.ProjectAsynchronousTasks.UnloadCancellationToken); + _disposables.Add(projectLink); + _disposables.Add(watcherLink); - // Create an action block to process file change notifications - ITargetBlock> fileWatcherAction = DataflowBlockFactory.CreateActionBlock>(ProcessFileChangeNotification, _project); - IDisposable watcherLink = _fileWatcher.SourceBlock.LinkTo(fileWatcherAction, DataflowOption.PropagateCompletion); + JoinUpstreamDataSources(_inputsDataSource, _projectSubscriptionService.ProjectRuleSource, _fileWatcher); + } + + protected override void Dispose(bool disposing) + { + try + { + _disposables.Dispose(); + } + finally + { + base.Dispose(disposing); + } + } - _disposables.Add(projectLink); - _disposables.Add(watcherLink); + internal void ProcessFileChangeNotification(IProjectVersionedValue arg) + { + // File changes don't change state, but it makes sense to run with the state at the time the update came in + DesignTimeInputSnapshot? state = _currentState; - JoinUpstreamDataSources(_inputsDataSource, _projectSubscriptionService.ProjectRuleSource, _fileWatcher); + // Ignore any file changes until we've received the first set of design time inputs (which shouldn't happen anyway) + // That first update will send out all of the files so we're not losing anything + if (state is null) + { + return; } - protected override void Dispose(bool disposing) + var changedInputs = new List(); + foreach (string changedFile in arg.Value) { - try + // if a shared input changes, we recompile everything + if (state.SharedInputs.Contains(changedFile)) { - _disposables.Dispose(); + foreach (string file in state.Inputs) + { + changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime: false)); + } + // Since we've just queued every file, we don't care about any other changed files in this set + break; } - finally + else { - base.Dispose(disposing); + changedInputs.Add(new DesignTimeInputFileChange(changedFile, ignoreFileWriteTime: false)); } } - internal void ProcessFileChangeNotification(IProjectVersionedValue arg) - { - // File changes don't change state, but it makes sense to run with the state at the time the update came in - DesignTimeInputSnapshot? state = _currentState; + // File changes don't get project state, so they don't update it. + var snapshot = new DesignTimeInputSnapshot(state.Inputs, state.SharedInputs, changedInputs, state.TempPEOutputPath); + PublishSnapshot(snapshot); + } - // Ignore any file changes until we've received the first set of design time inputs (which shouldn't happen anyway) - // That first update will send out all of the files so we're not losing anything - if (state is null) - { - return; - } + // Should always produce output data to avoid hangs. + internal void ProcessDataflowChanges(IProjectVersionedValue> input) + { + _currentState = GenerateOutputData(_currentState, input) ?? DesignTimeInputSnapshot.Empty; - var changedInputs = new List(); - foreach (string changedFile in arg.Value) - { - // if a shared input changes, we recompile everything - if (state.SharedInputs.Contains(changedFile)) - { - foreach (string file in state.Inputs) - { - changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime: false)); - } - // Since we've just queued every file, we don't care about any other changed files in this set - break; - } - else - { - changedInputs.Add(new DesignTimeInputFileChange(changedFile, ignoreFileWriteTime: false)); - } - } + PublishSnapshot(_currentState); + } - // File changes don't get project state, so they don't update it. - var snapshot = new DesignTimeInputSnapshot(state.Inputs, state.SharedInputs, changedInputs, state.TempPEOutputPath); - PublishSnapshot(snapshot); - } + private static DesignTimeInputSnapshot? GenerateOutputData( + DesignTimeInputSnapshot? previousState, + IProjectVersionedValue> input) + { + DesignTimeInputs inputs = input.Value.Item1; - // Should always produce output data to avoid hangs. - internal void ProcessDataflowChanges(IProjectVersionedValue> input) + if (!input.Value.Item2.ProjectChanges.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectChangeDescription configChanges)) { - _currentState = GenerateOutputData(_currentState, input) ?? DesignTimeInputSnapshot.Empty; - - PublishSnapshot(_currentState); + // If this isn't an update we can deal with, just ignore it + return null; } - private static DesignTimeInputSnapshot? GenerateOutputData( - DesignTimeInputSnapshot? previousState, - IProjectVersionedValue> input) + var changedInputs = new List(); + // On the first call where we receive design time inputs we queue compilation of all of them, knowing that we'll only compile if the file write date requires it + if (previousState is null) { - DesignTimeInputs inputs = input.Value.Item1; + AddAllInputsToQueue(false); + } + else + { + // If its not the first call... - if (!input.Value.Item2.ProjectChanges.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectChangeDescription configChanges)) + // If a new shared design time input is added, we need to recompile everything regardless of source file modified date + // because it could be an old file that is being promoted to a shared input + if (inputs.SharedInputs.Except(previousState.SharedInputs, StringComparers.Paths).Any()) { - // If this isn't an update we can deal with, just ignore it - return null; + AddAllInputsToQueue(true); } - - var changedInputs = new List(); - // On the first call where we receive design time inputs we queue compilation of all of them, knowing that we'll only compile if the file write date requires it - if (previousState is null) + // If the namespace or output path inputs have changed, then we recompile every file regardless of date + else if (configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.RootNamespaceProperty) || + configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.ProjectDirProperty) || + configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.IntermediateOutputPathProperty)) { - AddAllInputsToQueue(false); + AddAllInputsToQueue(true); } else { - // If its not the first call... - - // If a new shared design time input is added, we need to recompile everything regardless of source file modified date - // because it could be an old file that is being promoted to a shared input - if (inputs.SharedInputs.Except(previousState.SharedInputs, StringComparers.Paths).Any()) - { - AddAllInputsToQueue(true); - } - // If the namespace or output path inputs have changed, then we recompile every file regardless of date - else if (configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.RootNamespaceProperty) || - configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.ProjectDirProperty) || - configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.IntermediateOutputPathProperty)) - { - AddAllInputsToQueue(true); - } - else + // Otherwise we just queue any new design time inputs, and still do date checks + foreach (string file in inputs.Inputs.Except(previousState.Inputs, StringComparers.Paths)) { - // Otherwise we just queue any new design time inputs, and still do date checks - foreach (string file in inputs.Inputs.Except(previousState.Inputs, StringComparers.Paths)) - { - changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime: false)); - } + changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime: false)); } } + } - string tempPEOutputPath; - // Make sure we have the up to date output path. If either of these don't exist, they will be null and we'll handle the ArgumentException below - string? basePath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.ProjectDirProperty); - string? objPath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.IntermediateOutputPathProperty); - try - { - tempPEOutputPath = Path.Combine(basePath, objPath, "TempPE"); - } - catch (ArgumentException) - { - // if the path is bad, or we couldn't get part of it, then we presume we wouldn't be able to act on any files - // so we can just clear _currentState to ensure file changes aren't processed, and return. - // If the path is ever fixed this block will trigger again and all will be right with the world. - return null; - } + string tempPEOutputPath; + // Make sure we have the up to date output path. If either of these don't exist, they will be null and we'll handle the ArgumentException below + string? basePath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.ProjectDirProperty); + string? objPath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.IntermediateOutputPathProperty); + try + { + tempPEOutputPath = Path.Combine(basePath, objPath, "TempPE"); + } + catch (ArgumentException) + { + // if the path is bad, or we couldn't get part of it, then we presume we wouldn't be able to act on any files + // so we can just clear _currentState to ensure file changes aren't processed, and return. + // If the path is ever fixed this block will trigger again and all will be right with the world. + return null; + } - // This is our only update to current state, and data flow protects us from overlaps. File changes don't update state - return new DesignTimeInputSnapshot(inputs.Inputs, inputs.SharedInputs, changedInputs, tempPEOutputPath); + // This is our only update to current state, and data flow protects us from overlaps. File changes don't update state + return new DesignTimeInputSnapshot(inputs.Inputs, inputs.SharedInputs, changedInputs, tempPEOutputPath); - void AddAllInputsToQueue(bool ignoreFileWriteTime) + void AddAllInputsToQueue(bool ignoreFileWriteTime) + { + foreach (string file in inputs.Inputs) { - foreach (string file in inputs.Inputs) - { - changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime)); - } + changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime)); } } + } - private void PublishSnapshot(DesignTimeInputSnapshot snapshot) - { - _version++; - _broadcastBlock?.Post(new ProjectVersionedValue( - snapshot, - Empty.ProjectValueVersions.Add(DataSourceKey, _version))); - } + private void PublishSnapshot(DesignTimeInputSnapshot snapshot) + { + _version++; + _broadcastBlock?.Post(new ProjectVersionedValue( + snapshot, + Empty.ProjectValueVersions.Add(DataSourceKey, _version))); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueue.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueue.cs index 22e0332ffa..b57fd36abe 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueue.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueue.cs @@ -1,81 +1,80 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +internal partial class DesignTimeInputsCompiler { - internal partial class DesignTimeInputsCompiler + internal class CompilationQueue { - internal class CompilationQueue - { - private ImmutableDictionary _queue = ImmutableDictionary.Empty.WithComparers(StringComparers.Paths); + private ImmutableDictionary _queue = ImmutableDictionary.Empty.WithComparers(StringComparers.Paths); - public int Count => _queue.Count; + public int Count => _queue.Count; - public QueueItem? Pop() + public QueueItem? Pop() + { + QueueItem? result = null; + ImmutableInterlocked.Update(ref _queue, queue => { - QueueItem? result = null; - ImmutableInterlocked.Update(ref _queue, queue => + ImmutableDictionary.Enumerator enumerator = queue.GetEnumerator(); + if (enumerator.MoveNext()) { - ImmutableDictionary.Enumerator enumerator = queue.GetEnumerator(); - if (enumerator.MoveNext()) - { - result = enumerator.Current.Value; - return queue.Remove(result.FileName); - } - return queue; - }); + result = enumerator.Current.Value; + return queue.Remove(result.FileName); + } + return queue; + }); - return result; - } + return result; + } - public void Push(QueueItem item) - { - // Add an item to our queue, or if it exists, ensure that if anyone wanted us to ignore the file write time, we don't lose that info - ImmutableInterlocked.AddOrUpdate(ref _queue, item.FileName, item, (fileName, value) => new QueueItem(fileName, item.SharedInputs, item.TempPEOutputPath, item.IgnoreFileWriteTime | value.IgnoreFileWriteTime)); - } + public void Push(QueueItem item) + { + // Add an item to our queue, or if it exists, ensure that if anyone wanted us to ignore the file write time, we don't lose that info + ImmutableInterlocked.AddOrUpdate(ref _queue, item.FileName, item, (fileName, value) => new QueueItem(fileName, item.SharedInputs, item.TempPEOutputPath, item.IgnoreFileWriteTime | value.IgnoreFileWriteTime)); + } - /// - /// Updates the queue items by either adding or updating from addOrUpdateItems, whilst ensuring that no items are tracked that aren't in the master list - /// - /// The items to add or update in the queue - /// The master list of items which will always be a superset of the queue contents - /// The shared inputs that are to be updated in each item - /// The output path that is to be updated in each item - public void Update(ImmutableArray addOrUpdateItems, ImmutableHashSet masterListOfItems, ImmutableHashSet sharedInputs, string tempPEOutputPath) + /// + /// Updates the queue items by either adding or updating from addOrUpdateItems, whilst ensuring that no items are tracked that aren't in the master list + /// + /// The items to add or update in the queue + /// The master list of items which will always be a superset of the queue contents + /// The shared inputs that are to be updated in each item + /// The output path that is to be updated in each item + public void Update(ImmutableArray addOrUpdateItems, ImmutableHashSet masterListOfItems, ImmutableHashSet sharedInputs, string tempPEOutputPath) + { + ImmutableInterlocked.Update(ref _queue, (queue, args) => { - ImmutableInterlocked.Update(ref _queue, (queue, args) => + foreach (DesignTimeInputFileChange item in addOrUpdateItems) { - foreach (DesignTimeInputFileChange item in addOrUpdateItems) + if (queue.TryGetValue(item.File, out QueueItem? existing)) + { + // If the item exists, ensure that if anyone wanted us to ignore the file write time, we don't lose that info + // If the item doesn't need to be tracked, we'll clean it up later (we have to loop through everything then anyway) + queue = queue.SetItem(item.File, new QueueItem(item.File, args.sharedInputs, args.tempPEOutputPath, existing.IgnoreFileWriteTime | item.IgnoreFileWriteTime)); + } + // Minor optimization - no point adding only to remove later + else if (masterListOfItems.Contains(item.File)) { - if (queue.TryGetValue(item.File, out QueueItem? existing)) - { - // If the item exists, ensure that if anyone wanted us to ignore the file write time, we don't lose that info - // If the item doesn't need to be tracked, we'll clean it up later (we have to loop through everything then anyway) - queue = queue.SetItem(item.File, new QueueItem(item.File, args.sharedInputs, args.tempPEOutputPath, existing.IgnoreFileWriteTime | item.IgnoreFileWriteTime)); - } - // Minor optimization - no point adding only to remove later - else if (masterListOfItems.Contains(item.File)) - { - queue = queue.Add(item.File, new QueueItem(item.File, args.sharedInputs, args.tempPEOutputPath, item.IgnoreFileWriteTime)); - } + queue = queue.Add(item.File, new QueueItem(item.File, args.sharedInputs, args.tempPEOutputPath, item.IgnoreFileWriteTime)); } + } - // now go through our queue and make sure we aren't tracking items that aren't in the master list - foreach ((string? key, _) in queue) + // now go through our queue and make sure we aren't tracking items that aren't in the master list + foreach ((string? key, _) in queue) + { + if (!masterListOfItems.Contains(key)) { - if (!masterListOfItems.Contains(key)) - { - queue = queue.Remove(key); - } + queue = queue.Remove(key); } + } - return queue; - }, (sharedInputs, tempPEOutputPath)); - } + return queue; + }, (sharedInputs, tempPEOutputPath)); + } - public void RemoveSpecific(string fileName) - { - ImmutableInterlocked.TryRemove(ref _queue, fileName, out _); - } + public void RemoveSpecific(string fileName) + { + ImmutableInterlocked.TryRemove(ref _queue, fileName, out _); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.QueueItem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.QueueItem.cs index e06331f0e4..00cf1fb4bc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.QueueItem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.QueueItem.cs @@ -2,25 +2,24 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +internal partial class DesignTimeInputsCompiler { - internal partial class DesignTimeInputsCompiler + [DebuggerDisplay("{FileName}")] + public class QueueItem { - [DebuggerDisplay("{FileName}")] - public class QueueItem - { - public string FileName { get; } - public string TempPEOutputPath { get; } - public bool IgnoreFileWriteTime { get; } - public ImmutableHashSet SharedInputs { get; } + public string FileName { get; } + public string TempPEOutputPath { get; } + public bool IgnoreFileWriteTime { get; } + public ImmutableHashSet SharedInputs { get; } - public QueueItem(string fileName, ImmutableHashSet sharedInputs, string tempPEOutputPath, bool ignoreFileWriteTime) - { - FileName = fileName; - SharedInputs = sharedInputs; - TempPEOutputPath = tempPEOutputPath; - IgnoreFileWriteTime = ignoreFileWriteTime; - } + public QueueItem(string fileName, ImmutableHashSet sharedInputs, string tempPEOutputPath, bool ignoreFileWriteTime) + { + FileName = fileName; + SharedInputs = sharedInputs; + TempPEOutputPath = tempPEOutputPath; + IgnoreFileWriteTime = ignoreFileWriteTime; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.cs index 8b02723049..c9c4af84b9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.cs @@ -9,297 +9,296 @@ using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Threading.Tasks; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +[Export(typeof(IDesignTimeInputsCompiler))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal partial class DesignTimeInputsCompiler : OnceInitializedOnceDisposedAsync, IDesignTimeInputsCompiler { - [Export(typeof(IDesignTimeInputsCompiler))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal partial class DesignTimeInputsCompiler : OnceInitializedOnceDisposedAsync, IDesignTimeInputsCompiler + private static readonly TimeSpan s_compilationDelayTime = TimeSpan.FromMilliseconds(500); + + private readonly UnconfiguredProject _project; + private readonly IWorkspaceWriter _workspaceWriter; + private readonly IProjectThreadingService _threadingService; + private readonly IDesignTimeInputsChangeTracker _changeTracker; + private readonly ITempPECompiler _compiler; + private readonly IFileSystem _fileSystem; + private readonly ITelemetryService _telemetryService; + private readonly TaskDelayScheduler _scheduler; + + private ITargetBlock>? _compileActionBlock; + private IDisposable? _changeTrackerLink; + + private readonly CompilationQueue _queue = new(); + private CancellationTokenSource? _compilationCancellationSource; + + [ImportingConstructor] + public DesignTimeInputsCompiler(UnconfiguredProject project, + IWorkspaceWriter workspaceWriter, + IProjectThreadingService threadingService, + IDesignTimeInputsChangeTracker changeTracker, + ITempPECompiler compiler, + IFileSystem fileSystem, + ITelemetryService telemetryService) + : base(threadingService.JoinableTaskContext) { - private static readonly TimeSpan s_compilationDelayTime = TimeSpan.FromMilliseconds(500); - - private readonly UnconfiguredProject _project; - private readonly IWorkspaceWriter _workspaceWriter; - private readonly IProjectThreadingService _threadingService; - private readonly IDesignTimeInputsChangeTracker _changeTracker; - private readonly ITempPECompiler _compiler; - private readonly IFileSystem _fileSystem; - private readonly ITelemetryService _telemetryService; - private readonly TaskDelayScheduler _scheduler; - - private ITargetBlock>? _compileActionBlock; - private IDisposable? _changeTrackerLink; - - private readonly CompilationQueue _queue = new(); - private CancellationTokenSource? _compilationCancellationSource; - - [ImportingConstructor] - public DesignTimeInputsCompiler(UnconfiguredProject project, - IWorkspaceWriter workspaceWriter, - IProjectThreadingService threadingService, - IDesignTimeInputsChangeTracker changeTracker, - ITempPECompiler compiler, - IFileSystem fileSystem, - ITelemetryService telemetryService) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _workspaceWriter = workspaceWriter; - _threadingService = threadingService; - _changeTracker = changeTracker; - _compiler = compiler; - _fileSystem = fileSystem; - _telemetryService = telemetryService; - _scheduler = new TaskDelayScheduler(s_compilationDelayTime, threadingService, CancellationToken.None); - } + _project = project; + _workspaceWriter = workspaceWriter; + _threadingService = threadingService; + _changeTracker = changeTracker; + _compiler = compiler; + _fileSystem = fileSystem; + _telemetryService = telemetryService; + _scheduler = new TaskDelayScheduler(s_compilationDelayTime, threadingService, CancellationToken.None); + } - /// - /// This is to allow unit tests to run the compilation synchronously rather than waiting for async work to complete - /// - internal bool CompileSynchronously { get; set; } + /// + /// This is to allow unit tests to run the compilation synchronously rather than waiting for async work to complete + /// + internal bool CompileSynchronously { get; set; } - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _compileActionBlock = DataflowBlockFactory.CreateActionBlock>(ProcessDataflowChanges, _project); + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _compileActionBlock = DataflowBlockFactory.CreateActionBlock>(ProcessDataflowChanges, _project); - _changeTrackerLink = _changeTracker.SourceBlock.LinkTo(_compileActionBlock, DataflowOption.PropagateCompletion); + _changeTrackerLink = _changeTracker.SourceBlock.LinkTo(_compileActionBlock, DataflowOption.PropagateCompletion); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - internal void ProcessDataflowChanges(IProjectVersionedValue value) - { - // Cancel any in-progress queue processing - _compilationCancellationSource?.Cancel(); + internal void ProcessDataflowChanges(IProjectVersionedValue value) + { + // Cancel any in-progress queue processing + _compilationCancellationSource?.Cancel(); - DesignTimeInputSnapshot snapshot = value.Value; + DesignTimeInputSnapshot snapshot = value.Value; - // add all of the changes to our queue - _queue.Update(snapshot.ChangedInputs, snapshot.Inputs, snapshot.SharedInputs, snapshot.TempPEOutputPath); + // add all of the changes to our queue + _queue.Update(snapshot.ChangedInputs, snapshot.Inputs, snapshot.SharedInputs, snapshot.TempPEOutputPath); - Assumes.Present(_project.Services.ProjectAsynchronousTasks); + Assumes.Present(_project.Services.ProjectAsynchronousTasks); - // Create a cancellation source so we can cancel the compilation if another message comes through - _compilationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_project.Services.ProjectAsynchronousTasks.UnloadCancellationToken); + // Create a cancellation source so we can cancel the compilation if another message comes through + _compilationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_project.Services.ProjectAsynchronousTasks.UnloadCancellationToken); - JoinableTask task = _scheduler.ScheduleAsyncTask(ProcessCompileQueueAsync, _compilationCancellationSource.Token); + JoinableTask task = _scheduler.ScheduleAsyncTask(ProcessCompileQueueAsync, _compilationCancellationSource.Token); - // For unit testing purposes, optionally block the thread until the task we scheduled is complete - if (CompileSynchronously) - { - _threadingService.ExecuteSynchronously(() => task.Task); - } + // For unit testing purposes, optionally block the thread until the task we scheduled is complete + if (CompileSynchronously) + { + _threadingService.ExecuteSynchronously(() => task.Task); } + } - protected override async Task DisposeCoreAsync(bool initialized) + protected override async Task DisposeCoreAsync(bool initialized) + { + if (_compileActionBlock is not null) { - if (_compileActionBlock is not null) - { - // This will stop our blocks taking any more input - _compileActionBlock.Complete(); - - await _compileActionBlock.Completion; - } + // This will stop our blocks taking any more input + _compileActionBlock.Complete(); - _compilationCancellationSource?.Dispose(); - _scheduler.Dispose(); - _changeTrackerLink?.Dispose(); + await _compileActionBlock.Completion; } - private Task ProcessCompileQueueAsync(CancellationToken token) + _compilationCancellationSource?.Dispose(); + _scheduler.Dispose(); + _changeTrackerLink?.Dispose(); + } + + private Task ProcessCompileQueueAsync(CancellationToken token) + { + int compileCount = 0; + int initialQueueLength = _queue.Count; + var compileStopWatch = Stopwatch.StartNew(); + return _workspaceWriter.WriteAsync(async workspace => { - int compileCount = 0; - int initialQueueLength = _queue.Count; - var compileStopWatch = Stopwatch.StartNew(); - return _workspaceWriter.WriteAsync(async workspace => + while (true) { - while (true) + if (IsDisposing || IsDisposed) { - if (IsDisposing || IsDisposed) - { - return; - } - - // we don't want to pop if we've been cancelled in the time it took to take the write lock, so check just in case. - // this may be overkill - if (token.IsCancellationRequested) - { - break; - } + return; + } - // Grab the next file to compile off the queue - QueueItem? item = _queue.Pop(); - if (item is null) - { - break; - } + // we don't want to pop if we've been cancelled in the time it took to take the write lock, so check just in case. + // this may be overkill + if (token.IsCancellationRequested) + { + break; + } - bool cancelled = false; - string outputFileName = GetOutputFileName(item.FileName, item.TempPEOutputPath); - try - { - if (await CompileDesignTimeInputAsync(workspace.Context, item.FileName, outputFileName, item.SharedInputs, item.IgnoreFileWriteTime, token)) - { - compileCount++; - } - } - catch (OperationCanceledException) - { - cancelled = true; - } + // Grab the next file to compile off the queue + QueueItem? item = _queue.Pop(); + if (item is null) + { + break; + } - if (cancelled || token.IsCancellationRequested) + bool cancelled = false; + string outputFileName = GetOutputFileName(item.FileName, item.TempPEOutputPath); + try + { + if (await CompileDesignTimeInputAsync(workspace.Context, item.FileName, outputFileName, item.SharedInputs, item.IgnoreFileWriteTime, token)) { - // if the compilation was cancelled, we need to re-add the file so we catch it next time - _queue.Push(item); - break; + compileCount++; } } + catch (OperationCanceledException) + { + cancelled = true; + } - LogTelemetry(cancelled: token.IsCancellationRequested); - }, - token); - - void LogTelemetry(bool cancelled) - { - compileStopWatch!.Stop(); - _telemetryService.PostProperties(TelemetryEventName.TempPEProcessQueue, - [ - (TelemetryPropertyName.TempPE.CompileCount, compileCount), - (TelemetryPropertyName.TempPE.InitialQueueLength, initialQueueLength), - (TelemetryPropertyName.TempPE.CompileWasCancelled, cancelled), - (TelemetryPropertyName.TempPE.CompileDuration, compileStopWatch.ElapsedMilliseconds) - ]); + if (cancelled || token.IsCancellationRequested) + { + // if the compilation was cancelled, we need to re-add the file so we catch it next time + _queue.Push(item); + break; + } } - } - /// - /// Gets the XML that describes a TempPE DLL, including building it if necessary - /// - /// A project relative path to a source file that is a design time input - /// The path in which to place the TempPE DLL if one is created - /// The list of shared inputs to be included in the TempPE DLL - /// An XML description of the TempPE DLL for the specified file - public async Task BuildDesignTimeOutputAsync(string relativeFileName, string tempPEOutputPath, ImmutableHashSet sharedInputs) + LogTelemetry(cancelled: token.IsCancellationRequested); + }, + token); + + void LogTelemetry(bool cancelled) { - // A call to this method indicates that the TempPE system is in use for real, so we use it as a trigger for starting background compilation of things - // This means we get a nicer experience for the user once they start using designers, without wasted cycles compiling things just because a project is loaded - await InitializeAsync(); + compileStopWatch!.Stop(); + _telemetryService.PostProperties(TelemetryEventName.TempPEProcessQueue, + [ + (TelemetryPropertyName.TempPE.CompileCount, compileCount), + (TelemetryPropertyName.TempPE.InitialQueueLength, initialQueueLength), + (TelemetryPropertyName.TempPE.CompileWasCancelled, cancelled), + (TelemetryPropertyName.TempPE.CompileDuration, compileStopWatch.ElapsedMilliseconds) + ]); + } + } - int initialQueueLength = _queue.Count; + /// + /// Gets the XML that describes a TempPE DLL, including building it if necessary + /// + /// A project relative path to a source file that is a design time input + /// The path in which to place the TempPE DLL if one is created + /// The list of shared inputs to be included in the TempPE DLL + /// An XML description of the TempPE DLL for the specified file + public async Task BuildDesignTimeOutputAsync(string relativeFileName, string tempPEOutputPath, ImmutableHashSet sharedInputs) + { + // A call to this method indicates that the TempPE system is in use for real, so we use it as a trigger for starting background compilation of things + // This means we get a nicer experience for the user once they start using designers, without wasted cycles compiling things just because a project is loaded + await InitializeAsync(); - string fileName = _project.MakeRooted(relativeFileName); + int initialQueueLength = _queue.Count; - // Remove the file from our todo list, in case it was in there. - // Note that other than this avoidance of unnecessary work, this method is stateless. - _queue.RemoveSpecific(fileName); + string fileName = _project.MakeRooted(relativeFileName); - string outputFileName = GetOutputFileNameFromRelativePath(relativeFileName, tempPEOutputPath); - // make sure the file is up to date - bool compiled = await _workspaceWriter.WriteAsync(workspace => - { - return CompileDesignTimeInputAsync(workspace.Context, fileName, outputFileName, sharedInputs, ignoreFileWriteTime: false); - }); + // Remove the file from our todo list, in case it was in there. + // Note that other than this avoidance of unnecessary work, this method is stateless. + _queue.RemoveSpecific(fileName); - if (compiled) - { - _telemetryService.PostProperties(TelemetryEventName.TempPECompileOnDemand, - [ - (TelemetryPropertyName.TempPE.InitialQueueLength, initialQueueLength) - ]); - } + string outputFileName = GetOutputFileNameFromRelativePath(relativeFileName, tempPEOutputPath); + // make sure the file is up to date + bool compiled = await _workspaceWriter.WriteAsync(workspace => + { + return CompileDesignTimeInputAsync(workspace.Context, fileName, outputFileName, sharedInputs, ignoreFileWriteTime: false); + }); - return $""" - - - - - """; + if (compiled) + { + _telemetryService.PostProperties(TelemetryEventName.TempPECompileOnDemand, + [ + (TelemetryPropertyName.TempPE.InitialQueueLength, initialQueueLength) + ]); } - private async Task CompileDesignTimeInputAsync(IWorkspaceProjectContext context, string designTimeInput, string outputFileName, ImmutableHashSet sharedInputs, bool ignoreFileWriteTime, CancellationToken token = default) + return $""" + + + + + """; + } + + private async Task CompileDesignTimeInputAsync(IWorkspaceProjectContext context, string designTimeInput, string outputFileName, ImmutableHashSet sharedInputs, bool ignoreFileWriteTime, CancellationToken token = default) + { + HashSet filesToCompile = GetFilesToCompile(designTimeInput, sharedInputs); + + if (token.IsCancellationRequested) { - HashSet filesToCompile = GetFilesToCompile(designTimeInput, sharedInputs); + return false; + } - if (token.IsCancellationRequested) + if (ignoreFileWriteTime || CompilationNeeded(filesToCompile, outputFileName)) + { + bool result = false; + try { - return false; + result = await _compiler.CompileAsync(context, outputFileName, filesToCompile, token); } - - if (ignoreFileWriteTime || CompilationNeeded(filesToCompile, outputFileName)) + catch (IOException) + { } + finally { - bool result = false; - try - { - result = await _compiler.CompileAsync(context, outputFileName, filesToCompile, token); - } - catch (IOException) - { } - finally + // If the compilation failed or was cancelled we should clean up any old TempPE outputs lest a designer gets the wrong types, plus its what legacy did + // plus the way the Roslyn compiler works is by creating a 0 byte file first + if (!result) { - // If the compilation failed or was cancelled we should clean up any old TempPE outputs lest a designer gets the wrong types, plus its what legacy did - // plus the way the Roslyn compiler works is by creating a 0 byte file first - if (!result) + try { - try - { - _fileSystem.RemoveFile(outputFileName); - } - catch (IOException) - { } - catch (UnauthorizedAccessException) - { } + _fileSystem.RemoveFile(outputFileName); } + catch (IOException) + { } + catch (UnauthorizedAccessException) + { } } - - // true in this case means "we tried to compile", and is just for telemetry reasons. It doesn't indicate success or failure of compilation - return true; } - return false; - } - - private string GetOutputFileName(string fileName, string tempPEOutputPath) - { - // Turn the file path back into a relative path to compute output DLL name - return GetOutputFileNameFromRelativePath(_project.MakeRelative(fileName), tempPEOutputPath); - } - private static string GetOutputFileNameFromRelativePath(string relativePath, string tempPEOutputPath) - { - // Since we are given a relative path we can just replace path separators and we know we'll have a valid filename - return Path.Combine(tempPEOutputPath, relativePath.Replace('\\', '.') + ".dll"); + // true in this case means "we tried to compile", and is just for telemetry reasons. It doesn't indicate success or failure of compilation + return true; } + return false; + } - private bool CompilationNeeded(HashSet files, string outputFileName) - { - if (!_fileSystem.TryGetLastFileWriteTimeUtc(outputFileName, out DateTime? outputDateTime)) - return true; // File does not exist + private string GetOutputFileName(string fileName, string tempPEOutputPath) + { + // Turn the file path back into a relative path to compute output DLL name + return GetOutputFileNameFromRelativePath(_project.MakeRelative(fileName), tempPEOutputPath); + } - foreach (string file in files) - { - DateTime fileDateTime = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(file); - if (fileDateTime > outputDateTime) - return true; - } + private static string GetOutputFileNameFromRelativePath(string relativePath, string tempPEOutputPath) + { + // Since we are given a relative path we can just replace path separators and we know we'll have a valid filename + return Path.Combine(tempPEOutputPath, relativePath.Replace('\\', '.') + ".dll"); + } - return false; - } + private bool CompilationNeeded(HashSet files, string outputFileName) + { + if (!_fileSystem.TryGetLastFileWriteTimeUtc(outputFileName, out DateTime? outputDateTime)) + return true; // File does not exist - private static HashSet GetFilesToCompile(string moniker, ImmutableHashSet sharedInputs) + foreach (string file in files) { - // This is a HashSet because we allow files to be both inputs and shared inputs, and we don't want to compile the same file twice, - // plus Roslyn needs to call Contains on this quite a lot in order to ensure its only compiling the right files so we want that to be fast. - // When it comes to compiling the files there is no difference between shared and normal design time inputs, we just track differently because - // shared are included in every DLL. - var files = new HashSet(sharedInputs.Count + 1, StringComparers.Paths); - files.AddRange(sharedInputs); - files.Add(moniker); - return files; + DateTime fileDateTime = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(file); + if (fileDateTime > outputDateTime) + return true; } + + return false; + } + + private static HashSet GetFilesToCompile(string moniker, ImmutableHashSet sharedInputs) + { + // This is a HashSet because we allow files to be both inputs and shared inputs, and we don't want to compile the same file twice, + // plus Roslyn needs to call Contains on this quite a lot in order to ensure its only compiling the right files so we want that to be fast. + // When it comes to compiling the files there is no difference between shared and normal design time inputs, we just track differently because + // shared are included in every DLL. + var files = new HashSet(sharedInputs.Count + 1, StringComparers.Paths); + files.AddRange(sharedInputs); + files.Add(moniker); + return files; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsDataSource.cs index d3aefafe2c..cae32d4f7b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsDataSource.cs @@ -3,95 +3,94 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +/// +/// Processes Compile source items into that includes design time and shared design time inputs +/// only. +/// +[Export(typeof(IDesignTimeInputsDataSource))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal class DesignTimeInputsDataSource : ChainedProjectValueDataSourceBase, IDesignTimeInputsDataSource { - /// - /// Processes Compile source items into that includes design time and shared design time inputs - /// only. - /// - [Export(typeof(IDesignTimeInputsDataSource))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal class DesignTimeInputsDataSource : ChainedProjectValueDataSourceBase, IDesignTimeInputsDataSource - { - private static readonly ImmutableHashSet s_ruleNames = Empty.OrdinalIgnoreCaseStringSet.Add(Compile.SchemaName); - - private readonly UnconfiguredProject _project; - private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; + private static readonly ImmutableHashSet s_ruleNames = Empty.OrdinalIgnoreCaseStringSet.Add(Compile.SchemaName); - [ImportingConstructor] - public DesignTimeInputsDataSource(UnconfiguredProject project, - IUnconfiguredProjectServices unconfiguredProjectServices, - IActiveConfiguredProjectSubscriptionService projectSubscriptionService) - : base(project, synchronousDisposal: false, registerDataSource: false) - { - _project = project; - _projectSubscriptionService = projectSubscriptionService; - } + private readonly UnconfiguredProject _project; + private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; - protected override UnconfiguredProject ContainingProject - { - get { return _project; } - } + [ImportingConstructor] + public DesignTimeInputsDataSource(UnconfiguredProject project, + IUnconfiguredProjectServices unconfiguredProjectServices, + IActiveConfiguredProjectSubscriptionService projectSubscriptionService) + : base(project, synchronousDisposal: false, registerDataSource: false) + { + _project = project; + _projectSubscriptionService = projectSubscriptionService; + } - protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) - { - IProjectValueDataSource source = _projectSubscriptionService.SourceItemsRuleSource; + protected override UnconfiguredProject ContainingProject + { + get { return _project; } + } - // Transform the changes from evaluation/design-time build -> restore data - DisposableValue>> transformBlock = source.SourceBlock - .TransformWithNoDelta(update => update.Derive(u => GetDesignTimeInputs(u.CurrentState)), - suppressVersionOnlyUpdates: false, - ruleNames: s_ruleNames); + protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) + { + IProjectValueDataSource source = _projectSubscriptionService.SourceItemsRuleSource; - // Set the link up so that we publish changes to target block - transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + // Transform the changes from evaluation/design-time build -> restore data + DisposableValue>> transformBlock = source.SourceBlock + .TransformWithNoDelta(update => update.Derive(u => GetDesignTimeInputs(u.CurrentState)), + suppressVersionOnlyUpdates: false, + ruleNames: s_ruleNames); - // Join the source blocks, so if they need to switch to UI thread to complete - // and someone is blocked on us on the same thread, the call proceeds - JoinUpstreamDataSources(source); + // Set the link up so that we publish changes to target block + transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - return transformBlock; - } + // Join the source blocks, so if they need to switch to UI thread to complete + // and someone is blocked on us on the same thread, the call proceeds + JoinUpstreamDataSources(source); - private DesignTimeInputs GetDesignTimeInputs(IImmutableDictionary currentState) - { - var designTimeInputs = new HashSet(StringComparers.Paths); - var designTimeSharedInputs = new HashSet(StringComparers.Paths); + return transformBlock; + } - foreach ((string itemName, IImmutableDictionary metadata) in currentState.GetSnapshotOrEmpty(Compile.SchemaName).Items) - { - (bool designTime, bool designTimeShared) = GetDesignTimePropsForItem(metadata); + private DesignTimeInputs GetDesignTimeInputs(IImmutableDictionary currentState) + { + var designTimeInputs = new HashSet(StringComparers.Paths); + var designTimeSharedInputs = new HashSet(StringComparers.Paths); - string fullPath = _project.MakeRooted(itemName); + foreach ((string itemName, IImmutableDictionary metadata) in currentState.GetSnapshotOrEmpty(Compile.SchemaName).Items) + { + (bool designTime, bool designTimeShared) = GetDesignTimePropsForItem(metadata); - if (designTime) - { - designTimeInputs.Add(fullPath); - } + string fullPath = _project.MakeRooted(itemName); - // Legacy allows files to be DesignTime and DesignTimeShared - if (designTimeShared) - { - designTimeSharedInputs.Add(fullPath); - } + if (designTime) + { + designTimeInputs.Add(fullPath); } - return new DesignTimeInputs(designTimeInputs, designTimeSharedInputs); + // Legacy allows files to be DesignTime and DesignTimeShared + if (designTimeShared) + { + designTimeSharedInputs.Add(fullPath); + } } - private static (bool designTime, bool designTimeShared) GetDesignTimePropsForItem(IImmutableDictionary item) - { - item.TryGetValue(Compile.LinkProperty, out string linkString); - item.TryGetValue(Compile.DesignTimeProperty, out string designTimeString); - item.TryGetValue(Compile.DesignTimeSharedInputProperty, out string designTimeSharedString); + return new DesignTimeInputs(designTimeInputs, designTimeSharedInputs); + } - if (!string.IsNullOrEmpty(linkString)) - { - // Linked files are never used as TempPE inputs - return (false, false); - } + private static (bool designTime, bool designTimeShared) GetDesignTimePropsForItem(IImmutableDictionary item) + { + item.TryGetValue(Compile.LinkProperty, out string linkString); + item.TryGetValue(Compile.DesignTimeProperty, out string designTimeString); + item.TryGetValue(Compile.DesignTimeSharedInputProperty, out string designTimeSharedString); - return (StringComparers.PropertyLiteralValues.Equals(designTimeString, bool.TrueString), StringComparers.PropertyLiteralValues.Equals(designTimeSharedString, bool.TrueString)); + if (!string.IsNullOrEmpty(linkString)) + { + // Linked files are never used as TempPE inputs + return (false, false); } + + return (StringComparers.PropertyLiteralValues.Equals(designTimeString, bool.TrueString), StringComparers.PropertyLiteralValues.Equals(designTimeSharedString, bool.TrueString)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcher.cs index 8c02709155..12e6952e53 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcher.cs @@ -4,186 +4,185 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +/// +/// Produces output whenever a design time input changes +/// +[Export(typeof(IDesignTimeInputsFileWatcher))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] +internal class DesignTimeInputsFileWatcher : ProjectValueDataSourceBase, IVsFreeThreadedFileChangeEvents2, IDesignTimeInputsFileWatcher { + private readonly UnconfiguredProject _project; + private readonly IProjectThreadingService _threadingService; + private readonly IDesignTimeInputsDataSource _designTimeInputsDataSource; + private readonly IVsService _fileChangeService; + private readonly Dictionary _fileWatcherCookies = new(StringComparers.Paths); + + private int _version; + private IDisposable? _dataSourceLink; + + /// + /// The block that receives updates from the active tree provider. + /// + private IBroadcastBlock>? _broadcastBlock; + + /// + /// The public facade for the broadcast block. + /// + private IReceivableSourceBlock>? _publicBlock; + /// - /// Produces output whenever a design time input changes + /// The block that actually does our processing /// - [Export(typeof(IDesignTimeInputsFileWatcher))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasicLanguageService)] - internal class DesignTimeInputsFileWatcher : ProjectValueDataSourceBase, IVsFreeThreadedFileChangeEvents2, IDesignTimeInputsFileWatcher + private ITargetBlock>? _actionBlock; + + [ImportingConstructor] + public DesignTimeInputsFileWatcher(UnconfiguredProject project, + IUnconfiguredProjectServices unconfiguredProjectServices, + IProjectThreadingService threadingService, + IDesignTimeInputsDataSource designTimeInputsDataSource, + IVsService fileChangeService) + : base(unconfiguredProjectServices, synchronousDisposal: false, registerDataSource: false) { - private readonly UnconfiguredProject _project; - private readonly IProjectThreadingService _threadingService; - private readonly IDesignTimeInputsDataSource _designTimeInputsDataSource; - private readonly IVsService _fileChangeService; - private readonly Dictionary _fileWatcherCookies = new(StringComparers.Paths); - - private int _version; - private IDisposable? _dataSourceLink; - - /// - /// The block that receives updates from the active tree provider. - /// - private IBroadcastBlock>? _broadcastBlock; - - /// - /// The public facade for the broadcast block. - /// - private IReceivableSourceBlock>? _publicBlock; - - /// - /// The block that actually does our processing - /// - private ITargetBlock>? _actionBlock; - - [ImportingConstructor] - public DesignTimeInputsFileWatcher(UnconfiguredProject project, - IUnconfiguredProjectServices unconfiguredProjectServices, - IProjectThreadingService threadingService, - IDesignTimeInputsDataSource designTimeInputsDataSource, - IVsService fileChangeService) - : base(unconfiguredProjectServices, synchronousDisposal: false, registerDataSource: false) - { - _project = project; - _threadingService = threadingService; - _designTimeInputsDataSource = designTimeInputsDataSource; - _fileChangeService = fileChangeService; - } + _project = project; + _threadingService = threadingService; + _designTimeInputsDataSource = designTimeInputsDataSource; + _fileChangeService = fileChangeService; + } - /// - /// This is to allow unit tests to force completion of our source block rather than waiting for async work to complete - /// - internal bool AllowSourceBlockCompletion { get; set; } + /// + /// This is to allow unit tests to force completion of our source block rather than waiting for async work to complete + /// + internal bool AllowSourceBlockCompletion { get; set; } - public override NamedIdentity DataSourceKey { get; } = new NamedIdentity(nameof(DesignTimeInputsFileWatcher)); + public override NamedIdentity DataSourceKey { get; } = new NamedIdentity(nameof(DesignTimeInputsFileWatcher)); - public override IComparable DataSourceVersion => _version; + public override IComparable DataSourceVersion => _version; - public override IReceivableSourceBlock> SourceBlock + public override IReceivableSourceBlock> SourceBlock + { + get { - get - { - EnsureInitialized(); + EnsureInitialized(); - return _publicBlock!; - } + return _publicBlock!; } + } - protected override void Initialize() - { - base.Initialize(); + protected override void Initialize() + { + base.Initialize(); - _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(nameFormat: nameof(DesignTimeInputsFileWatcher) + " Broadcast: {1}"); - _publicBlock = AllowSourceBlockCompletion ? _broadcastBlock : _broadcastBlock.SafePublicize(); + _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(nameFormat: nameof(DesignTimeInputsFileWatcher) + " Broadcast: {1}"); + _publicBlock = AllowSourceBlockCompletion ? _broadcastBlock : _broadcastBlock.SafePublicize(); - _actionBlock = DataflowBlockFactory.CreateActionBlock>(ProcessDesignTimeInputsAsync, _project); + _actionBlock = DataflowBlockFactory.CreateActionBlock>(ProcessDesignTimeInputsAsync, _project); - _dataSourceLink = _designTimeInputsDataSource.SourceBlock.LinkTo(_actionBlock, DataflowOption.PropagateCompletion); + _dataSourceLink = _designTimeInputsDataSource.SourceBlock.LinkTo(_actionBlock, DataflowOption.PropagateCompletion); - JoinUpstreamDataSources(_designTimeInputsDataSource); - } + JoinUpstreamDataSources(_designTimeInputsDataSource); + } - private async Task ProcessDesignTimeInputsAsync(IProjectVersionedValue input) - { - DesignTimeInputs designTimeInputs = input.Value; + private async Task ProcessDesignTimeInputsAsync(IProjectVersionedValue input) + { + DesignTimeInputs designTimeInputs = input.Value; - IVsAsyncFileChangeEx vsAsyncFileChangeEx = await _fileChangeService.GetValueAsync(); + IVsAsyncFileChangeEx vsAsyncFileChangeEx = await _fileChangeService.GetValueAsync(); - // we don't care about the difference between types of inputs, so we just construct one hashset for fast comparisons later - var allFiles = new HashSet(StringComparers.Paths); - allFiles.AddRange(designTimeInputs.Inputs); - allFiles.AddRange(designTimeInputs.SharedInputs); + // we don't care about the difference between types of inputs, so we just construct one hashset for fast comparisons later + var allFiles = new HashSet(StringComparers.Paths); + allFiles.AddRange(designTimeInputs.Inputs); + allFiles.AddRange(designTimeInputs.SharedInputs); - // Remove any files we're watching that we don't care about any more - var removedFiles = new List(); - foreach ((string file, uint cookie) in _fileWatcherCookies) + // Remove any files we're watching that we don't care about any more + var removedFiles = new List(); + foreach ((string file, uint cookie) in _fileWatcherCookies) + { + if (!allFiles.Contains(file)) { - if (!allFiles.Contains(file)) - { - await vsAsyncFileChangeEx.UnadviseFileChangeAsync(cookie); - removedFiles.Add(file); - } + await vsAsyncFileChangeEx.UnadviseFileChangeAsync(cookie); + removedFiles.Add(file); } + } - foreach (string file in removedFiles) - { - _fileWatcherCookies.Remove(file); - } + foreach (string file in removedFiles) + { + _fileWatcherCookies.Remove(file); + } - // Now watch and output files that are new - foreach (string file in allFiles) + // Now watch and output files that are new + foreach (string file in allFiles) + { + if (!_fileWatcherCookies.ContainsKey(file)) { - if (!_fileWatcherCookies.ContainsKey(file)) - { - // We don't care about delete and add here, as they come through data flow, plus they are really bouncy - every file change is a Time, Del and Add event) - uint cookie = await vsAsyncFileChangeEx.AdviseFileChangeAsync(file, _VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Size, sink: this); + // We don't care about delete and add here, as they come through data flow, plus they are really bouncy - every file change is a Time, Del and Add event) + uint cookie = await vsAsyncFileChangeEx.AdviseFileChangeAsync(file, _VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Size, sink: this); - _fileWatcherCookies.Add(file, cookie); - } + _fileWatcherCookies.Add(file, cookie); } } + } - private void PublishFiles(string[] files) - { - _version++; - _broadcastBlock?.Post(new ProjectVersionedValue( - files, - Empty.ProjectValueVersions.Add(DataSourceKey, _version))); - } + private void PublishFiles(string[] files) + { + _version++; + _broadcastBlock?.Post(new ProjectVersionedValue( + files, + Empty.ProjectValueVersions.Add(DataSourceKey, _version))); + } - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) + { + if (disposing) { - if (disposing) + // Completing the output block before the action block means any final messages that are currently being produced + // will not be sent out, which is what we want in this case. + _broadcastBlock?.Complete(); + _dataSourceLink?.Dispose(); + + if (_actionBlock is not null) { - // Completing the output block before the action block means any final messages that are currently being produced - // will not be sent out, which is what we want in this case. - _broadcastBlock?.Complete(); - _dataSourceLink?.Dispose(); + _actionBlock.Complete(); - if (_actionBlock is not null) + _threadingService.ExecuteSynchronously(async () => { - _actionBlock.Complete(); + // Wait for any processing to finish so we don't fight over the cookies 🍪 + await _actionBlock.Completion; - _threadingService.ExecuteSynchronously(async () => + IVsAsyncFileChangeEx vsAsyncFileChangeEx = await _fileChangeService.GetValueAsync(); + + // Unsubscribe from all files + foreach (uint cookie in _fileWatcherCookies.Values) { - // Wait for any processing to finish so we don't fight over the cookies 🍪 - await _actionBlock.Completion; - - IVsAsyncFileChangeEx vsAsyncFileChangeEx = await _fileChangeService.GetValueAsync(); - - // Unsubscribe from all files - foreach (uint cookie in _fileWatcherCookies.Values) - { - await vsAsyncFileChangeEx.UnadviseFileChangeAsync(cookie); - } - }); - } + await vsAsyncFileChangeEx.UnadviseFileChangeAsync(cookie); + } + }); } - - base.Dispose(disposing); } - public int FilesChanged(uint cChanges, string[] rgpszFile, uint[] rggrfChange) - { - PublishFiles(rgpszFile); + base.Dispose(disposing); + } - return HResult.OK; - } + public int FilesChanged(uint cChanges, string[] rgpszFile, uint[] rggrfChange) + { + PublishFiles(rgpszFile); - public int DirectoryChanged(string pszDirectory) - { - return HResult.NotImplemented; - } + return HResult.OK; + } - public int DirectoryChangedEx(string pszDirectory, string pszFile) - { - return HResult.NotImplemented; - } + public int DirectoryChanged(string pszDirectory) + { + return HResult.NotImplemented; + } - public int DirectoryChangedEx2(string pszDirectory, uint cChanges, string[] rgpszFile, uint[] rggrfChange) - { - return HResult.NotImplemented; - } + public int DirectoryChangedEx(string pszDirectory, string pszFile) + { + return HResult.NotImplemented; + } + + public int DirectoryChangedEx2(string pszDirectory, uint cChanges, string[] rgpszFile, uint[] rggrfChange) + { + return HResult.NotImplemented; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsBuildManagerBridge.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsBuildManagerBridge.cs index ee17d4dab3..99847abeb6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsBuildManagerBridge.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsBuildManagerBridge.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +/// +/// Provides a bridge from the DesignTimeInputs system to the UI thread, for use by the BuildManager, which is part of VSLangProj +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IDesignTimeInputsBuildManagerBridge { /// - /// Provides a bridge from the DesignTimeInputs system to the UI thread, for use by the BuildManager, which is part of VSLangProj + /// Get the list of design time monikers that need to have TempPE libraries created. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IDesignTimeInputsBuildManagerBridge - { - /// - /// Get the list of design time monikers that need to have TempPE libraries created. - /// - Task GetDesignTimeOutputMonikersAsync(); + Task GetDesignTimeOutputMonikersAsync(); - /// - /// Builds a temporary portable executable (PE) and returns its description in an XML string. - /// - Task BuildDesignTimeOutputAsync(string outputMoniker); - } + /// + /// Builds a temporary portable executable (PE) and returns its description in an XML string. + /// + Task BuildDesignTimeOutputAsync(string outputMoniker); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsChangeTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsChangeTracker.cs index 07917b2ac2..5b0c3452cf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsChangeTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsChangeTracker.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +/// +/// Represents the data source of source items that are design time inputs or shared design time inputs, and have changed +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IDesignTimeInputsChangeTracker : IProjectValueDataSource { - /// - /// Represents the data source of source items that are design time inputs or shared design time inputs, and have changed - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IDesignTimeInputsChangeTracker : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsCompiler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsCompiler.cs index 214c19883e..7529feb5c5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsCompiler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsCompiler.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IDesignTimeInputsCompiler : IDisposable { - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IDesignTimeInputsCompiler : IDisposable - { - /// - /// Gets the XML that describes a TempPE DLL, including building it if necessary - /// - /// A project relative path to a source file that is a design time input - /// The path in which to place the TempPE DLL if one is created - /// The list of shared inputs to be included in the TempPE DLL - /// An XML description of the TempPE DLL for the specified file - Task BuildDesignTimeOutputAsync(string outputMoniker, string tempPEOutputPath, ImmutableHashSet sharedInputs); - } + /// + /// Gets the XML that describes a TempPE DLL, including building it if necessary + /// + /// A project relative path to a source file that is a design time input + /// The path in which to place the TempPE DLL if one is created + /// The list of shared inputs to be included in the TempPE DLL + /// An XML description of the TempPE DLL for the specified file + Task BuildDesignTimeOutputAsync(string outputMoniker, string tempPEOutputPath, ImmutableHashSet sharedInputs); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsDataSource.cs index 32b3c85c8a..7f331be03f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsDataSource.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +/// +/// Represents the data source of source items that are design time inputs or shared design time inputs +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IDesignTimeInputsDataSource : IProjectValueDataSource { - /// - /// Represents the data source of source items that are design time inputs or shared design time inputs - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IDesignTimeInputsDataSource : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsFileWatcher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsFileWatcher.cs index 30d5b79da2..4815e34715 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsFileWatcher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/TempPE/IDesignTimeInputsFileWatcher.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +/// +/// Represents a data source that produces output whenever a design time input changes +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IDesignTimeInputsFileWatcher : IProjectValueDataSource { - /// - /// Represents a data source that produces output whenever a design time input changes - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IDesignTimeInputsFileWatcher : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs index df3b8d0301..943d8575ac 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainedByRelationCollection.cs @@ -2,56 +2,55 @@ using System.Collections; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// An immutable collection of "contained-by" (parent) items for an , +/// aggregated across it's potentially many s. Eagerly populated. Used as +/// part of Solution Explorer search. +/// +public sealed class AggregateContainedByRelationCollection : IAggregateRelationCollection { /// - /// An immutable collection of "contained-by" (parent) items for an , - /// aggregated across it's potentially many s. Eagerly populated. Used as - /// part of Solution Explorer search. + /// doesn't change for this collection type. /// - public sealed class AggregateContainedByRelationCollection : IAggregateRelationCollection - { - /// - /// doesn't change for this collection type. - /// - event EventHandler IAggregateRelationCollection.HasItemsChanged { add { } remove { } } - - private readonly List _parentItems; + event EventHandler IAggregateRelationCollection.HasItemsChanged { add { } remove { } } - internal AggregateContainedByRelationCollection(List parentItems) - { - Requires.NotNull(parentItems); + private readonly List _parentItems; - _parentItems = parentItems; - } + internal AggregateContainedByRelationCollection(List parentItems) + { + Requires.NotNull(parentItems); - public bool HasItems => _parentItems.Count != 0; + _parentItems = parentItems; + } - void IAggregateRelationCollection.EnsureMaterialized() { } + public bool HasItems => _parentItems.Count != 0; - public IEnumerator GetEnumerator() => _parentItems.GetEnumerator(); + void IAggregateRelationCollection.EnsureMaterialized() { } - public int Count => _parentItems.Count; + public IEnumerator GetEnumerator() => _parentItems.GetEnumerator(); - public bool Contains(object value) => _parentItems.Contains(value); + public int Count => _parentItems.Count; - public int IndexOf(object value) => _parentItems.IndexOf(value); + public bool Contains(object value) => _parentItems.Contains(value); - object IList.this[int index] - { - get => _parentItems[index]; - set => throw new NotSupportedException(); - } + public int IndexOf(object value) => _parentItems.IndexOf(value); - void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException(); - object ICollection.SyncRoot => throw new NotSupportedException(); - bool ICollection.IsSynchronized => false; - int IList.Add(object value) => throw new NotSupportedException(); - void IList.Clear() => throw new NotSupportedException(); - void IList.Insert(int index, object value) => throw new NotSupportedException(); - void IList.Remove(object value) => throw new NotSupportedException(); - void IList.RemoveAt(int index) => throw new NotSupportedException(); - bool IList.IsReadOnly => true; - bool IList.IsFixedSize => true; + object IList.this[int index] + { + get => _parentItems[index]; + set => throw new NotSupportedException(); } + + void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException(); + object ICollection.SyncRoot => throw new NotSupportedException(); + bool ICollection.IsSynchronized => false; + int IList.Add(object value) => throw new NotSupportedException(); + void IList.Clear() => throw new NotSupportedException(); + void IList.Insert(int index, object value) => throw new NotSupportedException(); + void IList.Remove(object value) => throw new NotSupportedException(); + void IList.RemoveAt(int index) => throw new NotSupportedException(); + bool IList.IsReadOnly => true; + bool IList.IsFixedSize => true; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollection.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollection.cs index 96f75e5b67..0839855bbe 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollection.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollection.cs @@ -4,160 +4,126 @@ using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// An observable collection that aggregates child items of a given parent +/// across all of its s. Supports lazy materialization of child items. +/// +public sealed class AggregateContainsRelationCollection : IAggregateRelationCollection, INotifyCollectionChanged { /// - /// An observable collection that aggregates child items of a given parent - /// across all of its s. Supports lazy materialization of child items. + /// Attempts to create a collection for the children of . + /// Fails only when no relations exist to produce child items for the given item's type. /// - public sealed class AggregateContainsRelationCollection : IAggregateRelationCollection, INotifyCollectionChanged + public static bool TryCreate(IRelatableItem parentItem, IRelationProvider relationProvider, [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection? collection) { - /// - /// Attempts to create a collection for the children of . - /// Fails only when no relations exist to produce child items for the given item's type. - /// - public static bool TryCreate(IRelatableItem parentItem, IRelationProvider relationProvider, [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection? collection) + ImmutableArray containsRelations = relationProvider.GetContainsRelationsFor(parentItem.GetType()); + + if (containsRelations.IsEmpty) { - ImmutableArray containsRelations = relationProvider.GetContainsRelationsFor(parentItem.GetType()); + collection = null; + return false; + } - if (containsRelations.IsEmpty) - { - collection = null; - return false; - } + collection = new AggregateContainsRelationCollection(parentItem, containsRelations); + return true; + } - collection = new AggregateContainsRelationCollection(parentItem, containsRelations); - return true; - } + private event NotifyCollectionChangedEventHandler? CollectionChanged; + private event EventHandler? HasItemsChanged; - private event NotifyCollectionChangedEventHandler? CollectionChanged; - private event EventHandler? HasItemsChanged; + private readonly AggregateContainsRelationCollectionSpan[] _spans; + private readonly IRelatableItem _item; + private bool _isMaterialized; - private readonly AggregateContainsRelationCollectionSpan[] _spans; - private readonly IRelatableItem _item; - private bool _isMaterialized; + private AggregateContainsRelationCollection(IRelatableItem item, ImmutableArray containsRelations) + { + _item = item; + _spans = containsRelations + .Select(relation => new AggregateContainsRelationCollectionSpan(this, relation)) + .ToArray(); + } - private AggregateContainsRelationCollection(IRelatableItem item, ImmutableArray containsRelations) + /// + /// Called when the parent item's state is updated in order to propagate those state changes + /// across relations to any materialized items, recursively. + /// + public void OnStateUpdated() + { + if (_isMaterialized) { - _item = item; - _spans = containsRelations - .Select(relation => new AggregateContainsRelationCollectionSpan(this, relation)) - .ToArray(); - } + int beforeCount = 0; + int afterCount = 0; - /// - /// Called when the parent item's state is updated in order to propagate those state changes - /// across relations to any materialized items, recursively. - /// - public void OnStateUpdated() - { - if (_isMaterialized) + foreach (AggregateContainsRelationCollectionSpan span in _spans) { - int beforeCount = 0; - int afterCount = 0; - - foreach (AggregateContainsRelationCollectionSpan span in _spans) - { - beforeCount += span.Items?.Count ?? 0; - span.BaseIndex = afterCount; - span.Relation.UpdateContainsCollection(parent: _item, span); - afterCount += span.Items?.Count ?? 0; - } + beforeCount += span.Items?.Count ?? 0; + span.BaseIndex = afterCount; + span.Relation.UpdateContainsCollection(parent: _item, span); + afterCount += span.Items?.Count ?? 0; + } - if ((beforeCount == 0) != (afterCount == 0)) - { - HasItemsChanged?.Invoke(this, EventArgs.Empty); - } + if ((beforeCount == 0) != (afterCount == 0)) + { + HasItemsChanged?.Invoke(this, EventArgs.Empty); } } + } - event NotifyCollectionChangedEventHandler? INotifyCollectionChanged.CollectionChanged - { - add => CollectionChanged += value; - remove => CollectionChanged -= value; - } + event NotifyCollectionChangedEventHandler? INotifyCollectionChanged.CollectionChanged + { + add => CollectionChanged += value; + remove => CollectionChanged -= value; + } - event EventHandler IAggregateRelationCollection.HasItemsChanged - { - add => HasItemsChanged += value; - remove => HasItemsChanged -= value; - } + event EventHandler IAggregateRelationCollection.HasItemsChanged + { + add => HasItemsChanged += value; + remove => HasItemsChanged -= value; + } - bool IAggregateRelationCollection.HasItems - => _isMaterialized - ? _spans.Any(static span => span.Items?.Count > 0) - : _spans.Any(static (span, item) => span.Relation.HasContainedItem(item), _item); + bool IAggregateRelationCollection.HasItems + => _isMaterialized + ? _spans.Any(static span => span.Items?.Count > 0) + : _spans.Any(static (span, item) => span.Relation.HasContainedItem(item), _item); - int ICollection.Count => _spans.Sum(span => span.Items?.Count ?? 0); + int ICollection.Count => _spans.Sum(span => span.Items?.Count ?? 0); - void IAggregateRelationCollection.EnsureMaterialized() + void IAggregateRelationCollection.EnsureMaterialized() + { + if (!_isMaterialized) { - if (!_isMaterialized) - { - // Set this to true first, as events raised during materialization may trigger calls back - // into this object to read partially constructed state which is valid and must be supported. - _isMaterialized = true; + // Set this to true first, as events raised during materialization may trigger calls back + // into this object to read partially constructed state which is valid and must be supported. + _isMaterialized = true; - OnStateUpdated(); - } + OnStateUpdated(); } + } - internal void RaiseChange(NotifyCollectionChangedEventArgs e) => CollectionChanged?.Invoke(this, e); + internal void RaiseChange(NotifyCollectionChangedEventArgs e) => CollectionChanged?.Invoke(this, e); - IEnumerator IEnumerable.GetEnumerator() - { - foreach (AggregateContainsRelationCollectionSpan span in _spans) - { - if (span.Items is not null) - { - foreach (IRelatableItem item in span.Items) - { - yield return item; - } - } - } - } - - object IList.this[int index] + IEnumerator IEnumerable.GetEnumerator() + { + foreach (AggregateContainsRelationCollectionSpan span in _spans) { - get + if (span.Items is not null) { - if (_isMaterialized) + foreach (IRelatableItem item in span.Items) { - foreach (AggregateContainsRelationCollectionSpan span in _spans) - { - if (span.Items is null) - { - continue; - } - - int spanCount = span.Items.Count; - - if (spanCount == 0) - { - continue; - } - - if (index < spanCount) - { - return span.Items[index]; - } - - index -= spanCount; - } + yield return item; } - - throw new ArgumentOutOfRangeException(nameof(index), index, "Invalid index."); } - set => throw new NotSupportedException(); } + } - int IList.IndexOf(object value) + object IList.this[int index] + { + get { - if (_isMaterialized && value is IRelatableItem item) + if (_isMaterialized) { - int baseIndex = 0; - foreach (AggregateContainsRelationCollectionSpan span in _spans) { if (span.Items is null) @@ -172,31 +138,64 @@ int IList.IndexOf(object value) continue; } - int index = span.Items.IndexOf(item); - - if (index != -1) + if (index < spanCount) { - return baseIndex + index; + return span.Items[index]; } - baseIndex += spanCount; + index -= spanCount; } } - return -1; + throw new ArgumentOutOfRangeException(nameof(index), index, "Invalid index."); + } + set => throw new NotSupportedException(); + } + + int IList.IndexOf(object value) + { + if (_isMaterialized && value is IRelatableItem item) + { + int baseIndex = 0; + + foreach (AggregateContainsRelationCollectionSpan span in _spans) + { + if (span.Items is null) + { + continue; + } + + int spanCount = span.Items.Count; + + if (spanCount == 0) + { + continue; + } + + int index = span.Items.IndexOf(item); + + if (index != -1) + { + return baseIndex + index; + } + + baseIndex += spanCount; + } } - bool IList.Contains(object value) => value is IRelatableItem item && _spans.Any(static (span, item) => span.Items?.Contains(item) == true, item); - - void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException(); - object ICollection.SyncRoot => throw new NotSupportedException(); - bool ICollection.IsSynchronized => false; - int IList.Add(object value) => throw new NotSupportedException(); - void IList.Clear() => throw new NotSupportedException(); - void IList.Insert(int index, object value) => throw new NotSupportedException(); - void IList.Remove(object value) => throw new NotSupportedException(); - void IList.RemoveAt(int index) => throw new NotSupportedException(); - bool IList.IsReadOnly => true; - bool IList.IsFixedSize => true; + return -1; } + + bool IList.Contains(object value) => value is IRelatableItem item && _spans.Any(static (span, item) => span.Items?.Contains(item) == true, item); + + void ICollection.CopyTo(Array array, int index) => throw new NotSupportedException(); + object ICollection.SyncRoot => throw new NotSupportedException(); + bool ICollection.IsSynchronized => false; + int IList.Add(object value) => throw new NotSupportedException(); + void IList.Clear() => throw new NotSupportedException(); + void IList.Insert(int index, object value) => throw new NotSupportedException(); + void IList.Remove(object value) => throw new NotSupportedException(); + void IList.RemoveAt(int index) => throw new NotSupportedException(); + bool IList.IsReadOnly => true; + bool IList.IsFixedSize => true; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollectionSpan.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollectionSpan.cs index 69e59e171c..7c5b54d09b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollectionSpan.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateContainsRelationCollectionSpan.cs @@ -2,169 +2,168 @@ using System.Collections.Specialized; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Represents a subsection within a which is +/// owned by a particular . +/// +/// +/// +/// Instances of this type are owned by their parent +/// and passed to whenever the children of the parent item +/// must be created or updated. +/// +/// +/// Implementations should pay particular attention to the documentation of +/// as it has specific requirements of its arguments in order to perform correctly with good performance. +/// +/// +public sealed class AggregateContainsRelationCollectionSpan { + private readonly AggregateContainsRelationCollection _parent; + + internal int BaseIndex { get; set; } + internal IRelation Relation { get; } + internal List? Items { get; private set; } + + internal AggregateContainsRelationCollectionSpan(AggregateContainsRelationCollection parent, IRelation relation) + { + Requires.NotNull(parent); + Requires.NotNull(relation); + + _parent = parent; + Relation = relation; + } + /// - /// Represents a subsection within a which is - /// owned by a particular . + /// Updates the items contained within this span, which ultimately contributes to the + /// parent . /// /// /// - /// Instances of this type are owned by their parent - /// and passed to whenever the children of the parent item - /// must be created or updated. + /// This method runs on the UI thread, recursively across all materialized collections in the tree. + /// In order to provide linear time complexity, must have a stable sorted order across invocations + /// of this method. Failing this requirement will result in increased numbers of updates to items, degrading performance. /// /// - /// Implementations should pay particular attention to the documentation of - /// as it has specific requirements of its arguments in order to perform correctly with good performance. + /// This method operates over the ordered sequence of values in , + /// comparing them each in turn (via ) to any existing items + /// in the span. This comparison only considers the 'identity' of its operands, not their state. The comparison determines + /// what happens for that data/item pair: + /// + /// + /// returns zero -- the source value and existing item match. is + /// called with both, allowing the data value to update the item in-place. If returns + /// then any materialized descendents of the item are updated recursively via their relations. + /// + /// + /// returns negative -- the source value does not exist in the current collection and should + /// be added. is called to produce the new item to insert. + /// + /// + /// returns positive -- the source no longer contains the item, and it should be removed. + /// + /// + /// This method ensures the appropriate events are triggered on the parent + /// in response to these updates. /// /// - public sealed class AggregateContainsRelationCollectionSpan + /// + /// The sequence of data values that the resulting items in this span will reflect when this method completes. The sequence must + /// be ordered in a way that . + /// + /// + /// Compares a source value with an existing item to determine whether they have equivalent identity. + /// Does not consider the state within either type while producing its result. + /// + /// + /// Updates a tree item based on a data value. Returns is the update mutated the tree item in + /// some way, otherwise . + /// + /// Creates a new item for a given data value. + /// + public void UpdateContainsItems( + IEnumerable sources, + Func comparer, + Func update, + Func factory) + where TItem : class, IRelatableItem { - private readonly AggregateContainsRelationCollection _parent; - - internal int BaseIndex { get; set; } - internal IRelation Relation { get; } - internal List? Items { get; private set; } - - internal AggregateContainsRelationCollectionSpan(AggregateContainsRelationCollection parent, IRelation relation) - { - Requires.NotNull(parent); - Requires.NotNull(relation); + using IEnumerator src = sources.GetEnumerator(); - _parent = parent; - Relation = relation; - } + bool? srcConsumed = null; - /// - /// Updates the items contained within this span, which ultimately contributes to the - /// parent . - /// - /// - /// - /// This method runs on the UI thread, recursively across all materialized collections in the tree. - /// In order to provide linear time complexity, must have a stable sorted order across invocations - /// of this method. Failing this requirement will result in increased numbers of updates to items, degrading performance. - /// - /// - /// This method operates over the ordered sequence of values in , - /// comparing them each in turn (via ) to any existing items - /// in the span. This comparison only considers the 'identity' of its operands, not their state. The comparison determines - /// what happens for that data/item pair: - /// - /// - /// returns zero -- the source value and existing item match. is - /// called with both, allowing the data value to update the item in-place. If returns - /// then any materialized descendents of the item are updated recursively via their relations. - /// - /// - /// returns negative -- the source value does not exist in the current collection and should - /// be added. is called to produce the new item to insert. - /// - /// - /// returns positive -- the source no longer contains the item, and it should be removed. - /// - /// - /// This method ensures the appropriate events are triggered on the parent - /// in response to these updates. - /// - /// - /// - /// The sequence of data values that the resulting items in this span will reflect when this method completes. The sequence must - /// be ordered in a way that . - /// - /// - /// Compares a source value with an existing item to determine whether they have equivalent identity. - /// Does not consider the state within either type while producing its result. - /// - /// - /// Updates a tree item based on a data value. Returns is the update mutated the tree item in - /// some way, otherwise . - /// - /// Creates a new item for a given data value. - /// - public void UpdateContainsItems( - IEnumerable sources, - Func comparer, - Func update, - Func factory) - where TItem : class, IRelatableItem + for (int itemIndex = 0; Items is not null && itemIndex < Items.Count; itemIndex++) { - using IEnumerator src = sources.GetEnumerator(); - - bool? srcConsumed = null; - - for (int itemIndex = 0; Items is not null && itemIndex < Items.Count; itemIndex++) + if (srcConsumed != false && !src.MoveNext()) { - if (srcConsumed != false && !src.MoveNext()) + // Source stream ended, all remaining items are invalid. Remove each in turn. + // Note we do not reset as that reset would refresh the entire parent collection, not just this span of items. + // We remove items in reverse order to reduce shuffling items in collections. + for (int removeAtIndex = Items.Count - 1; removeAtIndex >= itemIndex; removeAtIndex--) { - // Source stream ended, all remaining items are invalid. Remove each in turn. - // Note we do not reset as that reset would refresh the entire parent collection, not just this span of items. - // We remove items in reverse order to reduce shuffling items in collections. - for (int removeAtIndex = Items.Count - 1; removeAtIndex >= itemIndex; removeAtIndex--) - { - IRelatableItem removedItem = Items[removeAtIndex]; - Items.RemoveAt(removeAtIndex); - _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItem, BaseIndex + removeAtIndex)); - } - - srcConsumed = true; - break; + IRelatableItem removedItem = Items[removeAtIndex]; + Items.RemoveAt(removeAtIndex); + _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItem, BaseIndex + removeAtIndex)); } - TData source = src.Current; + srcConsumed = true; + break; + } - var item = (TItem)Items[itemIndex]; + TData source = src.Current; - int comparison = comparer(source, item); + var item = (TItem)Items[itemIndex]; - if (comparison == 0) - { - // Items match, update in place - if (update(source, item)) - { - // The update changed state, so notify its contains collection to update any materialized children via its relations - item.ContainsCollection?.OnStateUpdated(); - } - srcConsumed = true; - } - else if (comparison < 0) - { - // Source contains a new item to insert - TItem newItem = factory(source); - Items.Insert(itemIndex, newItem); - _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, BaseIndex + itemIndex)); - srcConsumed = true; - } - else - { - // Source is missing this item, remove it - Items.RemoveAt(itemIndex); - _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, BaseIndex + itemIndex)); + int comparison = comparer(source, item); - // decrement the index as we've removed the item from this index and need to consider the one which is now at this index - itemIndex--; - srcConsumed = false; + if (comparison == 0) + { + // Items match, update in place + if (update(source, item)) + { + // The update changed state, so notify its contains collection to update any materialized children via its relations + item.ContainsCollection?.OnStateUpdated(); } + srcConsumed = true; } - - while (srcConsumed == false || src.MoveNext()) + else if (comparison < 0) { - // Add extra source items to end of list - TData source = src.Current; + // Source contains a new item to insert TItem newItem = factory(source); - Items ??= new List(); - Items.Add(newItem); - _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, BaseIndex + Items.Count - 1)); + Items.Insert(itemIndex, newItem); + _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, BaseIndex + itemIndex)); srcConsumed = true; } - - if (Items?.Count == 0) + else { - Items = null; + // Source is missing this item, remove it + Items.RemoveAt(itemIndex); + _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, BaseIndex + itemIndex)); + + // decrement the index as we've removed the item from this index and need to consider the one which is now at this index + itemIndex--; + srcConsumed = false; } } - public override string ToString() => $"{Relation.GetType().Name} ({Items?.Count ?? 0})"; + while (srcConsumed == false || src.MoveNext()) + { + // Add extra source items to end of list + TData source = src.Current; + TItem newItem = factory(source); + Items ??= new List(); + Items.Add(newItem); + _parent.RaiseChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, BaseIndex + Items.Count - 1)); + srcConsumed = true; + } + + if (Items?.Count == 0) + { + Items = null; + } } + + public override string ToString() => $"{Relation.GetType().Name} ({Items?.Count ?? 0})"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateRelationCollectionSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateRelationCollectionSource.cs index 5a20b75ce5..898cb88422 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateRelationCollectionSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AggregateRelationCollectionSource.cs @@ -5,75 +5,74 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Implementation of that aggregates items provided by multiple +/// s for a given source item. +/// +/// +/// Supports delayed construction of the backing instance, for +/// cases where a collection is requested immediately via a synchronous call to +/// on the UI thread, but the data +/// required for the collection must be obtained asynchronously. +/// +public sealed class AggregateRelationCollectionSource : IAsyncAttachedCollectionSource { - /// - /// Implementation of that aggregates items provided by multiple - /// s for a given source item. - /// - /// - /// Supports delayed construction of the backing instance, for - /// cases where a collection is requested immediately via a synchronous call to - /// on the UI thread, but the data - /// required for the collection must be obtained asynchronously. - /// - public sealed class AggregateRelationCollectionSource : IAsyncAttachedCollectionSource - { - public event PropertyChangedEventHandler? PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; - private readonly object _sourceItem; - private IAggregateRelationCollection? _collection; + private readonly object _sourceItem; + private IAggregateRelationCollection? _collection; - public AggregateRelationCollectionSource(object sourceItem, IAggregateRelationCollection? collection = null) - { - _sourceItem = Requires.NotNull(sourceItem); + public AggregateRelationCollectionSource(object sourceItem, IAggregateRelationCollection? collection = null) + { + _sourceItem = Requires.NotNull(sourceItem); - if (collection is not null) - { - SetCollection(collection); - } + if (collection is not null) + { + SetCollection(collection); } + } - object IAttachedCollectionSource.SourceItem => _sourceItem; + object IAttachedCollectionSource.SourceItem => _sourceItem; - IEnumerable? IAttachedCollectionSource.Items + IEnumerable? IAttachedCollectionSource.Items + { + get { - get - { - // We are being asked for items, so materialize them if they have not yet been - _collection?.EnsureMaterialized(); - return _collection; - } + // We are being asked for items, so materialize them if they have not yet been + _collection?.EnsureMaterialized(); + return _collection; } + } - // This can be computed without materializing items (by querying the item's relations) - bool IAttachedCollectionSource.HasItems => _collection?.HasItems == true; + // This can be computed without materializing items (by querying the item's relations) + bool IAttachedCollectionSource.HasItems => _collection?.HasItems == true; - // We are updating items until they are provided - bool IAsyncAttachedCollectionSource.IsUpdatingHasItems => _collection is null; + // We are updating items until they are provided + bool IAsyncAttachedCollectionSource.IsUpdatingHasItems => _collection is null; - /// - /// Sets the backing collection for this source, for cases where that collection was not available at the time - /// this collection source was constructed. - /// - /// - /// The collection has already been set, either via this method or via the constructor. - /// - public void SetCollection(IAggregateRelationCollection collection) + /// + /// Sets the backing collection for this source, for cases where that collection was not available at the time + /// this collection source was constructed. + /// + /// + /// The collection has already been set, either via this method or via the constructor. + /// + public void SetCollection(IAggregateRelationCollection collection) + { + if (_collection is not null) { - if (_collection is not null) - { - throw new InvalidOperationException("Backing collection has already been provided."); - } - - _collection = Requires.NotNull(collection); - _collection.HasItemsChanged += delegate - { - PropertyChanged?.Invoke(this, KnownEventArgs.HasItemsPropertyChanged); - }; + throw new InvalidOperationException("Backing collection has already been provided."); + } - PropertyChanged?.Invoke(this, KnownEventArgs.IsUpdatingItemsPropertyChanged); + _collection = Requires.NotNull(collection); + _collection.HasItemsChanged += delegate + { PropertyChanged?.Invoke(this, KnownEventArgs.HasItemsPropertyChanged); - } + }; + + PropertyChanged?.Invoke(this, KnownEventArgs.IsUpdatingItemsPropertyChanged); + PropertyChanged?.Invoke(this, KnownEventArgs.HasItemsPropertyChanged); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AttachedCollectionItemBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AttachedCollectionItemBase.cs index b2da08f17e..edf8745566 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AttachedCollectionItemBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/AttachedCollectionItemBase.cs @@ -6,123 +6,122 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Base class for attached items in the dependencies tree in Solution Explorer. +/// +/// +/// Subclasses should probably derive from . +/// +public abstract class AttachedCollectionItemBase + : ITreeDisplayItem, + ITreeDisplayItemWithImages, + IPrioritizedComparable, + IInteractionPatternProvider, + IBrowsablePattern, + IContextMenuPattern, + INotifyPropertyChanged { - /// - /// Base class for attached items in the dependencies tree in Solution Explorer. - /// - /// - /// Subclasses should probably derive from . - /// - public abstract class AttachedCollectionItemBase - : ITreeDisplayItem, - ITreeDisplayItemWithImages, - IPrioritizedComparable, - IInteractionPatternProvider, - IBrowsablePattern, - IContextMenuPattern, - INotifyPropertyChanged + // Other patterns we may wish to utilise in future are: + // + // - ISupportExpansionEvents + // - ISupportExpansionState + // - IDragDropSourcePattern + // - IDragDropTargetPattern + // - ISupportDisposalNotification + // - IRenamePattern + // - IPivotItemProviderPattern + // - We also see requests for IVsHierarchyItem + // + // NOTE we don't have to support ITreeDisplayItemWithImages -- it's covered by ITreeDisplayItem + + private static readonly HashSet s_supportedPatterns = new() { - // Other patterns we may wish to utilise in future are: - // - // - ISupportExpansionEvents - // - ISupportExpansionState - // - IDragDropSourcePattern - // - IDragDropTargetPattern - // - ISupportDisposalNotification - // - IRenamePattern - // - IPivotItemProviderPattern - // - We also see requests for IVsHierarchyItem - // - // NOTE we don't have to support ITreeDisplayItemWithImages -- it's covered by ITreeDisplayItem - - private static readonly HashSet s_supportedPatterns = new() - { - typeof(ITreeDisplayItem), - typeof(IBrowsablePattern), - typeof(IContextMenuPattern), - typeof(IInvocationPattern) - }; + typeof(ITreeDisplayItem), + typeof(IBrowsablePattern), + typeof(IContextMenuPattern), + typeof(IInvocationPattern) + }; - public event PropertyChangedEventHandler? PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; - private string _text; + private string _text; - protected AttachedCollectionItemBase(string name) - { - Requires.NotNullOrWhiteSpace(name); + protected AttachedCollectionItemBase(string name) + { + Requires.NotNullOrWhiteSpace(name); - _text = name; - } + _text = name; + } - public string Text + public string Text + { + get => _text; + protected set { - get => _text; - protected set + if (_text != value) { - if (_text != value) - { - _text = value; - RaisePropertyChanged(KnownEventArgs.TextPropertyChanged); - } + _text = value; + RaisePropertyChanged(KnownEventArgs.TextPropertyChanged); } } + } - public abstract int Priority { get; } + public abstract int Priority { get; } - public virtual FontStyle FontStyle => FontStyles.Normal; + public virtual FontStyle FontStyle => FontStyles.Normal; - public virtual FontWeight FontWeight => FontWeights.Normal; + public virtual FontWeight FontWeight => FontWeights.Normal; - public abstract ImageMoniker IconMoniker { get; } + public abstract ImageMoniker IconMoniker { get; } - public virtual ImageMoniker ExpandedIconMoniker => IconMoniker; + public virtual ImageMoniker ExpandedIconMoniker => IconMoniker; - public virtual ImageMoniker OverlayIconMoniker => default; + public virtual ImageMoniker OverlayIconMoniker => default; - public virtual ImageMoniker StateIconMoniker => default; + public virtual ImageMoniker StateIconMoniker => default; - public virtual string? StateToolTipText => null; + public virtual string? StateToolTipText => null; - // Return null means ToolTipText is displayed only when the item's label is truncated - public virtual object? ToolTipContent => null; + // Return null means ToolTipText is displayed only when the item's label is truncated + public virtual object? ToolTipContent => null; - public string ToolTipText => Text; + public string ToolTipText => Text; - public bool IsCut => false; + public bool IsCut => false; - public virtual object? GetBrowseObject() => null; + public virtual object? GetBrowseObject() => null; - IContextMenuController? IContextMenuPattern.ContextMenuController => ContextMenuController; + IContextMenuController? IContextMenuPattern.ContextMenuController => ContextMenuController; - protected virtual IContextMenuController? ContextMenuController => null; + protected virtual IContextMenuController? ContextMenuController => null; - public virtual TPattern? GetPattern() where TPattern : class + public virtual TPattern? GetPattern() where TPattern : class + { + if (s_supportedPatterns.Contains(typeof(TPattern))) { - if (s_supportedPatterns.Contains(typeof(TPattern))) - { - return this as TPattern; - } - - return null; + return this as TPattern; } - public virtual int CompareTo(object obj) - { - if (obj is ITreeDisplayItem item) - { - // Order by caption - return StringComparer.OrdinalIgnoreCase.Compare(Text, item.Text); - } - - return 0; - } + return null; + } - protected void RaisePropertyChanged(PropertyChangedEventArgs e) + public virtual int CompareTo(object obj) + { + if (obj is ITreeDisplayItem item) { - PropertyChanged?.Invoke(this, e); + // Order by caption + return StringComparer.OrdinalIgnoreCase.Compare(Text, item.Text); } - public override string ToString() => Text; + return 0; } + + protected void RaisePropertyChanged(PropertyChangedEventArgs e) + { + PropertyChanged?.Invoke(this, e); + } + + public override string ToString() => Text; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesAttachedCollectionSourceProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesAttachedCollectionSourceProviderBase.cs index 1a4b31835f..33750681d2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesAttachedCollectionSourceProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesAttachedCollectionSourceProviderBase.cs @@ -4,76 +4,75 @@ using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Base class for attaching children to s in the tree. +/// +/// +/// See also which attaches children and parents +/// to objects that already exist in the tree. +/// +public abstract class DependenciesAttachedCollectionSourceProviderBase : IAttachedCollectionSourceProvider { + private readonly ProjectTreeFlags _flags; + + [Import] private IRelationProvider RelationProvider { get; set; } = null!; + + protected DependenciesAttachedCollectionSourceProviderBase(ProjectTreeFlags flags) => _flags = flags; + /// - /// Base class for attaching children to s in the tree. + /// Creates a collection source for a hierarchy item. /// /// - /// See also which attaches children and parents - /// to objects that already exist in the tree. + /// Only called for hierarchy items whose flags match those passed to the constructor. /// - public abstract class DependenciesAttachedCollectionSourceProviderBase : IAttachedCollectionSourceProvider - { - private readonly ProjectTreeFlags _flags; + /// The VS hierarchy item to create a collection for. + /// Legacy parameter, no longer provided. Will always be an empty string. + /// A string that identifies the target framework, for multi-targeting projects. + /// The relation provider. + /// The returned collection source. + protected abstract bool TryCreateCollectionSource( + IVsHierarchyItem hierarchyItem, + string unused, + string? target, + IRelationProvider relationProvider, + [NotNullWhen(returnValue: true)] out AggregateRelationCollectionSource? containsCollectionSource); - [Import] private IRelationProvider RelationProvider { get; set; } = null!; - - protected DependenciesAttachedCollectionSourceProviderBase(ProjectTreeFlags flags) => _flags = flags; - - /// - /// Creates a collection source for a hierarchy item. - /// - /// - /// Only called for hierarchy items whose flags match those passed to the constructor. - /// - /// The VS hierarchy item to create a collection for. - /// Legacy parameter, no longer provided. Will always be an empty string. - /// A string that identifies the target framework, for multi-targeting projects. - /// The relation provider. - /// The returned collection source. - protected abstract bool TryCreateCollectionSource( - IVsHierarchyItem hierarchyItem, - string unused, - string? target, - IRelationProvider relationProvider, - [NotNullWhen(returnValue: true)] out AggregateRelationCollectionSource? containsCollectionSource); - - IAttachedCollectionSource? IAttachedCollectionSourceProvider.CreateCollectionSource(object item, string relationName) + IAttachedCollectionSource? IAttachedCollectionSourceProvider.CreateCollectionSource(object item, string relationName) + { + if (relationName == KnownRelationships.Contains) { - if (relationName == KnownRelationships.Contains) + if (item is IVsHierarchyItem hierarchyItem) { - if (item is IVsHierarchyItem hierarchyItem) + if (hierarchyItem.TryGetFlags(out ProjectTreeFlags flags) && flags.Contains(_flags)) { - if (hierarchyItem.TryGetFlags(out ProjectTreeFlags flags) && flags.Contains(_flags)) - { - hierarchyItem.TryFindTarget(out string? target); + hierarchyItem.TryFindTarget(out string? target); - // NOTE historically we used to pass a string having all project tree flags concatenated - // in a single string. Nothing actually uses this value, and it's expensive to create. - // Unfortunately this is a public API and the signature of the method cannot change. - // So instead, we always just pass an empty string. - string flagsString = ""; + // NOTE historically we used to pass a string having all project tree flags concatenated + // in a single string. Nothing actually uses this value, and it's expensive to create. + // Unfortunately this is a public API and the signature of the method cannot change. + // So instead, we always just pass an empty string. + string flagsString = ""; - if (TryCreateCollectionSource(hierarchyItem, flagsString, target, RelationProvider, out AggregateRelationCollectionSource? containsCollection)) - { - return containsCollection; - } + if (TryCreateCollectionSource(hierarchyItem, flagsString, target, RelationProvider, out AggregateRelationCollectionSource? containsCollection)) + { + return containsCollection; } } } - - return null; } - IEnumerable IAttachedCollectionSourceProvider.GetRelationships(object item) + return null; + } + + IEnumerable IAttachedCollectionSourceProvider.GetRelationships(object item) + { + // Unlike RelationAttachedCollectionSourceProviderBase, this method will not be + // called for context menus, as the source item is a IVsHierarchyItem in the tree. + if (item is IRelatableItem) { - // Unlike RelationAttachedCollectionSourceProviderBase, this method will not be - // called for context menus, as the source item is a IVsHierarchyItem in the tree. - if (item is IRelatableItem) - { - yield return Relationships.Contains; - } + yield return Relationships.Contains; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeConfiguredProjectSearchContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeConfiguredProjectSearchContext.cs index 5241541406..39affc0dd8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeConfiguredProjectSearchContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeConfiguredProjectSearchContext.cs @@ -3,112 +3,111 @@ using Microsoft.VisualStudio.ProjectSystem.Utilities; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +internal sealed class DependenciesTreeConfiguredProjectSearchContext : IDependenciesTreeConfiguredProjectSearchContext { - internal sealed class DependenciesTreeConfiguredProjectSearchContext : IDependenciesTreeConfiguredProjectSearchContext + private readonly DependenciesTreeSearchContext _inner; + private readonly IVsHierarchyItemManager _hierarchyItemManager; + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IRelationProvider _relationProvider; + private readonly IProjectTree _targetRootNode; + + public DependenciesTreeConfiguredProjectSearchContext( + DependenciesTreeSearchContext inner, + IProjectTree targetRootNode, + IVsHierarchyItemManager hierarchyItemManager, + IUnconfiguredProjectVsServices projectVsServices, + IRelationProvider relationProvider) + { + _inner = inner; + _hierarchyItemManager = hierarchyItemManager; + _projectVsServices = projectVsServices; + _relationProvider = relationProvider; + _targetRootNode = targetRootNode; + } + + public CancellationToken CancellationToken => _inner.CancellationToken; + + public bool IsMatch(string candidateText) => _inner.IsMatch(candidateText); + + private readonly Dictionary _itemByKey = new(); + + public void SubmitResult(IRelatableItem? item) { - private readonly DependenciesTreeSearchContext _inner; - private readonly IVsHierarchyItemManager _hierarchyItemManager; - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IRelationProvider _relationProvider; - private readonly IProjectTree _targetRootNode; - - public DependenciesTreeConfiguredProjectSearchContext( - DependenciesTreeSearchContext inner, - IProjectTree targetRootNode, - IVsHierarchyItemManager hierarchyItemManager, - IUnconfiguredProjectVsServices projectVsServices, - IRelationProvider relationProvider) + if (item is null) { - _inner = inner; - _hierarchyItemManager = hierarchyItemManager; - _projectVsServices = projectVsServices; - _relationProvider = relationProvider; - _targetRootNode = targetRootNode; + return; } - public CancellationToken CancellationToken => _inner.CancellationToken; + item = DeduplicateItem(item); - public bool IsMatch(string candidateText) => _inner.IsMatch(candidateText); + PopulateAncestors(item); - private readonly Dictionary _itemByKey = new(); + _inner.SubmitResult(item); - public void SubmitResult(IRelatableItem? item) + void PopulateAncestors(IRelatableItem childItem) { - if (item is null) + if (childItem.ContainedByCollection is not null) { + // We've already populated this item's ancestors. It's likely an ancestor of + // another search result. This also prevents runaway in case of cycles. return; } - item = DeduplicateItem(item); - - PopulateAncestors(item); + ImmutableArray containedByRelations = _relationProvider.GetContainedByRelationsFor(childItem.GetType()); - _inner.SubmitResult(item); - - void PopulateAncestors(IRelatableItem childItem) + if (containedByRelations.IsEmpty) { - if (childItem.ContainedByCollection is not null) - { - // We've already populated this item's ancestors. It's likely an ancestor of - // another search result. This also prevents runaway in case of cycles. - return; - } - - ImmutableArray containedByRelations = _relationProvider.GetContainedByRelationsFor(childItem.GetType()); + // We should never have a scenario where an item type does not have a parent. + TraceUtilities.TraceError($"No IRelation exports exist that provide parent (ContainedBy) items for type {childItem.GetType()}."); + return; + } - if (containedByRelations.IsEmpty) - { - // We should never have a scenario where an item type does not have a parent. - TraceUtilities.TraceError($"No IRelation exports exist that provide parent (ContainedBy) items for type {childItem.GetType()}."); - return; - } + var allParentItems = new List(); - var allParentItems = new List(); + childItem.ContainedByCollection = new AggregateContainedByRelationCollection(allParentItems); - childItem.ContainedByCollection = new AggregateContainedByRelationCollection(allParentItems); + foreach (IRelation relation in containedByRelations) + { + IEnumerable? relationParentItems = relation.CreateContainedByItems(childItem); - foreach (IRelation relation in containedByRelations) + if (relationParentItems is not null) { - IEnumerable? relationParentItems = relation.CreateContainedByItems(childItem); - - if (relationParentItems is not null) + foreach (IRelatableItem parentItem in relationParentItems) { - foreach (IRelatableItem parentItem in relationParentItems) + IRelatableItem deduplicateItem = DeduplicateItem(parentItem); + allParentItems.Add(deduplicateItem); + + if (deduplicateItem.TryGetProjectNode(_targetRootNode, parentItem, out IProjectTree? projectNode)) + { + uint itemId = (uint)projectNode.Identity.ToInt32(); + IVsHierarchyItem hierarchyItem = _hierarchyItemManager.GetHierarchyItem(_projectVsServices.VsHierarchy, itemId); + allParentItems.Add(hierarchyItem); + } + + if (deduplicateItem.ContainedByCollection is null) { - IRelatableItem deduplicateItem = DeduplicateItem(parentItem); - allParentItems.Add(deduplicateItem); - - if (deduplicateItem.TryGetProjectNode(_targetRootNode, parentItem, out IProjectTree? projectNode)) - { - uint itemId = (uint)projectNode.Identity.ToInt32(); - IVsHierarchyItem hierarchyItem = _hierarchyItemManager.GetHierarchyItem(_projectVsServices.VsHierarchy, itemId); - allParentItems.Add(hierarchyItem); - } - - if (deduplicateItem.ContainedByCollection is null) - { - PopulateAncestors(deduplicateItem); - } + PopulateAncestors(deduplicateItem); } } } } + } - IRelatableItem DeduplicateItem(IRelatableItem item) - { - object key = GetItemKey(item); - - if (_itemByKey.TryGetValue(key, out IRelatableItem existingItem)) - { - return existingItem; - } + IRelatableItem DeduplicateItem(IRelatableItem item) + { + object key = GetItemKey(item); - _itemByKey.Add(key, item); - return item; + if (_itemByKey.TryGetValue(key, out IRelatableItem existingItem)) + { + return existingItem; } - } - private static object GetItemKey(IRelatableItem item) => (item.GetType(), item.Identity); + _itemByKey.Add(key, item); + return item; + } } + + private static object GetItemKey(IRelatableItem item) => (item.GetType(), item.Identity); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeProjectSearchContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeProjectSearchContext.cs index 958bc5a70e..eb5a462aef 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeProjectSearchContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeProjectSearchContext.cs @@ -6,78 +6,77 @@ using Flags = Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.DependencyTreeFlags; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// A search context which decorates an instance +/// with project-specific behavior. +/// +internal sealed class DependenciesTreeProjectSearchContext : IDependenciesTreeProjectSearchContext { - /// - /// A search context which decorates an instance - /// with project-specific behavior. - /// - internal sealed class DependenciesTreeProjectSearchContext : IDependenciesTreeProjectSearchContext + private readonly DependenciesTreeSearchContext _inner; + private readonly IProjectTree _dependenciesNode; + private readonly IVsHierarchyItemManager _hierarchyItemManager; + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IRelationProvider _relationProvider; + + public UnconfiguredProject UnconfiguredProject { get; } + + public DependenciesTreeProjectSearchContext( + DependenciesTreeSearchContext outer, + UnconfiguredProject unconfiguredProject, + IProjectTree dependenciesNode, + IVsHierarchyItemManager hierarchyItemManager, + IUnconfiguredProjectVsServices projectVsServices, + IRelationProvider relationProvider) { - private readonly DependenciesTreeSearchContext _inner; - private readonly IProjectTree _dependenciesNode; - private readonly IVsHierarchyItemManager _hierarchyItemManager; - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IRelationProvider _relationProvider; - - public UnconfiguredProject UnconfiguredProject { get; } - - public DependenciesTreeProjectSearchContext( - DependenciesTreeSearchContext outer, - UnconfiguredProject unconfiguredProject, - IProjectTree dependenciesNode, - IVsHierarchyItemManager hierarchyItemManager, - IUnconfiguredProjectVsServices projectVsServices, - IRelationProvider relationProvider) - { - _inner = outer; - UnconfiguredProject = unconfiguredProject; - _dependenciesNode = dependenciesNode; - _hierarchyItemManager = hierarchyItemManager; - _projectVsServices = projectVsServices; - _relationProvider = relationProvider; - } + _inner = outer; + UnconfiguredProject = unconfiguredProject; + _dependenciesNode = dependenciesNode; + _hierarchyItemManager = hierarchyItemManager; + _projectVsServices = projectVsServices; + _relationProvider = relationProvider; + } - public CancellationToken CancellationToken => _inner.CancellationToken; + public CancellationToken CancellationToken => _inner.CancellationToken; - public async Task ForConfiguredProjectAsync(ConfiguredProject configuredProject, CancellationToken cancellationToken = default) - { - Requires.NotNull(configuredProject); + public async Task ForConfiguredProjectAsync(ConfiguredProject configuredProject, CancellationToken cancellationToken = default) + { + Requires.NotNull(configuredProject); - IProjectTree targetRootNode; + IProjectTree targetRootNode; - if (_dependenciesNode.FindChildWithFlags(Flags.TargetNode) is null) + if (_dependenciesNode.FindChildWithFlags(Flags.TargetNode) is null) + { + // Tree does not show any target nodes + targetRootNode = _dependenciesNode; + } + else + { + if (configuredProject.Services.ProjectSubscription is null) { - // Tree does not show any target nodes - targetRootNode = _dependenciesNode; + return null; } - else - { - if (configuredProject.Services.ProjectSubscription is null) - { - return null; - } - - IProjectSubscriptionUpdate subscriptionUpdate = (await configuredProject.Services.ProjectSubscription.ProjectRuleSource.GetLatestVersionAsync(configuredProject, cancellationToken: cancellationToken)).Value; - if (!subscriptionUpdate.CurrentState.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectRuleSnapshot configurationGeneralSnapshot) || - !configurationGeneralSnapshot.Properties.TryGetValue(ConfigurationGeneral.TargetFrameworkProperty, out string tf)) - { - return null; - } + IProjectSubscriptionUpdate subscriptionUpdate = (await configuredProject.Services.ProjectSubscription.ProjectRuleSource.GetLatestVersionAsync(configuredProject, cancellationToken: cancellationToken)).Value; - IProjectTree? targetNode = _dependenciesNode.FindChildWithFlags(ProjectTreeFlags.Create("$TFM:" + tf)); + if (!subscriptionUpdate.CurrentState.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectRuleSnapshot configurationGeneralSnapshot) || + !configurationGeneralSnapshot.Properties.TryGetValue(ConfigurationGeneral.TargetFrameworkProperty, out string tf)) + { + return null; + } - if (targetNode is null) - { - TraceUtilities.TraceError("Should not fail to find the target node."); - return null; - } + IProjectTree? targetNode = _dependenciesNode.FindChildWithFlags(ProjectTreeFlags.Create("$TFM:" + tf)); - targetRootNode = targetNode; + if (targetNode is null) + { + TraceUtilities.TraceError("Should not fail to find the target node."); + return null; } - return new DependenciesTreeConfiguredProjectSearchContext(_inner, targetRootNode, _hierarchyItemManager, _projectVsServices, _relationProvider); + targetRootNode = targetNode; } + + return new DependenciesTreeConfiguredProjectSearchContext(_inner, targetRootNode, _hierarchyItemManager, _projectVsServices, _relationProvider); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchContext.cs index cacab5d287..ed45b520e3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchContext.cs @@ -2,57 +2,56 @@ using Microsoft.Internal.VisualStudio.PlatformUI; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// A top-level search context, common to all projects and all +/// instances. +/// +internal sealed class DependenciesTreeSearchContext : IDisposable { - /// - /// A top-level search context, common to all projects and all - /// instances. - /// - internal sealed class DependenciesTreeSearchContext : IDisposable + private readonly string _searchString; + private readonly uint _maximumResults; + private readonly Action _resultAccumulator; + private readonly CancellationTokenSource _cts; + private long _submittedResultCount; // long as there's no interlocked increment for uint32 + + public DependenciesTreeSearchContext(IRelationshipSearchParameters parameters, Action resultAccumulator) { - private readonly string _searchString; - private readonly uint _maximumResults; - private readonly Action _resultAccumulator; - private readonly CancellationTokenSource _cts; - private long _submittedResultCount; // long as there's no interlocked increment for uint32 + _searchString = parameters.SearchQuery.SearchString; + _maximumResults = parameters.MaximumResults; + _resultAccumulator = resultAccumulator; + _cts = CancellationTokenSource.CreateLinkedTokenSource(parameters.CancellationToken); + } - public DependenciesTreeSearchContext(IRelationshipSearchParameters parameters, Action resultAccumulator) - { - _searchString = parameters.SearchQuery.SearchString; - _maximumResults = parameters.MaximumResults; - _resultAccumulator = resultAccumulator; - _cts = CancellationTokenSource.CreateLinkedTokenSource(parameters.CancellationToken); - } + public CancellationToken CancellationToken => _cts.Token; - public CancellationToken CancellationToken => _cts.Token; + public bool IsMatch(string candidateText) => candidateText.IndexOf(_searchString, StringComparisons.UserEnteredSearchTermIgnoreCase) != -1; - public bool IsMatch(string candidateText) => candidateText.IndexOf(_searchString, StringComparisons.UserEnteredSearchTermIgnoreCase) != -1; + public void SubmitResult(IRelatableItem? item) + { + if (item is null || CancellationToken.IsCancellationRequested) + { + return; + } - public void SubmitResult(IRelatableItem? item) + if (Interlocked.Increment(ref _submittedResultCount) >= _maximumResults) { - if (item is null || CancellationToken.IsCancellationRequested) - { - return; - } - - if (Interlocked.Increment(ref _submittedResultCount) >= _maximumResults) - { - _cts.Cancel(); - return; - } - - _resultAccumulator(new DependenciesSearchResult(item)); + _cts.Cancel(); + return; } - public void Dispose() => _cts.Dispose(); + _resultAccumulator(new DependenciesSearchResult(item)); + } - private sealed class DependenciesSearchResult : ISearchResult - { - private readonly object _item; + public void Dispose() => _cts.Dispose(); - public DependenciesSearchResult(object item) => _item = item; + private sealed class DependenciesSearchResult : ISearchResult + { + private readonly object _item; - public object GetDisplayItem() => _item; - } + public DependenciesSearchResult(object item) => _item = item; + + public object GetDisplayItem() => _item; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchProvider.cs index aa19693e97..838bc9b304 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/DependenciesTreeSearchProvider.cs @@ -7,105 +7,104 @@ using Flags = Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.DependencyTreeFlags; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Provides search results for non-top-level dependency tree nodes of all projects across the solution. +/// +/// +/// +/// Delegates solution explorer search for dependencies tree items to implementations. +/// +/// +/// Because we only materialize dependencies tree items when needed, we cannot assume here that search results +/// coincide with existing tree items. Instead, for each match we find we create a new tree item. The tree then +/// calls back via the 'contained by' relationship to determine parents, repeating the process for all ancestors +/// until a known item is returned to which the lineage can be attached. For our purposes, these known items are +/// the instances of top-level project dependencies. +/// +/// +/// Identifying parent hierarchy items requires a bunch of extra context (tree items, hierarchy item manager) +/// which we don't want to store on every item in order to make it available for relations. All search items and +/// their ancestors will definitely be expanded in the view, so we can pre-populate their parent collections at +/// the time they're created. This allows passing the necessary context into the construction process rather than +/// storing it on items. +/// +/// +/// We must also de-duplicate parent items. If two nodes have a common ancestor, that ancestor must be a single +/// object, rather than one-per-descendant. This is not required when computing 'contains' relationships. +/// Again, this means more context specific to the construction of search results. +/// +/// +/// Search results for top-level dependencies occurs via the hierarchy, so those items need not be included here. +/// +/// +[AppliesToProject(ProjectCapability.DependenciesTree)] +[Export(typeof(ISearchProvider))] +[Name("DependenciesTreeSearchProvider")] +[VisualStudio.Utilities.Order(Before = "GraphSearchProvider")] +internal sealed class DependenciesTreeSearchProvider : ISearchProvider { - /// - /// Provides search results for non-top-level dependency tree nodes of all projects across the solution. - /// - /// - /// - /// Delegates solution explorer search for dependencies tree items to implementations. - /// - /// - /// Because we only materialize dependencies tree items when needed, we cannot assume here that search results - /// coincide with existing tree items. Instead, for each match we find we create a new tree item. The tree then - /// calls back via the 'contained by' relationship to determine parents, repeating the process for all ancestors - /// until a known item is returned to which the lineage can be attached. For our purposes, these known items are - /// the instances of top-level project dependencies. - /// - /// - /// Identifying parent hierarchy items requires a bunch of extra context (tree items, hierarchy item manager) - /// which we don't want to store on every item in order to make it available for relations. All search items and - /// their ancestors will definitely be expanded in the view, so we can pre-populate their parent collections at - /// the time they're created. This allows passing the necessary context into the construction process rather than - /// storing it on items. - /// - /// - /// We must also de-duplicate parent items. If two nodes have a common ancestor, that ancestor must be a single - /// object, rather than one-per-descendant. This is not required when computing 'contains' relationships. - /// Again, this means more context specific to the construction of search results. - /// - /// - /// Search results for top-level dependencies occurs via the hierarchy, so those items need not be included here. - /// - /// - [AppliesToProject(ProjectCapability.DependenciesTree)] - [Export(typeof(ISearchProvider))] - [Name("DependenciesTreeSearchProvider")] - [VisualStudio.Utilities.Order(Before = "GraphSearchProvider")] - internal sealed class DependenciesTreeSearchProvider : ISearchProvider + private readonly ImmutableArray _providers; + private readonly JoinableTaskContext _joinableTaskContext; + private readonly IVsHierarchyItemManager _hierarchyItemManager; + private readonly IProjectServiceAccessor _projectServiceAccessor; + private readonly IRelationProvider _relationProvider; + + [ImportingConstructor] + public DependenciesTreeSearchProvider( + [ImportMany] IEnumerable providers, + JoinableTaskContext joinableTaskContext, + IVsHierarchyItemManager hierarchyItemManager, + IProjectServiceAccessor projectServiceAccessor, + IRelationProvider relationProvider) + { + _providers = providers.ToImmutableArray(); + _joinableTaskContext = joinableTaskContext; + _hierarchyItemManager = hierarchyItemManager; + _projectServiceAccessor = projectServiceAccessor; + _relationProvider = relationProvider; + } + + public void Search(IRelationshipSearchParameters parameters, Action resultAccumulator) { - private readonly ImmutableArray _providers; - private readonly JoinableTaskContext _joinableTaskContext; - private readonly IVsHierarchyItemManager _hierarchyItemManager; - private readonly IProjectServiceAccessor _projectServiceAccessor; - private readonly IRelationProvider _relationProvider; + Requires.NotNull(parameters); + Requires.NotNull(resultAccumulator); - [ImportingConstructor] - public DependenciesTreeSearchProvider( - [ImportMany] IEnumerable providers, - JoinableTaskContext joinableTaskContext, - IVsHierarchyItemManager hierarchyItemManager, - IProjectServiceAccessor projectServiceAccessor, - IRelationProvider relationProvider) + if (_providers.IsEmpty) { - _providers = providers.ToImmutableArray(); - _joinableTaskContext = joinableTaskContext; - _hierarchyItemManager = hierarchyItemManager; - _projectServiceAccessor = projectServiceAccessor; - _relationProvider = relationProvider; + // No providers registered + return; } - public void Search(IRelationshipSearchParameters parameters, Action resultAccumulator) + if (!parameters.Options.SearchExternalItems) { - Requires.NotNull(parameters); - Requires.NotNull(resultAccumulator); + // Consider the dependencies tree as containing 'external items', allowing the + // tree to be excluded from search results via this option. + return; + } - if (_providers.IsEmpty) - { - // No providers registered - return; - } + using var context = new DependenciesTreeSearchContext(parameters, resultAccumulator); - if (!parameters.Options.SearchExternalItems) - { - // Consider the dependencies tree as containing 'external items', allowing the - // tree to be excluded from search results via this option. - return; - } - - using var context = new DependenciesTreeSearchContext(parameters, resultAccumulator); + _joinableTaskContext.Factory.Run(SearchSolutionAsync); - _joinableTaskContext.Factory.Run(SearchSolutionAsync); + Task SearchSolutionAsync() + { + // Search projects concurrently + return Task.WhenAll(_projectServiceAccessor.GetProjectService().LoadedUnconfiguredProjects.Select(SearchProjectAsync)); + } - Task SearchSolutionAsync() - { - // Search projects concurrently - return Task.WhenAll(_projectServiceAccessor.GetProjectService().LoadedUnconfiguredProjects.Select(SearchProjectAsync)); - } + async Task SearchProjectAsync(UnconfiguredProject unconfiguredProject) + { + IUnconfiguredProjectVsServices? projectVsServices = unconfiguredProject.Services.ExportProvider.GetExportedValue(); + IProjectTree? dependenciesNode = projectVsServices?.ProjectTree.CurrentTree?.FindChildWithFlags(Flags.DependenciesRootNode); - async Task SearchProjectAsync(UnconfiguredProject unconfiguredProject) + if (projectVsServices is not null && dependenciesNode is not null) { - IUnconfiguredProjectVsServices? projectVsServices = unconfiguredProject.Services.ExportProvider.GetExportedValue(); - IProjectTree? dependenciesNode = projectVsServices?.ProjectTree.CurrentTree?.FindChildWithFlags(Flags.DependenciesRootNode); - - if (projectVsServices is not null && dependenciesNode is not null) - { - var projectContext = new DependenciesTreeProjectSearchContext(context, unconfiguredProject, dependenciesNode, _hierarchyItemManager, projectVsServices, _relationProvider); + var projectContext = new DependenciesTreeProjectSearchContext(context, unconfiguredProject, dependenciesNode, _hierarchyItemManager, projectVsServices, _relationProvider); - // Search providers concurrently - await Task.WhenAll(_providers.Select(provider => provider.SearchAsync(projectContext))); - } + // Search providers concurrently + await Task.WhenAll(_providers.Select(provider => provider.SearchAsync(projectContext))); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IAggregateRelationCollection.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IAggregateRelationCollection.cs index 9158b8dbf9..e9e20d8800 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IAggregateRelationCollection.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IAggregateRelationCollection.cs @@ -4,49 +4,48 @@ using System.Windows.Data; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Defines a lazily constructed collection of items which are related to some other item. +/// This collection can identify whether it contains items before actually materializing them. +/// +/// +/// +/// Dependencies tree extenders should not need to implement this interface, instead: +/// +/// Use for children of a parent +/// Use for parents of a child +/// +/// +/// +/// This collection is "aggregate" in the sense that it contains a collection of items per +/// that applies to its source . +/// +/// +/// This interface extends which allows efficient use by . +/// +/// +public interface IAggregateRelationCollection : IList { /// - /// Defines a lazily constructed collection of items which are related to some other item. - /// This collection can identify whether it contains items before actually materializing them. + /// Raised whenever changes. /// /// - /// - /// Dependencies tree extenders should not need to implement this interface, instead: - /// - /// Use for children of a parent - /// Use for parents of a child - /// - /// - /// - /// This collection is "aggregate" in the sense that it contains a collection of items per - /// that applies to its source . - /// - /// - /// This interface extends which allows efficient use by . - /// + /// Used by to trigger changes to its + /// property. /// - public interface IAggregateRelationCollection : IList - { - /// - /// Raised whenever changes. - /// - /// - /// Used by to trigger changes to its - /// property. - /// - event EventHandler HasItemsChanged; + event EventHandler HasItemsChanged; - /// - /// Gets whether this collection contains items. The return value can be computed without materializing items. - /// This allows expansion indicators to be displayed correctly without actually instantiating items. - /// - bool HasItems { get; } + /// + /// Gets whether this collection contains items. The return value can be computed without materializing items. + /// This allows expansion indicators to be displayed correctly without actually instantiating items. + /// + bool HasItems { get; } - /// - /// Materializes the items of this collection. If items have already been materialized, - /// this method returns immediately. - /// - void EnsureMaterialized(); - } + /// + /// Materializes the items of this collection. If items have already been materialized, + /// this method returns immediately. + /// + void EnsureMaterialized(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeConfiguredProjectSearchContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeConfiguredProjectSearchContext.cs index cc1571ca8d..369a45f412 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeConfiguredProjectSearchContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeConfiguredProjectSearchContext.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Provides services for a specific project configuration in a given search operation. +/// +/// +/// Instances of this type are obtained via . +/// +public interface IDependenciesTreeConfiguredProjectSearchContext { /// - /// Provides services for a specific project configuration in a given search operation. + /// Gets a token that signals cancellation of the ongoing search operation. /// - /// - /// Instances of this type are obtained via . - /// - public interface IDependenciesTreeConfiguredProjectSearchContext - { - /// - /// Gets a token that signals cancellation of the ongoing search operation. - /// - CancellationToken CancellationToken { get; } + CancellationToken CancellationToken { get; } - /// - /// Gets whether matches the user's search. - /// - bool IsMatch(string candidateText); + /// + /// Gets whether matches the user's search. + /// + bool IsMatch(string candidateText); - /// - /// Submits as a search result. - /// - /// - /// The result is not submitted if: - /// - /// is - /// has been cancelled - /// the maximum number of items have already been returned, in which case is cancelled - /// - /// - void SubmitResult(IRelatableItem? item); - } + /// + /// Submits as a search result. + /// + /// + /// The result is not submitted if: + /// + /// is + /// has been cancelled + /// the maximum number of items have already been returned, in which case is cancelled + /// + /// + void SubmitResult(IRelatableItem? item); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeProjectSearchContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeProjectSearchContext.cs index 618ebd0983..4ca9dca040 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeProjectSearchContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeProjectSearchContext.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Provides services throughout the lifetime of a search operation. +/// +public interface IDependenciesTreeProjectSearchContext { /// - /// Provides services throughout the lifetime of a search operation. + /// Gets a token that signals cancellation of the ongoing search operation. /// - public interface IDependenciesTreeProjectSearchContext - { - /// - /// Gets a token that signals cancellation of the ongoing search operation. - /// - CancellationToken CancellationToken { get; } + CancellationToken CancellationToken { get; } - /// - /// Gets the unconfigured project being searched. - /// - UnconfiguredProject UnconfiguredProject { get; } + /// + /// Gets the unconfigured project being searched. + /// + UnconfiguredProject UnconfiguredProject { get; } - /// - /// Gets a sub-context specific to a given project configuration. - /// - /// The configured project being searched. - /// Allows cancellation of the operation. - /// The sub-context for the configuration, or if not found. - Task ForConfiguredProjectAsync( - ConfiguredProject configuredProject, - CancellationToken cancellationToken = default); - } + /// + /// Gets a sub-context specific to a given project configuration. + /// + /// The configured project being searched. + /// Allows cancellation of the operation. + /// The sub-context for the configuration, or if not found. + Task ForConfiguredProjectAsync( + ConfiguredProject configuredProject, + CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeSearchProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeSearchProvider.cs index bf61ec7564..39806eecc0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeSearchProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IDependenciesTreeSearchProvider.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Extension point via which dependencies tree providers may implement search across their trees. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IDependenciesTreeSearchProvider { /// - /// Extension point via which dependencies tree providers may implement search across their trees. + /// Performs an asynchronous search across the dependencies tree provider's internal data for a specific unconfigured project. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IDependenciesTreeSearchProvider - { - /// - /// Performs an asynchronous search across the dependencies tree provider's internal data for a specific unconfigured project. - /// - /// - /// Implementations should check regularly for cancellation. - /// - /// The context via which search text may be matched and results may be returned. - Task SearchAsync(IDependenciesTreeProjectSearchContext searchContext); - } + /// + /// Implementations should check regularly for cancellation. + /// + /// The context via which search text may be matched and results may be returned. + Task SearchAsync(IDependenciesTreeProjectSearchContext searchContext); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelatableItem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelatableItem.cs index 94b57724e6..8c4ced60cd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelatableItem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelatableItem.cs @@ -2,85 +2,84 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Defines an item in the dependencies tree which has relationships with parent/child items, via which the +/// tree may be lazily constructed. +/// +/// +/// +/// Implementors will usually want to derive from rather than implementing +/// this interface directly. +/// +/// +/// Enough state must be available on items for their s to create parent and child items. +/// +/// +public interface IRelatableItem { /// - /// Defines an item in the dependencies tree which has relationships with parent/child items, via which the - /// tree may be lazily constructed. + /// Gets the "contains" collection for this item, if materialized. + /// + /// + /// This collection holds child items and is lazily constructed by , + /// and lazily populated via . + /// + AggregateContainsRelationCollection? ContainsCollection { get; } + + /// + /// Gets and sets the "contained by" collection for this item, if it exists. /// /// /// - /// Implementors will usually want to derive from rather than implementing - /// this interface directly. + /// This collection holds parent items and is only constructed for items which appear as part of search results. + /// This includes both items that directly match the search and their ancestors. /// /// - /// Enough state must be available on items for their s to create parent and child items. + /// This collection is set during search. Implementations only need to provide storage for this field, with no + /// logic in the getter or setter. /// /// - public interface IRelatableItem - { - /// - /// Gets the "contains" collection for this item, if materialized. - /// - /// - /// This collection holds child items and is lazily constructed by , - /// and lazily populated via . - /// - AggregateContainsRelationCollection? ContainsCollection { get; } - - /// - /// Gets and sets the "contained by" collection for this item, if it exists. - /// - /// - /// - /// This collection holds parent items and is only constructed for items which appear as part of search results. - /// This includes both items that directly match the search and their ancestors. - /// - /// - /// This collection is set during search. Implementations only need to provide storage for this field, with no - /// logic in the getter or setter. - /// - /// - AggregateContainedByRelationCollection? ContainedByCollection { get; set; } + AggregateContainedByRelationCollection? ContainedByCollection { get; set; } - /// - /// Gets a value that uniquely identifies amongst other items of the same type within the project's target framework. - /// - /// - /// - /// This value is used to deduplicate items when building ancestral chains of search results. The value may - /// be anything, so long as two equivalent items have the same value (hash code and equality), and two - /// different items do not. - /// - /// - /// The item's type and target are implicitly part of this identity, so do not need to be included by implementations. - /// - /// - object Identity { get; } + /// + /// Gets a value that uniquely identifies amongst other items of the same type within the project's target framework. + /// + /// + /// + /// This value is used to deduplicate items when building ancestral chains of search results. The value may + /// be anything, so long as two equivalent items have the same value (hash code and equality), and two + /// different items do not. + /// + /// + /// The item's type and target are implicitly part of this identity, so do not need to be included by implementations. + /// + /// + object Identity { get; } - /// - /// Gets the "contains" collection for this item, creating it if necessary. Creation will fail if there - /// are no relations registered for this item type, in which case there is no need to allocate a collection. - /// - bool TryGetOrCreateContainsCollection( - IRelationProvider relationProvider, - [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection? relationCollection); + /// + /// Gets the "contains" collection for this item, creating it if necessary. Creation will fail if there + /// are no relations registered for this item type, in which case there is no need to allocate a collection. + /// + bool TryGetOrCreateContainsCollection( + IRelationProvider relationProvider, + [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection? relationCollection); - /// - /// Attempts to find the item that corresponds to the top-level dependency modeled by this item. - /// - /// - /// - /// This method should only be implemented for item types that correspond to top-level project dependencies. All other item - /// types may . - /// - /// - /// This value is used during search to connection a search result's ancestry to existing tree items. - /// - /// - bool TryGetProjectNode( - IProjectTree targetRootNode, - IRelatableItem item, - [NotNullWhen(returnValue: true)] out IProjectTree? projectTree); - } + /// + /// Attempts to find the item that corresponds to the top-level dependency modeled by this item. + /// + /// + /// + /// This method should only be implemented for item types that correspond to top-level project dependencies. All other item + /// types may . + /// + /// + /// This value is used during search to connection a search result's ancestry to existing tree items. + /// + /// + bool TryGetProjectNode( + IProjectTree targetRootNode, + IRelatableItem item, + [NotNullWhen(returnValue: true)] out IProjectTree? projectTree); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelation.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelation.cs index 25efae6dfb..95bacd540a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelation.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelation.cs @@ -1,46 +1,45 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Models a bidirectional relationship between a parent and child items, +/// where the parent may contain children, and a child is contained by parents. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IRelation { /// - /// Models a bidirectional relationship between a parent and child items, - /// where the parent may contain children, and a child is contained by parents. + /// Determines whether this relation can produce contained (child) items for . /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IRelation - { - /// - /// Determines whether this relation can produce contained (child) items for . - /// - bool SupportsContainsFor(Type parentType); + bool SupportsContainsFor(Type parentType); - /// - /// Determines whether this relation can produce containing (parent) items for . - /// - bool SupportsContainedByFor(Type childType); + /// + /// Determines whether this relation can produce containing (parent) items for . + /// + bool SupportsContainedByFor(Type childType); - /// - /// Gets whether this relation will produce at least one child item for . - /// - bool HasContainedItem(IRelatableItem parent); + /// + /// Gets whether this relation will produce at least one child item for . + /// + bool HasContainedItem(IRelatableItem parent); - /// - /// Updates the child items of in . - /// - /// - /// Implementations should call - /// and provide callbacks specific to the particular relation. - /// - void UpdateContainsCollection(IRelatableItem parent, AggregateContainsRelationCollectionSpan span); + /// + /// Updates the child items of in . + /// + /// + /// Implementations should call + /// and provide callbacks specific to the particular relation. + /// + void UpdateContainsCollection(IRelatableItem parent, AggregateContainsRelationCollectionSpan span); - /// - /// Creates the set of parent items that contains . - /// - /// - /// Parent collections are only used in search. Search does not support dynamically updating results - /// in the tree, so unlike , implementations of this method are - /// only concerned with the one-time construction of parent items. - /// - IEnumerable? CreateContainedByItems(IRelatableItem child); - } + /// + /// Creates the set of parent items that contains . + /// + /// + /// Parent collections are only used in search. Search does not support dynamically updating results + /// in the tree, so unlike , implementations of this method are + /// only concerned with the one-time construction of parent items. + /// + IEnumerable? CreateContainedByItems(IRelatableItem child); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelationProvider.cs index 1f15afe43c..2feda611b0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IRelationProvider.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Provides the set of exported instances that apply to given types, +/// both for contains and contained-by relationships. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.ExactlyOne)] +public interface IRelationProvider { /// - /// Provides the set of exported instances that apply to given types, - /// both for contains and contained-by relationships. + /// Gets the set of instances that can produce child (contained) items for parent items of type . + /// If no relations are found, is returned. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.System, Cardinality = ImportCardinality.ExactlyOne)] - public interface IRelationProvider - { - /// - /// Gets the set of instances that can produce child (contained) items for parent items of type . - /// If no relations are found, is returned. - /// - ImmutableArray GetContainsRelationsFor(Type parentType); + ImmutableArray GetContainsRelationsFor(Type parentType); - /// - /// Gets the set of instances that can produce parent (contained by) items for child items of type . - /// If no relations are found, is returned. - /// - ImmutableArray GetContainedByRelationsFor(Type childType); - } + /// + /// Gets the set of instances that can produce parent (contained by) items for child items of type . + /// If no relations are found, is returned. + /// + ImmutableArray GetContainedByRelationsFor(Type childType); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IVsHierarchyItemExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IVsHierarchyItemExtensions.cs index 31d3849934..dbcbf64dde 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IVsHierarchyItemExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/IVsHierarchyItemExtensions.cs @@ -6,77 +6,76 @@ using Flags = Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.DependencyTreeFlags; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Extension methods for that support attached collections. +/// +internal static class IVsHierarchyItemExtensions { /// - /// Extension methods for that support attached collections. + /// Detects the target configuration dimension associated with a given hierarchy item in the dependencies tree, if + /// nested within a target group node. For projects that do not multi-target, this will always return . + /// This method searches ancestors until a target is found, or the project root is found. /// - internal static class IVsHierarchyItemExtensions + /// The item to test. + /// The detected target, if found. + /// if the target was found, otherwise . + public static bool TryFindTarget(this IVsHierarchyItem item, [NotNullWhen(returnValue: true)] out string? target) { - /// - /// Detects the target configuration dimension associated with a given hierarchy item in the dependencies tree, if - /// nested within a target group node. For projects that do not multi-target, this will always return . - /// This method searches ancestors until a target is found, or the project root is found. - /// - /// The item to test. - /// The detected target, if found. - /// if the target was found, otherwise . - public static bool TryFindTarget(this IVsHierarchyItem item, [NotNullWhen(returnValue: true)] out string? target) + // Check this hierarchy item, then walk upwards through its ancestry. + for (; item is not null; item = item.Parent) { - // Check this hierarchy item, then walk upwards through its ancestry. - for (; item is not null; item = item.Parent) + if (item.TryGetFlags(out ProjectTreeFlags flags) && flags.Contains(Flags.TargetNode)) { - if (item.TryGetFlags(out ProjectTreeFlags flags) && flags.Contains(Flags.TargetNode)) - { - // Found an ancestor target node. - const string prefix = "$TFM:"; - string? flag = flags.FirstOrDefault(f => f.StartsWith(prefix)); - - if (flag is not null) - { - target = flag.Substring(prefix.Length); - return true; - } + // Found an ancestor target node. + const string prefix = "$TFM:"; + string? flag = flags.FirstOrDefault(f => f.StartsWith(prefix)); - // Target node didn't have a TFM flag for some reason. This is unexpected, but there's not much - // we can do about it. Don't check any more ancestors, as there should only be one relevant target node. - break; + if (flag is not null) + { + target = flag.Substring(prefix.Length); + return true; } - } - target = null; - return false; + // Target node didn't have a TFM flag for some reason. This is unexpected, but there's not much + // we can do about it. Don't check any more ancestors, as there should only be one relevant target node. + break; + } } - public static bool TryGetFlags(this IVsHierarchyItem item, out ProjectTreeFlags flags) - { - uint itemId = item.HierarchyIdentity.ItemID; + target = null; + return false; + } - // Skip the root as we can't pass that ID to the CPS API. - if (itemId != (uint)VSConstants.VSITEMID.Root) + public static bool TryGetFlags(this IVsHierarchyItem item, out ProjectTreeFlags flags) + { + uint itemId = item.HierarchyIdentity.ItemID; + + // Skip the root as we can't pass that ID to the CPS API. + if (itemId != (uint)VSConstants.VSITEMID.Root) + { + // Browse objects are created lazily, and we want to avoid creating them when possible. + // This method is typically invoked for every hierarchy item in the tree, via Solution Explorer APIs. + // Rather than create a browse object for every node, we find the project root node and use that. + // In this way, we only ever create one browse object per project. + IVsHierarchyItem root = item; + while (!root.HierarchyIdentity.IsRoot) { - // Browse objects are created lazily, and we want to avoid creating them when possible. - // This method is typically invoked for every hierarchy item in the tree, via Solution Explorer APIs. - // Rather than create a browse object for every node, we find the project root node and use that. - // In this way, we only ever create one browse object per project. - IVsHierarchyItem root = item; - while (!root.HierarchyIdentity.IsRoot) - { - root = root.Parent; - } + root = root.Parent; + } - if (root.HierarchyIdentity.NestedHierarchy is IVsBrowseObjectContext { UnconfiguredProject.Services.ProjectTreeService.CurrentTree.Tree: { } tree }) + if (root.HierarchyIdentity.NestedHierarchy is IVsBrowseObjectContext { UnconfiguredProject.Services.ProjectTreeService.CurrentTree.Tree: { } tree }) + { + if (tree.TryFind((IntPtr)itemId, out IProjectTree? subtree)) { - if (tree.TryFind((IntPtr)itemId, out IProjectTree? subtree)) - { - flags = subtree.Flags; - return true; - } + flags = subtree.Flags; + return true; } } - - flags = default; - return false; } + + flags = default; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDescriptionAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDescriptionAttribute.cs index d984424242..8c574794df 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDescriptionAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDescriptionAttribute.cs @@ -3,32 +3,31 @@ using System.ComponentModel; using System.Globalization; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +/// +/// Specifies a localized description for a property or event. +/// +[AttributeUsage(AttributeTargets.Property)] +internal sealed class BrowseObjectDescriptionAttribute : DescriptionAttribute { - /// - /// Specifies a localized description for a property or event. - /// - [AttributeUsage(AttributeTargets.Property)] - internal sealed class BrowseObjectDescriptionAttribute : DescriptionAttribute - { - private readonly string _key; + private readonly string _key; - public BrowseObjectDescriptionAttribute(string key) => _key = key; + public BrowseObjectDescriptionAttribute(string key) => _key = key; - public override string Description + public override string Description + { + get { - get - { - // Defer lookup and cache in base class's DescriptionValue field - string name = base.Description; + // Defer lookup and cache in base class's DescriptionValue field + string name = base.Description; - if (name.Length == 0) - { - name = DescriptionValue = VSResources.ResourceManager.GetString(_key, CultureInfo.CurrentUICulture); - } - - return name; + if (name.Length == 0) + { + name = DescriptionValue = VSResources.ResourceManager.GetString(_key, CultureInfo.CurrentUICulture); } + + return name; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDisplayNameAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDisplayNameAttribute.cs index 71bcf5d67b..5db60b02cb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDisplayNameAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/BrowseObjectDisplayNameAttribute.cs @@ -3,32 +3,31 @@ using System.ComponentModel; using System.Globalization; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +/// +/// Specifies the localized display name for a property, event or public void method which takes no arguments. +/// +[AttributeUsage(AttributeTargets.Property)] +internal sealed class BrowseObjectDisplayNameAttribute : DisplayNameAttribute { - /// - /// Specifies the localized display name for a property, event or public void method which takes no arguments. - /// - [AttributeUsage(AttributeTargets.Property)] - internal sealed class BrowseObjectDisplayNameAttribute : DisplayNameAttribute - { - private readonly string _key; + private readonly string _key; - public BrowseObjectDisplayNameAttribute(string key) => _key = key; + public BrowseObjectDisplayNameAttribute(string key) => _key = key; - public override string DisplayName + public override string DisplayName + { + get { - get - { - // Defer lookup and cache in base class's DisplayNameValue field - string name = base.DisplayName; + // Defer lookup and cache in base class's DisplayNameValue field + string name = base.DisplayName; - if (name.Length == 0) - { - name = DisplayNameValue = VSResources.ResourceManager.GetString(_key, CultureInfo.CurrentUICulture); - } - - return name; + if (name.Length == 0) + { + name = DisplayNameValue = VSResources.ResourceManager.GetString(_key, CultureInfo.CurrentUICulture); } + + return name; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyAttachedCollectionSourceProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyAttachedCollectionSourceProvider.cs index a475e44035..9cdcb3afdd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyAttachedCollectionSourceProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyAttachedCollectionSourceProvider.cs @@ -9,52 +9,51 @@ using Flags = Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.DependencyTreeFlags; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +[AppliesToProject(ProjectCapability.DependenciesTree)] +[Export(typeof(IAttachedCollectionSourceProvider))] +[Name(nameof(FrameworkReferenceAssemblyAttachedCollectionSourceProvider))] +[VisualStudio.Utilities.Order(Before = HierarchyItemsProviderNames.Contains)] +internal sealed class FrameworkReferenceAssemblyAttachedCollectionSourceProvider : DependenciesAttachedCollectionSourceProviderBase { - [AppliesToProject(ProjectCapability.DependenciesTree)] - [Export(typeof(IAttachedCollectionSourceProvider))] - [Name(nameof(FrameworkReferenceAssemblyAttachedCollectionSourceProvider))] - [VisualStudio.Utilities.Order(Before = HierarchyItemsProviderNames.Contains)] - internal sealed class FrameworkReferenceAssemblyAttachedCollectionSourceProvider : DependenciesAttachedCollectionSourceProviderBase + [ImportingConstructor] + public FrameworkReferenceAssemblyAttachedCollectionSourceProvider() + : base(Flags.FrameworkDependency) { - [ImportingConstructor] - public FrameworkReferenceAssemblyAttachedCollectionSourceProvider() - : base(Flags.FrameworkDependency) - { - } + } - protected override bool TryCreateCollectionSource( - IVsHierarchyItem hierarchyItem, - string flagsString, - string? target, - IRelationProvider relationProvider, - [NotNullWhen(returnValue: true)] out AggregateRelationCollectionSource? containsCollectionSource) + protected override bool TryCreateCollectionSource( + IVsHierarchyItem hierarchyItem, + string flagsString, + string? target, + IRelationProvider relationProvider, + [NotNullWhen(returnValue: true)] out AggregateRelationCollectionSource? containsCollectionSource) + { + if (ErrorHandler.Succeeded(hierarchyItem.HierarchyIdentity.Hierarchy.GetProperty( + hierarchyItem.HierarchyIdentity.ItemID, (int)__VSHPROPID.VSHPROPID_ExtObject, out object projectItemObject))) { - if (ErrorHandler.Succeeded(hierarchyItem.HierarchyIdentity.Hierarchy.GetProperty( - hierarchyItem.HierarchyIdentity.ItemID, (int)__VSHPROPID.VSHPROPID_ExtObject, out object projectItemObject))) + var projectItem = projectItemObject as ProjectItem; + EnvDTE.Properties? props = projectItem?.Properties; + + if (props?.Item("TargetingPackPath")?.Value is string path && + props?.Item("OriginalItemSpec")?.Value is string name && + !string.IsNullOrWhiteSpace(path) && + !string.IsNullOrWhiteSpace(name)) { - var projectItem = projectItemObject as ProjectItem; - EnvDTE.Properties? props = projectItem?.Properties; + string? profile = props?.Item("Profile").Value as string; - if (props?.Item("TargetingPackPath")?.Value is string path && - props?.Item("OriginalItemSpec")?.Value is string name && - !string.IsNullOrWhiteSpace(path) && - !string.IsNullOrWhiteSpace(name)) + var framework = new FrameworkReferenceIdentity(path, profile, name); + var item = new FrameworkReferenceItem(framework); + if (AggregateContainsRelationCollection.TryCreate(item, relationProvider, out AggregateContainsRelationCollection? collection)) { - string? profile = props?.Item("Profile").Value as string; - - var framework = new FrameworkReferenceIdentity(path, profile, name); - var item = new FrameworkReferenceItem(framework); - if (AggregateContainsRelationCollection.TryCreate(item, relationProvider, out AggregateContainsRelationCollection? collection)) - { - containsCollectionSource = new AggregateRelationCollectionSource(hierarchyItem, collection); - return true; - } + containsCollectionSource = new AggregateRelationCollectionSource(hierarchyItem, collection); + return true; } } - - containsCollectionSource = null; - return false; } + + containsCollectionSource = null; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyItem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyItem.cs index b94e7cd1d7..ec724f7a0e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyItem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceAssemblyItem.cs @@ -5,66 +5,65 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +internal sealed class FrameworkReferenceAssemblyItem : RelatableItemBase, IObjectBrowserItem { - internal sealed class FrameworkReferenceAssemblyItem : RelatableItemBase, IObjectBrowserItem - { - private const int IDM_VS_CTXT_TRANSITIVE_ASSEMBLY_REFERENCE = 0x04B1; + private const int IDM_VS_CTXT_TRANSITIVE_ASSEMBLY_REFERENCE = 0x04B1; - private static readonly IContextMenuController s_defaultMenuController = CreateContextMenuController(VsMenus.guidSHLMainMenu, IDM_VS_CTXT_TRANSITIVE_ASSEMBLY_REFERENCE); + private static readonly IContextMenuController s_defaultMenuController = CreateContextMenuController(VsMenus.guidSHLMainMenu, IDM_VS_CTXT_TRANSITIVE_ASSEMBLY_REFERENCE); - public string AssemblyName { get; } - public string? Path { get; } - public string? AssemblyVersion { get; } - public string? FileVersion { get; } - public FrameworkReferenceIdentity Framework { get; } + public string AssemblyName { get; } + public string? Path { get; } + public string? AssemblyVersion { get; } + public string? FileVersion { get; } + public FrameworkReferenceIdentity Framework { get; } - public FrameworkReferenceAssemblyItem(string assemblyName, string? path, string? assemblyVersion, string? fileVersion, FrameworkReferenceIdentity framework) - : base(assemblyName) - { - Requires.NotNull(framework); - AssemblyName = assemblyName; - Path = path; - AssemblyVersion = assemblyVersion; - FileVersion = fileVersion; - Framework = framework; - } + public FrameworkReferenceAssemblyItem(string assemblyName, string? path, string? assemblyVersion, string? fileVersion, FrameworkReferenceIdentity framework) + : base(assemblyName) + { + Requires.NotNull(framework); + AssemblyName = assemblyName; + Path = path; + AssemblyVersion = assemblyVersion; + FileVersion = fileVersion; + Framework = framework; + } - public override object Identity => Text; - public override int Priority => 0; - public override ImageMoniker IconMoniker => KnownMonikers.ReferencePrivate; + public override object Identity => Text; + public override int Priority => 0; + public override ImageMoniker IconMoniker => KnownMonikers.ReferencePrivate; - protected override IContextMenuController? ContextMenuController => s_defaultMenuController; + protected override IContextMenuController? ContextMenuController => s_defaultMenuController; - public override object? GetBrowseObject() => new BrowseObject(this); + public override object? GetBrowseObject() => new BrowseObject(this); - string? IObjectBrowserItem.AssemblyPath => GetAssemblyPath(); + string? IObjectBrowserItem.AssemblyPath => GetAssemblyPath(); - private string? GetAssemblyPath() => Path is not null - ? System.IO.Path.GetFullPath(System.IO.Path.Combine(Framework.Path, Path)) - : null; + private string? GetAssemblyPath() => Path is not null + ? System.IO.Path.GetFullPath(System.IO.Path.Combine(Framework.Path, Path)) + : null; - private sealed class BrowseObject(FrameworkReferenceAssemblyItem item) : LocalizableProperties - { - public override string GetComponentName() => item.AssemblyName; + private sealed class BrowseObject(FrameworkReferenceAssemblyItem item) : LocalizableProperties + { + public override string GetComponentName() => item.AssemblyName; - public override string GetClassName() => VSResources.FrameworkAssemblyBrowseObjectClassName; + public override string GetClassName() => VSResources.FrameworkAssemblyBrowseObjectClassName; - [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyAssemblyNameDisplayName))] - [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyAssemblyNameDescription))] - public string AssemblyName => item.Text; + [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyAssemblyNameDisplayName))] + [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyAssemblyNameDescription))] + public string AssemblyName => item.Text; - [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyPathDisplayName))] - [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyPathDescription))] - public string Path => item.GetAssemblyPath() ?? ""; + [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyPathDisplayName))] + [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyPathDescription))] + public string Path => item.GetAssemblyPath() ?? ""; - [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyAssemblyVersionDisplayName))] - [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyAssemblyVersionDescription))] - public string AssemblyVersion => item.AssemblyVersion ?? ""; + [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyAssemblyVersionDisplayName))] + [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyAssemblyVersionDescription))] + public string AssemblyVersion => item.AssemblyVersion ?? ""; - [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyFileVersionDisplayName))] - [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyFileVersionDescription))] - public string FileVersion => item.FileVersion ?? ""; - } + [BrowseObjectDisplayName(nameof(VSResources.FrameworkAssemblyFileVersionDisplayName))] + [BrowseObjectDescription(nameof(VSResources.FrameworkAssemblyFileVersionDescription))] + public string FileVersion => item.FileVersion ?? ""; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceIdentity.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceIdentity.cs index be20af3c61..890858fe24 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceIdentity.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceIdentity.cs @@ -1,39 +1,38 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +internal sealed class FrameworkReferenceIdentity { - internal sealed class FrameworkReferenceIdentity - { - public string Path { get; } - public string? Profile { get; } - public string Name { get; } + public string Path { get; } + public string? Profile { get; } + public string Name { get; } - public FrameworkReferenceIdentity(string path, string? profile, string name) - { - Requires.NotNullOrWhiteSpace(path); - Requires.NotNullOrWhiteSpace(name); + public FrameworkReferenceIdentity(string path, string? profile, string name) + { + Requires.NotNullOrWhiteSpace(path); + Requires.NotNullOrWhiteSpace(name); - Path = path; - Profile = profile; - Name = name; - } + Path = path; + Profile = profile; + Name = name; + } - private bool Equals(FrameworkReferenceIdentity other) - { - return Path == other.Path && Profile == other.Profile; - } + private bool Equals(FrameworkReferenceIdentity other) + { + return Path == other.Path && Profile == other.Profile; + } - public override bool Equals(object? obj) - { - return ReferenceEquals(this, obj) || (obj is FrameworkReferenceIdentity other && Equals(other)); - } + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || (obj is FrameworkReferenceIdentity other && Equals(other)); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return (Path.GetHashCode() * 397) ^ (Profile is not null ? Profile.GetHashCode() : 0); - } + return (Path.GetHashCode() * 397) ^ (Profile is not null ? Profile.GetHashCode() : 0); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceItem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceItem.cs index a215d8d9cd..96ca2f2d38 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceItem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkReferenceItem.cs @@ -4,26 +4,25 @@ using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +internal sealed class FrameworkReferenceItem : RelatableItemBase { - internal sealed class FrameworkReferenceItem : RelatableItemBase - { - public FrameworkReferenceIdentity Framework { get; } + public FrameworkReferenceIdentity Framework { get; } - public FrameworkReferenceItem(FrameworkReferenceIdentity framework) - : base(framework.Name) - { - Framework = framework; - } + public FrameworkReferenceItem(FrameworkReferenceIdentity framework) + : base(framework.Name) + { + Framework = framework; + } - public override object Identity => (Framework.Path, Framework.Profile); - public override int Priority => 0; - public override ImageMoniker IconMoniker => KnownMonikers.FrameworkPrivate; + public override object Identity => (Framework.Path, Framework.Profile); + public override int Priority => 0; + public override ImageMoniker IconMoniker => KnownMonikers.FrameworkPrivate; - protected override bool TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(true)] out IProjectTree? projectTree) - { - // TODO implement this to support searching framework reference assemblies - return base.TryGetProjectNode(targetRootNode, item, out projectTree); - } + protected override bool TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(true)] out IProjectTree? projectTree) + { + // TODO implement this to support searching framework reference assemblies + return base.TryGetProjectNode(targetRootNode, item, out projectTree); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkToFrameworkAssemblyRelation.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkToFrameworkAssemblyRelation.cs index 7407ae833c..615f244cb9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkToFrameworkAssemblyRelation.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/FrameworkToFrameworkAssemblyRelation.cs @@ -1,37 +1,36 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +[Export(typeof(IRelation))] +internal sealed class FrameworkToFrameworkAssemblyRelation : RelationBase { - [Export(typeof(IRelation))] - internal sealed class FrameworkToFrameworkAssemblyRelation : RelationBase - { - private readonly ITargetFrameworkContentCache _frameworkContentCache; + private readonly ITargetFrameworkContentCache _frameworkContentCache; - [ImportingConstructor] - public FrameworkToFrameworkAssemblyRelation(ITargetFrameworkContentCache frameworkContentCache) - { - _frameworkContentCache = frameworkContentCache; - } + [ImportingConstructor] + public FrameworkToFrameworkAssemblyRelation(ITargetFrameworkContentCache frameworkContentCache) + { + _frameworkContentCache = frameworkContentCache; + } - protected override bool HasContainedItems(FrameworkReferenceItem parent) - { - return !_frameworkContentCache.GetContents(parent.Framework).IsEmpty; - } + protected override bool HasContainedItems(FrameworkReferenceItem parent) + { + return !_frameworkContentCache.GetContents(parent.Framework).IsEmpty; + } - protected override void UpdateContainsCollection(FrameworkReferenceItem parent, AggregateContainsRelationCollectionSpan span) - { - ImmutableArray assemblies = _frameworkContentCache.GetContents(parent.Framework); + protected override void UpdateContainsCollection(FrameworkReferenceItem parent, AggregateContainsRelationCollectionSpan span) + { + ImmutableArray assemblies = _frameworkContentCache.GetContents(parent.Framework); - span.UpdateContainsItems( - assemblies.OrderBy(assembly => assembly.Text), - (sourceItem, targetItem) => StringComparer.Ordinal.Compare(sourceItem.Text, targetItem.Text), - (sourceItem, targetItem) => false, - sourceItem => sourceItem); - } + span.UpdateContainsItems( + assemblies.OrderBy(assembly => assembly.Text), + (sourceItem, targetItem) => StringComparer.Ordinal.Compare(sourceItem.Text, targetItem.Text), + (sourceItem, targetItem) => false, + sourceItem => sourceItem); + } - protected override IEnumerable? CreateContainedByItems(FrameworkReferenceAssemblyItem child) - { - yield return new FrameworkReferenceItem(child.Framework); - } + protected override IEnumerable? CreateContainedByItems(FrameworkReferenceAssemblyItem child) + { + yield return new FrameworkReferenceItem(child.Framework); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/ITargetFrameworkContentCache.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/ITargetFrameworkContentCache.cs index fb23cab4ac..edd85686a8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/ITargetFrameworkContentCache.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/ITargetFrameworkContentCache.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +/// +/// Global service that reads and caches the contents of framework references. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ExactlyOne)] +internal interface ITargetFrameworkContentCache { /// - /// Global service that reads and caches the contents of framework references. + /// Returns an array of framework reference assembly items, lazily loading them on the first invocation. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ExactlyOne)] - internal interface ITargetFrameworkContentCache - { - /// - /// Returns an array of framework reference assembly items, lazily loading them on the first invocation. - /// - ImmutableArray GetContents(FrameworkReferenceIdentity framework); - } + ImmutableArray GetContents(FrameworkReferenceIdentity framework); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/TargetFrameworkContentCache.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/TargetFrameworkContentCache.cs index 92460de06e..b11cd89e52 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/TargetFrameworkContentCache.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Implementation/TargetFrameworkContentCache.cs @@ -3,87 +3,86 @@ using System.Xml.Linq; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections.Implementation; + +[Export(typeof(ITargetFrameworkContentCache))] +internal sealed class TargetFrameworkContentCache : ITargetFrameworkContentCache { - [Export(typeof(ITargetFrameworkContentCache))] - internal sealed class TargetFrameworkContentCache : ITargetFrameworkContentCache + private ImmutableDictionary> _cache = ImmutableDictionary>.Empty; + + public ImmutableArray GetContents(FrameworkReferenceIdentity framework) { - private ImmutableDictionary> _cache = ImmutableDictionary>.Empty; + return ImmutableInterlocked.GetOrAdd(ref _cache, framework, LoadItems); - public ImmutableArray GetContents(FrameworkReferenceIdentity framework) + static ImmutableArray LoadItems(FrameworkReferenceIdentity framework) { - return ImmutableInterlocked.GetOrAdd(ref _cache, framework, LoadItems); + string frameworkListPath = Path.Combine(framework.Path, "data", "FrameworkList.xml"); + var pool = new Dictionary(StringComparer.Ordinal); - static ImmutableArray LoadItems(FrameworkReferenceIdentity framework) + XDocument doc; + try { - string frameworkListPath = Path.Combine(framework.Path, "data", "FrameworkList.xml"); - var pool = new Dictionary(StringComparer.Ordinal); - - XDocument doc; - try - { - doc = XDocument.Load(frameworkListPath); - } - catch - { - return ImmutableArray.Empty; - } + doc = XDocument.Load(frameworkListPath); + } + catch + { + return ImmutableArray.Empty; + } - ImmutableArray.Builder results = ImmutableArray.CreateBuilder(); + ImmutableArray.Builder results = ImmutableArray.CreateBuilder(); - foreach (XElement file in doc.Root.Elements("File")) + foreach (XElement file in doc.Root.Elements("File")) + { + if (!Strings.IsNullOrEmpty(framework.Profile)) { - if (!Strings.IsNullOrEmpty(framework.Profile)) + // We must filter to a specific profile + string? fileProfile = file.Attribute("Profile")?.Value; + + if (fileProfile is null) { - // We must filter to a specific profile - string? fileProfile = file.Attribute("Profile")?.Value; - - if (fileProfile is null) - { - // The file doesn't specify a profile, so skip it - continue; - } - - if (!new LazyStringSplit(fileProfile, ';').Contains(framework.Profile, StringComparer.OrdinalIgnoreCase)) - { - // File file specifies a profile, but not one we are looking for, so skip it - continue; - } + // The file doesn't specify a profile, so skip it + continue; } - if (file.Attribute("ReferencedByDefault")?.Value.Equals("false", StringComparison.OrdinalIgnoreCase) == true) + if (!new LazyStringSplit(fileProfile, ';').Contains(framework.Profile, StringComparer.OrdinalIgnoreCase)) { - // Don't include if ReferencedByDefault=false + // File file specifies a profile, but not one we are looking for, so skip it continue; } + } - string? assemblyName = Pool(file.Attribute("AssemblyName")?.Value); - string? path = Pool(file.Attribute("Path")?.Value); - string? assemblyVersion = Pool(file.Attribute("AssemblyVersion")?.Value); - string? fileVersion = Pool(file.Attribute("FileVersion")?.Value); + if (file.Attribute("ReferencedByDefault")?.Value.Equals("false", StringComparison.OrdinalIgnoreCase) == true) + { + // Don't include if ReferencedByDefault=false + continue; + } - if (assemblyName is not null) - { - results.Add(new FrameworkReferenceAssemblyItem(assemblyName, path, assemblyVersion, fileVersion, framework)); - } + string? assemblyName = Pool(file.Attribute("AssemblyName")?.Value); + string? path = Pool(file.Attribute("Path")?.Value); + string? assemblyVersion = Pool(file.Attribute("AssemblyVersion")?.Value); + string? fileVersion = Pool(file.Attribute("FileVersion")?.Value); + + if (assemblyName is not null) + { + results.Add(new FrameworkReferenceAssemblyItem(assemblyName, path, assemblyVersion, fileVersion, framework)); } + } - return results.ToImmutable(); + return results.ToImmutable(); - string? Pool(string? s) + string? Pool(string? s) + { + if (s is not null) { - if (s is not null) + if (pool.TryGetValue(s, out string existing)) { - if (pool.TryGetValue(s, out string existing)) - { - return existing; - } - - pool.Add(s, s); + return existing; } - return s; + pool.Add(s, s); } + + return s; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.DefaultContextMenuController.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.DefaultContextMenuController.cs index d7b2c8ab02..5c1de48891 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.DefaultContextMenuController.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.DefaultContextMenuController.cs @@ -5,82 +5,81 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +public abstract partial class RelatableItemBase { - public abstract partial class RelatableItemBase + /// + /// Creates a for use in overrides of . + /// + public static IContextMenuController CreateContextMenuController(Guid menuGuid, int menuId) => new MenuController(menuGuid, menuId); + + internal sealed class MenuController(Guid menuGuid, int menuId) : IContextMenuController { - /// - /// Creates a for use in overrides of . - /// - public static IContextMenuController CreateContextMenuController(Guid menuGuid, int menuId) => new MenuController(menuGuid, menuId); + public static ImmutableArray CurrentItems { get; private set; } = []; - internal sealed class MenuController(Guid menuGuid, int menuId) : IContextMenuController + public bool ShowContextMenu(IEnumerable items, Point location) { - public static ImmutableArray CurrentItems { get; private set; } = []; + ImmutableArray? relatableItems = GetItems(); - public bool ShowContextMenu(IEnumerable items, Point location) + if (relatableItems is null) { - ImmutableArray? relatableItems = GetItems(); + return false; + } - if (relatableItems is null) - { - return false; - } + CurrentItems = relatableItems.Value; - CurrentItems = relatableItems.Value; + try + { + return ShowContextMenu(); + } + finally + { + CurrentItems = []; + } - try - { - return ShowContextMenu(); - } - finally - { - CurrentItems = []; - } + ImmutableArray? GetItems() + { + ImmutableArray.Builder? builder = null; - ImmutableArray? GetItems() + foreach (object item in items) { - ImmutableArray.Builder? builder = null; - - foreach (object item in items) + if (item is IRelatableItem relatableItem) { - if (item is IRelatableItem relatableItem) - { - builder ??= ImmutableArray.CreateBuilder(); - builder.Add(relatableItem); - } - else - { - return null; - } + builder ??= ImmutableArray.CreateBuilder(); + builder.Add(relatableItem); } - - if (builder is null) + else { return null; } - - return builder.ToImmutable(); } - bool ShowContextMenu() + if (builder is null) { - if (Package.GetGlobalService(typeof(SVsUIShell)) is IVsUIShell shell) - { - Guid guidContextMenu = menuGuid; + return null; + } - int result = shell.ShowContextMenu( - dwCompRole: 0, - rclsidActive: ref guidContextMenu, - nMenuId: menuId, - pos: [new POINTS { x = (short)location.X, y = (short)location.Y }], - pCmdTrgtActive: null); + return builder.ToImmutable(); + } - return ErrorHandler.Succeeded(result); - } + bool ShowContextMenu() + { + if (Package.GetGlobalService(typeof(SVsUIShell)) is IVsUIShell shell) + { + Guid guidContextMenu = menuGuid; + + int result = shell.ShowContextMenu( + dwCompRole: 0, + rclsidActive: ref guidContextMenu, + nMenuId: menuId, + pos: [new POINTS { x = (short)location.X, y = (short)location.Y }], + pCmdTrgtActive: null); - return false; + return ErrorHandler.Succeeded(result); } + + return false; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.cs index e6739c5964..d152881e7d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelatableItemBase.cs @@ -4,56 +4,55 @@ using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Base class for implementations. Derives from +/// to include common patterns for attached items. +/// +public abstract partial class RelatableItemBase : AttachedCollectionItemBase, IRelatableItem { - /// - /// Base class for implementations. Derives from - /// to include common patterns for attached items. - /// - public abstract partial class RelatableItemBase : AttachedCollectionItemBase, IRelatableItem - { - private const int IDM_VS_CTXT_DEPENDENCY_TRANSITIVE_ITEM = 0x04B0; + private const int IDM_VS_CTXT_DEPENDENCY_TRANSITIVE_ITEM = 0x04B0; - private static readonly IContextMenuController s_defaultMenuController = CreateContextMenuController(VsMenus.guidSHLMainMenu, IDM_VS_CTXT_DEPENDENCY_TRANSITIVE_ITEM); + private static readonly IContextMenuController s_defaultMenuController = CreateContextMenuController(VsMenus.guidSHLMainMenu, IDM_VS_CTXT_DEPENDENCY_TRANSITIVE_ITEM); - private AggregateContainsRelationCollection? _containsCollection; + private AggregateContainsRelationCollection? _containsCollection; - protected RelatableItemBase(string name) - : base(name) - { - } + protected RelatableItemBase(string name) + : base(name) + { + } - public abstract object Identity { get; } + public abstract object Identity { get; } - protected override IContextMenuController? ContextMenuController => s_defaultMenuController; + protected override IContextMenuController? ContextMenuController => s_defaultMenuController; - AggregateContainsRelationCollection? IRelatableItem.ContainsCollection => _containsCollection; + AggregateContainsRelationCollection? IRelatableItem.ContainsCollection => _containsCollection; - AggregateContainedByRelationCollection? IRelatableItem.ContainedByCollection { get; set; } + AggregateContainedByRelationCollection? IRelatableItem.ContainedByCollection { get; set; } - bool IRelatableItem.TryGetOrCreateContainsCollection( - IRelationProvider relationProvider, - [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection? relationCollection) + bool IRelatableItem.TryGetOrCreateContainsCollection( + IRelationProvider relationProvider, + [NotNullWhen(returnValue: true)] out AggregateContainsRelationCollection? relationCollection) + { + if (_containsCollection is null && AggregateContainsRelationCollection.TryCreate(this, relationProvider, out AggregateContainsRelationCollection? collection)) { - if (_containsCollection is null && AggregateContainsRelationCollection.TryCreate(this, relationProvider, out AggregateContainsRelationCollection? collection)) - { - _containsCollection = collection; - } - - relationCollection = _containsCollection; - return relationCollection is not null; + _containsCollection = collection; } - bool IRelatableItem.TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree? projectTree) - { - return TryGetProjectNode(targetRootNode, item, out projectTree); - } + relationCollection = _containsCollection; + return relationCollection is not null; + } - /// - protected virtual bool TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree? projectTree) - { - projectTree = null; - return false; - } + bool IRelatableItem.TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree? projectTree) + { + return TryGetProjectNode(targetRootNode, item, out projectTree); + } + + /// + protected virtual bool TryGetProjectNode(IProjectTree targetRootNode, IRelatableItem item, [NotNullWhen(returnValue: true)] out IProjectTree? projectTree) + { + projectTree = null; + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationAttachedCollectionSourceProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationAttachedCollectionSourceProvider.cs index 45e494c7c2..468e81685e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationAttachedCollectionSourceProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationAttachedCollectionSourceProvider.cs @@ -4,59 +4,58 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Attaches parents/children to instances via s. +/// +/// +/// See also which attaches children +/// to the objects that represent top-level project dependencies. +/// +[AppliesToProject(ProjectCapability.DependenciesTree)] +[Export(typeof(IAttachedCollectionSourceProvider))] +[Name(nameof(RelationAttachedCollectionSourceProvider))] +[VisualStudio.Utilities.Order(Before = HierarchyItemsProviderNames.Contains)] +internal sealed class RelationAttachedCollectionSourceProvider : IAttachedCollectionSourceProvider { - /// - /// Attaches parents/children to instances via s. - /// - /// - /// See also which attaches children - /// to the objects that represent top-level project dependencies. - /// - [AppliesToProject(ProjectCapability.DependenciesTree)] - [Export(typeof(IAttachedCollectionSourceProvider))] - [Name(nameof(RelationAttachedCollectionSourceProvider))] - [VisualStudio.Utilities.Order(Before = HierarchyItemsProviderNames.Contains)] - internal sealed class RelationAttachedCollectionSourceProvider : IAttachedCollectionSourceProvider - { - [Import] private IRelationProvider RelationProvider { get; set; } = null!; + [Import] private IRelationProvider RelationProvider { get; set; } = null!; - public IAttachedCollectionSource? CreateCollectionSource(object item, string relationName) + public IAttachedCollectionSource? CreateCollectionSource(object item, string relationName) + { + if (relationName == KnownRelationships.Contains) { - if (relationName == KnownRelationships.Contains) + if (item is IRelatableItem relatableItem) { - if (item is IRelatableItem relatableItem) + if (relatableItem.TryGetOrCreateContainsCollection(RelationProvider, out AggregateContainsRelationCollection? collection)) { - if (relatableItem.TryGetOrCreateContainsCollection(RelationProvider, out AggregateContainsRelationCollection? collection)) - { - return new AggregateRelationCollectionSource(relatableItem, collection); - } + return new AggregateRelationCollectionSource(relatableItem, collection); } } - else if (relationName == KnownRelationships.ContainedBy) + } + else if (relationName == KnownRelationships.ContainedBy) + { + if (item is IRelatableItem { ContainedByCollection: { } containedByCollection } relatableItem) { - if (item is IRelatableItem { ContainedByCollection: { } containedByCollection } relatableItem) - { - return new AggregateRelationCollectionSource(relatableItem, containedByCollection); - } + return new AggregateRelationCollectionSource(relatableItem, containedByCollection); } - - return null; } - public IEnumerable GetRelationships(object item) + return null; + } + + public IEnumerable GetRelationships(object item) + { + if (item is IRelatableItem relatableItem) { - if (item is IRelatableItem relatableItem) - { - // We always return "Contains" relationship, even if no IRelation exists to produce - // those children. Doing so lights up context menu items related to Solution Explorer - // scoping. - yield return Relationships.Contains; + // We always return "Contains" relationship, even if no IRelation exists to produce + // those children. Doing so lights up context menu items related to Solution Explorer + // scoping. + yield return Relationships.Contains; - if (relatableItem.ContainedByCollection is not null) - { - yield return Relationships.ContainedBy; - } + if (relatableItem.ContainedByCollection is not null) + { + yield return Relationships.ContainedBy; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationBase.cs index 4af4b003fc..b777320923 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationBase.cs @@ -1,49 +1,48 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +/// +/// Typed wrapper for that uses specific types for parent/child items. +/// +/// +/// Using this type makes it easier to implement correctly. +/// +/// The type of parents in the relation. +/// The type of children in the relation. +public abstract class RelationBase : IRelation where TParent : class, IRelatableItem + where TChild : class, IRelatableItem { - /// - /// Typed wrapper for that uses specific types for parent/child items. - /// - /// - /// Using this type makes it easier to implement correctly. - /// - /// The type of parents in the relation. - /// The type of children in the relation. - public abstract class RelationBase : IRelation where TParent : class, IRelatableItem - where TChild : class, IRelatableItem + void IRelation.UpdateContainsCollection(IRelatableItem parent, AggregateContainsRelationCollectionSpan span) { - void IRelation.UpdateContainsCollection(IRelatableItem parent, AggregateContainsRelationCollectionSpan span) + if (parent is TParent typedParent) { - if (parent is TParent typedParent) - { - UpdateContainsCollection(typedParent, span); - } + UpdateContainsCollection(typedParent, span); } + } - IEnumerable? IRelation.CreateContainedByItems(IRelatableItem child) + IEnumerable? IRelation.CreateContainedByItems(IRelatableItem child) + { + if (child is TChild typedChild) { - if (child is TChild typedChild) - { - return CreateContainedByItems(typedChild); - } - - return null; + return CreateContainedByItems(typedChild); } - bool IRelation.SupportsContainsFor(Type parentType) => ReferenceEquals(parentType, typeof(TParent)); + return null; + } - bool IRelation.SupportsContainedByFor(Type childType) => ReferenceEquals(childType, typeof(TChild)); + bool IRelation.SupportsContainsFor(Type parentType) => ReferenceEquals(parentType, typeof(TParent)); - bool IRelation.HasContainedItem(IRelatableItem parent) => HasContainedItems((TParent)parent); + bool IRelation.SupportsContainedByFor(Type childType) => ReferenceEquals(childType, typeof(TChild)); - /// - protected abstract bool HasContainedItems(TParent parent); + bool IRelation.HasContainedItem(IRelatableItem parent) => HasContainedItems((TParent)parent); - /// - protected abstract void UpdateContainsCollection(TParent parent, AggregateContainsRelationCollectionSpan span); + /// + protected abstract bool HasContainedItems(TParent parent); - /// - protected abstract IEnumerable? CreateContainedByItems(TChild child); - } + /// + protected abstract void UpdateContainsCollection(TParent parent, AggregateContainsRelationCollectionSpan span); + + /// + protected abstract IEnumerable? CreateContainedByItems(TChild child); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationProvider.cs index 9c61114fa4..13b48a3a68 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/RelationProvider.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +[Export(typeof(IRelationProvider))] +internal sealed class RelationProvider : IRelationProvider { - [Export(typeof(IRelationProvider))] - internal sealed class RelationProvider : IRelationProvider - { - private readonly IEnumerable _allRelations; - private ImmutableDictionary> _containsRelationsByTypes = ImmutableDictionary>.Empty; - private ImmutableDictionary> _containedByRelationsByTypes = ImmutableDictionary>.Empty; + private readonly IEnumerable _allRelations; + private ImmutableDictionary> _containsRelationsByTypes = ImmutableDictionary>.Empty; + private ImmutableDictionary> _containedByRelationsByTypes = ImmutableDictionary>.Empty; - [ImportingConstructor] - public RelationProvider([ImportMany] IEnumerable allRelations) - { - _allRelations = allRelations; - } + [ImportingConstructor] + public RelationProvider([ImportMany] IEnumerable allRelations) + { + _allRelations = allRelations; + } - public ImmutableArray GetContainsRelationsFor(Type parentType) - { - return ImmutableInterlocked.GetOrAdd( - ref _containsRelationsByTypes, - parentType, - (t, all) => all.Where(relation => relation.SupportsContainsFor(t)).ToImmutableArray(), - _allRelations); - } + public ImmutableArray GetContainsRelationsFor(Type parentType) + { + return ImmutableInterlocked.GetOrAdd( + ref _containsRelationsByTypes, + parentType, + (t, all) => all.Where(relation => relation.SupportsContainsFor(t)).ToImmutableArray(), + _allRelations); + } - public ImmutableArray GetContainedByRelationsFor(Type childType) - { - return ImmutableInterlocked.GetOrAdd( - ref _containedByRelationsByTypes, - childType, - (t, all) => all.Where(relation => relation.SupportsContainedByFor(t)).ToImmutableArray(), - _allRelations); - } + public ImmutableArray GetContainedByRelationsFor(Type childType) + { + return ImmutableInterlocked.GetOrAdd( + ref _containedByRelationsByTypes, + childType, + (t, all) => all.Where(relation => relation.SupportsContainedByFor(t)).ToImmutableArray(), + _allRelations); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Relationships.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Relationships.cs index 2af479c56d..86a30f7958 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Relationships.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/AttachedCollections/Relationships.cs @@ -3,24 +3,23 @@ using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.AttachedCollections; + +internal static class Relationships { - internal static class Relationships - { - public static IAttachedRelationship Contains { get; } = new ContainsAttachedRelationship(); + public static IAttachedRelationship Contains { get; } = new ContainsAttachedRelationship(); - public static IAttachedRelationship ContainedBy { get; } = new ContainedByAttachedRelationship(); + public static IAttachedRelationship ContainedBy { get; } = new ContainedByAttachedRelationship(); - private sealed class ContainsAttachedRelationship : IAttachedRelationship - { - public string Name => KnownRelationships.Contains; - public string DisplayName => KnownRelationships.Contains; - } + private sealed class ContainsAttachedRelationship : IAttachedRelationship + { + public string Name => KnownRelationships.Contains; + public string DisplayName => KnownRelationships.Contains; + } - private sealed class ContainedByAttachedRelationship : IAttachedRelationship - { - public string Name => KnownRelationships.ContainedBy; - public string DisplayName => KnownRelationships.ContainedBy; - } + private sealed class ContainedByAttachedRelationship : IAttachedRelationship + { + public string Name => KnownRelationships.ContainedBy; + public string DisplayName => KnownRelationships.ContainedBy; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/DependenciesContextMenuProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/DependenciesContextMenuProvider.cs index f20df8d819..61389295ed 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/DependenciesContextMenuProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/DependenciesContextMenuProvider.cs @@ -2,153 +2,152 @@ using Flags = Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.DependencyTreeFlags; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands; + +/// +/// Provides context menus for nodes in the dependencies tree. +/// +[Export(typeof(IProjectItemContextMenuProvider))] +[AppliesTo(ProjectCapability.DependenciesTree)] +[Order(Order.Default)] +internal class DependenciesContextMenuProvider : IProjectItemContextMenuProvider { - /// - /// Provides context menus for nodes in the dependencies tree. - /// - [Export(typeof(IProjectItemContextMenuProvider))] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order.Default)] - internal class DependenciesContextMenuProvider : IProjectItemContextMenuProvider + private static class Menus { - private static class Menus - { - public const int IDM_VS_CTXT_REFERENCEROOT = 0x0450; - public const int IDM_VS_CTXT_REFERENCE = 0x0451; - public const int IDM_VS_CTXT_DEPENDENCYTARGET = 0X04A0; // Target framework group node - public const int IDM_VS_CTXT_REFERENCE_GROUP = 0X04A1; // Assembly reference group - public const int IDM_VS_CTXT_PACKAGEREFERENCE_GROUP = 0x04A2; - public const int IDM_VS_CTXT_PACKAGEREFERENCE = 0x04A3; - public const int IDM_VS_CTXT_COMREFERENCE_GROUP = 0x04A4; - public const int IDM_VS_CTXT_COMREFERENCE = 0x04A5; - public const int IDM_VS_CTXT_PROJECTREFERENCE_GROUP = 0x04A6; - public const int IDM_VS_CTXT_PROJECTREFERENCE = 0x04A7; - public const int IDM_VS_CTXT_SHAREDPROJECTREFERENCE = 0x04A8; - public const int IDM_VS_CTXT_FRAMEWORKREFERENCE_GROUP = 0x04A9; - public const int IDM_VS_CTXT_FRAMEWORKREFERENCE = 0x04AA; - public const int IDM_VS_CTXT_ANALYZERREFERENCE_GROUP = 0x04AB; - public const int IDM_VS_CTXT_ANALYZERREFERENCE = 0x04AC; - public const int IDM_VS_CTXT_SDKREFERENCE_GROUP = 0x04AD; - public const int IDM_VS_CTXT_SDKREFERENCE = 0x04AE; - } - - public bool TryGetContextMenu(IProjectTree projectItem, out Guid menuCommandGuid, out int menuCommandId) - { - Requires.NotNull(projectItem); + public const int IDM_VS_CTXT_REFERENCEROOT = 0x0450; + public const int IDM_VS_CTXT_REFERENCE = 0x0451; + public const int IDM_VS_CTXT_DEPENDENCYTARGET = 0X04A0; // Target framework group node + public const int IDM_VS_CTXT_REFERENCE_GROUP = 0X04A1; // Assembly reference group + public const int IDM_VS_CTXT_PACKAGEREFERENCE_GROUP = 0x04A2; + public const int IDM_VS_CTXT_PACKAGEREFERENCE = 0x04A3; + public const int IDM_VS_CTXT_COMREFERENCE_GROUP = 0x04A4; + public const int IDM_VS_CTXT_COMREFERENCE = 0x04A5; + public const int IDM_VS_CTXT_PROJECTREFERENCE_GROUP = 0x04A6; + public const int IDM_VS_CTXT_PROJECTREFERENCE = 0x04A7; + public const int IDM_VS_CTXT_SHAREDPROJECTREFERENCE = 0x04A8; + public const int IDM_VS_CTXT_FRAMEWORKREFERENCE_GROUP = 0x04A9; + public const int IDM_VS_CTXT_FRAMEWORKREFERENCE = 0x04AA; + public const int IDM_VS_CTXT_ANALYZERREFERENCE_GROUP = 0x04AB; + public const int IDM_VS_CTXT_ANALYZERREFERENCE = 0x04AC; + public const int IDM_VS_CTXT_SDKREFERENCE_GROUP = 0x04AD; + public const int IDM_VS_CTXT_SDKREFERENCE = 0x04AE; + } - if (projectItem.Flags.Contains(Flags.DependenciesRootNode)) - { - menuCommandId = Menus.IDM_VS_CTXT_REFERENCEROOT; - } - else if (projectItem.Flags.Contains(Flags.TargetNode)) - { - menuCommandId = Menus.IDM_VS_CTXT_DEPENDENCYTARGET; - } - else if (projectItem.Flags.Contains(Flags.AssemblyDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_REFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.AssemblyDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_REFERENCE; - } - else if (projectItem.Flags.Contains(Flags.PackageDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_PACKAGEREFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.PackageDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_PACKAGEREFERENCE; - } - else if (projectItem.Flags.Contains(Flags.ComDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_COMREFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.ComDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_COMREFERENCE; - } - else if (projectItem.Flags.Contains(Flags.ProjectDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_PROJECTREFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.ProjectDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_PROJECTREFERENCE; - } - else if (projectItem.Flags.Contains(Flags.SharedProjectDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_SHAREDPROJECTREFERENCE; - } - else if (projectItem.Flags.Contains(Flags.AnalyzerDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_ANALYZERREFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.AnalyzerDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_ANALYZERREFERENCE; - } - else if (projectItem.Flags.Contains(Flags.FrameworkDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_FRAMEWORKREFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.FrameworkDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_FRAMEWORKREFERENCE; - } - else if (projectItem.Flags.Contains(Flags.SdkDependencyGroup)) - { - menuCommandId = Menus.IDM_VS_CTXT_SDKREFERENCE_GROUP; - } - else if (projectItem.Flags.Contains(Flags.SdkDependency)) - { - menuCommandId = Menus.IDM_VS_CTXT_SDKREFERENCE; - } - else - { - menuCommandGuid = default; - menuCommandId = default; - return false; - } + public bool TryGetContextMenu(IProjectTree projectItem, out Guid menuCommandGuid, out int menuCommandId) + { + Requires.NotNull(projectItem); - menuCommandGuid = VSConstants.CMDSETID.ShellMainMenu_guid; - return true; + if (projectItem.Flags.Contains(Flags.DependenciesRootNode)) + { + menuCommandId = Menus.IDM_VS_CTXT_REFERENCEROOT; } - - public bool TryGetMixedItemsContextMenu(IEnumerable projectItems, out Guid menuCommandGuid, out int menuCommandId) + else if (projectItem.Flags.Contains(Flags.TargetNode)) + { + menuCommandId = Menus.IDM_VS_CTXT_DEPENDENCYTARGET; + } + else if (projectItem.Flags.Contains(Flags.AssemblyDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_REFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.AssemblyDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_REFERENCE; + } + else if (projectItem.Flags.Contains(Flags.PackageDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_PACKAGEREFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.PackageDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_PACKAGEREFERENCE; + } + else if (projectItem.Flags.Contains(Flags.ComDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_COMREFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.ComDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_COMREFERENCE; + } + else if (projectItem.Flags.Contains(Flags.ProjectDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_PROJECTREFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.ProjectDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_PROJECTREFERENCE; + } + else if (projectItem.Flags.Contains(Flags.SharedProjectDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_SHAREDPROJECTREFERENCE; + } + else if (projectItem.Flags.Contains(Flags.AnalyzerDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_ANALYZERREFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.AnalyzerDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_ANALYZERREFERENCE; + } + else if (projectItem.Flags.Contains(Flags.FrameworkDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_FRAMEWORKREFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.FrameworkDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_FRAMEWORKREFERENCE; + } + else if (projectItem.Flags.Contains(Flags.SdkDependencyGroup)) + { + menuCommandId = Menus.IDM_VS_CTXT_SDKREFERENCE_GROUP; + } + else if (projectItem.Flags.Contains(Flags.SdkDependency)) + { + menuCommandId = Menus.IDM_VS_CTXT_SDKREFERENCE; + } + else { - Requires.NotNull(projectItems); - menuCommandGuid = default; menuCommandId = default; + return false; + } - // If there are multiple items, and any item is a group node, suppress the context menu altogether + menuCommandGuid = VSConstants.CMDSETID.ShellMainMenu_guid; + return true; + } - int count = 0; - bool containsProhibited = false; + public bool TryGetMixedItemsContextMenu(IEnumerable projectItems, out Guid menuCommandGuid, out int menuCommandId) + { + Requires.NotNull(projectItems); - foreach (IProjectTree item in projectItems) - { - count++; + menuCommandGuid = default; + menuCommandId = default; - if (!containsProhibited) - { - if (item.Flags.Contains(Flags.DependencyGroup) || - item.Flags.Contains(Flags.TargetNode) || - item.Flags.Contains(Flags.DependenciesRootNode)) - { - containsProhibited = true; - } - } + // If there are multiple items, and any item is a group node, suppress the context menu altogether + + int count = 0; + bool containsProhibited = false; + + foreach (IProjectTree item in projectItems) + { + count++; - if (containsProhibited && count > 1) + if (!containsProhibited) + { + if (item.Flags.Contains(Flags.DependencyGroup) || + item.Flags.Contains(Flags.TargetNode) || + item.Flags.Contains(Flags.DependenciesRootNode)) { - return true; + containsProhibited = true; } } - return false; + if (containsProhibited && count > 1) + { + return true; + } } + + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/HideAddReferenceCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/HideAddReferenceCommand.cs index 0d128bf430..c1464b5690 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/HideAddReferenceCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/HideAddReferenceCommand.cs @@ -3,29 +3,28 @@ using Microsoft.VisualStudio.ProjectSystem.Input; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands; + +/// +/// Hides the "Add Reference..." command in various menus. +/// +/// +/// All places that previously had this command should now feature IDG_VS_CTXT_REFERENCEMANAGEMENT +/// which includes specific "Add ___ Reference..." commands, including "Add Assembly Reference...". +/// +[ProjectCommand(VSConstants.CMDSETID.StandardCommandSet2K_string, (long)VSConstants.VSStd2KCmdID.ADDREFERENCE)] +[AppliesTo(ProjectCapability.DependenciesTree)] +[Order(Order.Default)] +internal sealed class HideAddReferenceCommand : AbstractSingleNodeProjectCommand { - /// - /// Hides the "Add Reference..." command in various menus. - /// - /// - /// All places that previously had this command should now feature IDG_VS_CTXT_REFERENCEMANAGEMENT - /// which includes specific "Add ___ Reference..." commands, including "Add Assembly Reference...". - /// - [ProjectCommand(VSConstants.CMDSETID.StandardCommandSet2K_string, (long)VSConstants.VSStd2KCmdID.ADDREFERENCE)] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order.Default)] - internal sealed class HideAddReferenceCommand : AbstractSingleNodeProjectCommand + protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) { - protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) - { - progressiveStatus |= CommandStatus.Invisible; - return GetCommandStatusResult.Handled(commandText, progressiveStatus); - } + progressiveStatus |= CommandStatus.Invisible; + return GetCommandStatusResult.Handled(commandText, progressiveStatus); + } - protected override Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - return TaskResult.False; - } + protected override Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + return TaskResult.False; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/NavigateToProjectCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/NavigateToProjectCommand.cs index 266962b285..6d17aeaf2a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/NavigateToProjectCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/NavigateToProjectCommand.cs @@ -8,76 +8,75 @@ using Flags = Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.DependencyTreeFlags; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands; + +/// +/// Navigates from a reference to associated project or shared project +/// in Solution Explorer. +/// +[ProjectCommand(CommandGroup.ManagedProjectSystem, ManagedProjectSystemCommandId.NavigateToProject)] +[AppliesTo(ProjectCapability.DependenciesTree)] +internal class NavigateToProjectCommand : AbstractSingleNodeProjectCommand { - /// - /// Navigates from a reference to associated project or shared project - /// in Solution Explorer. - /// - [ProjectCommand(CommandGroup.ManagedProjectSystem, ManagedProjectSystemCommandId.NavigateToProject)] - [AppliesTo(ProjectCapability.DependenciesTree)] - internal class NavigateToProjectCommand : AbstractSingleNodeProjectCommand + private readonly UnconfiguredProject _project; + private readonly IProjectThreadingService _threadingService; + private readonly IVsProjectServices _projectServices; + private readonly SolutionExplorerWindow _solutionExplorer; + + [ImportingConstructor] + public NavigateToProjectCommand( + UnconfiguredProject project, + IProjectThreadingService threadingService, + IVsProjectServices projectServices, + SolutionExplorerWindow solutionExplorer) { - private readonly UnconfiguredProject _project; - private readonly IProjectThreadingService _threadingService; - private readonly IVsProjectServices _projectServices; - private readonly SolutionExplorerWindow _solutionExplorer; + _project = project; + _threadingService = threadingService; + _projectServices = projectServices; + _solutionExplorer = solutionExplorer; + } - [ImportingConstructor] - public NavigateToProjectCommand( - UnconfiguredProject project, - IProjectThreadingService threadingService, - IVsProjectServices projectServices, - SolutionExplorerWindow solutionExplorer) + protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) + { + if (CanNavigateTo(node)) { - _project = project; - _threadingService = threadingService; - _projectServices = projectServices; - _solutionExplorer = solutionExplorer; + return GetCommandStatusResult.Handled(commandText, progressiveStatus | CommandStatus.Enabled); } - protected override Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus) - { - if (CanNavigateTo(node)) - { - return GetCommandStatusResult.Handled(commandText, progressiveStatus | CommandStatus.Enabled); - } - - return GetCommandStatusResult.Unhandled; - } + return GetCommandStatusResult.Unhandled; + } - protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + protected override async Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + if (CanNavigateTo(node)) { - if (CanNavigateTo(node)) - { - await NavigateToAsync(node); - return true; - } - - return false; + await NavigateToAsync(node); + return true; } - private static bool CanNavigateTo(IProjectTree node) - { - return node.Flags.ContainsAny( - Flags.ProjectDependency | - Flags.SharedProjectDependency); - } + return false; + } - private async Task NavigateToAsync(IProjectTree node) - { - string? browsePath = await DependencyServices.GetBrowsePathAsync(_project, node); - if (browsePath is null) - return; + private static bool CanNavigateTo(IProjectTree node) + { + return node.Flags.ContainsAny( + Flags.ProjectDependency | + Flags.SharedProjectDependency); + } + + private async Task NavigateToAsync(IProjectTree node) + { + string? browsePath = await DependencyServices.GetBrowsePathAsync(_project, node); + if (browsePath is null) + return; - await _threadingService.SwitchToUIThread(); + await _threadingService.SwitchToUIThread(); - // Find the hierarchy based on the project file, and then select it - var hierarchy = (IVsUIHierarchy?)_projectServices.GetHierarchyByProjectName(browsePath); - if (hierarchy is null || !_solutionExplorer.IsAvailable) - return; + // Find the hierarchy based on the project file, and then select it + var hierarchy = (IVsUIHierarchy?)_projectServices.GetHierarchyByProjectName(browsePath); + if (hierarchy is null || !_solutionExplorer.IsAvailable) + return; - _ = _solutionExplorer.Select(hierarchy, HierarchyId.Root); - } + _ = _solutionExplorer.Select(hierarchy, HierarchyId.Root); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ReferenceManagerCommandHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ReferenceManagerCommandHandler.cs index 11e6214b4e..896ed4afe0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ReferenceManagerCommandHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Commands/ReferenceManagerCommandHandler.cs @@ -5,91 +5,90 @@ using VsReferenceManagerUserImportCollection = Microsoft.VisualStudio.ProjectSystem.OrderPrecedenceImportCollection; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Commands; + +/// +/// Enables commands for launching Reference Manager at a specific page. +/// +[ExportCommandGroup(VSConstants.CMDSETID.StandardCommandSet16_string)] +[AppliesTo(ProjectCapability.DependenciesTree)] +[Order(Order.Default)] +internal sealed class ReferenceManagerCommandHandler : ICommandGroupHandler { - /// - /// Enables commands for launching Reference Manager at a specific page. - /// - [ExportCommandGroup(VSConstants.CMDSETID.StandardCommandSet16_string)] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order.Default)] - internal sealed class ReferenceManagerCommandHandler : ICommandGroupHandler - { - private readonly IReferencesUI _referencesUI; + private readonly IReferencesUI _referencesUI; - [ImportingConstructor] - public ReferenceManagerCommandHandler(ConfiguredProject project, IReferencesUI referencesUI) - { - _referencesUI = referencesUI; - ReferenceManagerUsers = new VsReferenceManagerUserImportCollection(projectCapabilityCheckProvider: project); - } + [ImportingConstructor] + public ReferenceManagerCommandHandler(ConfiguredProject project, IReferencesUI referencesUI) + { + _referencesUI = referencesUI; + ReferenceManagerUsers = new VsReferenceManagerUserImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - public VsReferenceManagerUserImportCollection ReferenceManagerUsers - { - get; - } + [ImportMany] + public VsReferenceManagerUserImportCollection ReferenceManagerUsers + { + get; + } - public CommandStatusResult GetCommandStatus(IImmutableSet items, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + public CommandStatusResult GetCommandStatus(IImmutableSet items, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + { + if (CanAddReference(commandId)) { - if (CanAddReference(commandId)) - { - progressiveStatus &= ~CommandStatus.Invisible; - progressiveStatus |= CommandStatus.Enabled | CommandStatus.Supported; + progressiveStatus &= ~CommandStatus.Invisible; + progressiveStatus |= CommandStatus.Enabled | CommandStatus.Supported; - if (items.Any(tree => tree.IsFolder)) - { // Hide these commands for Folder -> Add - progressiveStatus |= CommandStatus.InvisibleOnContextMenu; - } - - return new CommandStatusResult(handled: true, commandText, progressiveStatus); + if (items.Any(tree => tree.IsFolder)) + { // Hide these commands for Folder -> Add + progressiveStatus |= CommandStatus.InvisibleOnContextMenu; } - return CommandStatusResult.Unhandled; + return new CommandStatusResult(handled: true, commandText, progressiveStatus); } - public bool TryHandleCommand(IImmutableSet items, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - string? identifier = GetReferenceProviderIdentifier(commandId); + return CommandStatusResult.Unhandled; + } - if (identifier is not null) - { - _referencesUI.ShowReferenceManagerDialog(new Guid(identifier)); - return true; - } + public bool TryHandleCommand(IImmutableSet items, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + string? identifier = GetReferenceProviderIdentifier(commandId); - return false; + if (identifier is not null) + { + _referencesUI.ShowReferenceManagerDialog(new Guid(identifier)); + return true; } - private bool CanAddReference(long commandId) - { - string? identifier = GetReferenceProviderIdentifier(commandId); - if (identifier is not null) - { - Lazy? user = ReferenceManagerUsers.FirstOrDefault(u => u.Metadata.ProviderContextIdentifier == identifier); + return false; + } - return user?.Value.IsApplicable() == true; - } + private bool CanAddReference(long commandId) + { + string? identifier = GetReferenceProviderIdentifier(commandId); + if (identifier is not null) + { + Lazy? user = ReferenceManagerUsers.FirstOrDefault(u => u.Metadata.ProviderContextIdentifier == identifier); - return false; + return user?.Value.IsApplicable() == true; } - private static string? GetReferenceProviderIdentifier(long commandId) + return false; + } + + private static string? GetReferenceProviderIdentifier(long commandId) + { + return (VSConstants.VSStd16CmdID)commandId switch { - return (VSConstants.VSStd16CmdID)commandId switch - { - VSConstants.VSStd16CmdID.AddAssemblyReference => VSConstants.AssemblyReferenceProvider_string, - VSConstants.VSStd16CmdID.AddComReference => VSConstants.ComReferenceProvider_string, - VSConstants.VSStd16CmdID.AddProjectReference => VSConstants.ProjectReferenceProvider_string, - VSConstants.VSStd16CmdID.AddSharedProjectReference => VSConstants.SharedProjectReferenceProvider_string, - VSConstants.VSStd16CmdID.AddSdkReference => VSConstants.PlatformReferenceProvider_string, - _ => null, - }; - - // Other known provider GUIDs: - // - // - VSConstants.FileReferenceProvider_string - // - VSConstants.ConnectedServiceInstanceReferenceProvider_string - } + VSConstants.VSStd16CmdID.AddAssemblyReference => VSConstants.AssemblyReferenceProvider_string, + VSConstants.VSStd16CmdID.AddComReference => VSConstants.ComReferenceProvider_string, + VSConstants.VSStd16CmdID.AddProjectReference => VSConstants.ProjectReferenceProvider_string, + VSConstants.VSStd16CmdID.AddSharedProjectReference => VSConstants.SharedProjectReferenceProvider_string, + VSConstants.VSStd16CmdID.AddSdkReference => VSConstants.PlatformReferenceProvider_string, + _ => null, + }; + + // Other known provider GUIDs: + // + // - VSConstants.FileReferenceProvider_string + // - VSConstants.ConnectedServiceInstanceReferenceProvider_string } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/ProjectImports/ImportTreeCommandGroupHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/ProjectImports/ImportTreeCommandGroupHandler.cs index bf0407e0ab..f6d85a8421 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/ProjectImports/ImportTreeCommandGroupHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/ProjectImports/ImportTreeCommandGroupHandler.cs @@ -9,223 +9,222 @@ using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; using static Microsoft.VisualStudio.VSConstants; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.ProjectImports +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.ProjectImports; + +/// +/// Handles opening of files displayed in the project imports tree. +/// +internal abstract class ProjectImportsCommandGroupHandlerBase : IAsyncCommandGroupHandler { - /// - /// Handles opening of files displayed in the project imports tree. - /// - internal abstract class ProjectImportsCommandGroupHandlerBase : IAsyncCommandGroupHandler + private readonly IServiceProvider _serviceProvider; + private readonly ConfiguredProject _configuredProject; + private readonly IVsUIService _uiShellOpenDocument; + private readonly IVsUIService _externalFilesManager; + private readonly IVsUIService _oleServiceProvider; + + protected ProjectImportsCommandGroupHandlerBase( + IServiceProvider serviceProvider, + ConfiguredProject configuredProject, + IVsUIService uiShellOpenDocument, + IVsUIService externalFilesManager, + IVsUIService oleServiceProvider) { - private readonly IServiceProvider _serviceProvider; - private readonly ConfiguredProject _configuredProject; - private readonly IVsUIService _uiShellOpenDocument; - private readonly IVsUIService _externalFilesManager; - private readonly IVsUIService _oleServiceProvider; - - protected ProjectImportsCommandGroupHandlerBase( - IServiceProvider serviceProvider, - ConfiguredProject configuredProject, - IVsUIService uiShellOpenDocument, - IVsUIService externalFilesManager, - IVsUIService oleServiceProvider) - { - Requires.NotNull(serviceProvider); - Requires.NotNull(configuredProject); - Requires.NotNull(uiShellOpenDocument); - Requires.NotNull(externalFilesManager); - Requires.NotNull(oleServiceProvider); - - _serviceProvider = serviceProvider; - _configuredProject = configuredProject; - _uiShellOpenDocument = uiShellOpenDocument; - _externalFilesManager = externalFilesManager; - _oleServiceProvider = oleServiceProvider; - } + Requires.NotNull(serviceProvider); + Requires.NotNull(configuredProject); + Requires.NotNull(uiShellOpenDocument); + Requires.NotNull(externalFilesManager); + Requires.NotNull(oleServiceProvider); + + _serviceProvider = serviceProvider; + _configuredProject = configuredProject; + _uiShellOpenDocument = uiShellOpenDocument; + _externalFilesManager = externalFilesManager; + _oleServiceProvider = oleServiceProvider; + } - protected abstract bool IsOpenCommand(long commandId); + protected abstract bool IsOpenCommand(long commandId); - protected abstract bool IsOpenWithCommand(long commandId); + protected abstract bool IsOpenWithCommand(long commandId); - public Task GetCommandStatusAsync(IImmutableSet items, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + public Task GetCommandStatusAsync(IImmutableSet items, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + { + if (IsOpenCommand(commandId) && items.All(CanOpenFile)) { - if (IsOpenCommand(commandId) && items.All(CanOpenFile)) - { - progressiveStatus |= CommandStatus.Enabled | CommandStatus.Supported; - return new CommandStatusResult(true, commandText, progressiveStatus).AsTask(); - } - - return CommandStatusResult.Unhandled.AsTask(); + progressiveStatus |= CommandStatus.Enabled | CommandStatus.Supported; + return new CommandStatusResult(true, commandText, progressiveStatus).AsTask(); } - public Task TryHandleCommandAsync(IImmutableSet items, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + return CommandStatusResult.Unhandled.AsTask(); + } + + public Task TryHandleCommandAsync(IImmutableSet items, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + if (IsOpenCommand(commandId) && items.All(CanOpenFile)) { - if (IsOpenCommand(commandId) && items.All(CanOpenFile)) - { - OpenItems(); + OpenItems(); - return TaskResult.True; - } + return TaskResult.True; + } - return TaskResult.False; + return TaskResult.False; - void OpenItems() - { - Assumes.NotNull(_configuredProject.UnconfiguredProject.Services.HostObject); - var hierarchy = (IVsUIHierarchy)_configuredProject.UnconfiguredProject.Services.HostObject; - var rdt = new RunningDocumentTable(_serviceProvider); + void OpenItems() + { + Assumes.NotNull(_configuredProject.UnconfiguredProject.Services.HostObject); + var hierarchy = (IVsUIHierarchy)_configuredProject.UnconfiguredProject.Services.HostObject; + var rdt = new RunningDocumentTable(_serviceProvider); - // Open all items. - RunAllAndAggregateExceptions(items, OpenItem); + // Open all items. + RunAllAndAggregateExceptions(items, OpenItem); - void OpenItem(IProjectTree item) + void OpenItem(IProjectTree item) + { + IVsWindowFrame? windowFrame = null; + try { - IVsWindowFrame? windowFrame = null; - try + // Open the document. + Guid logicalView = IsOpenWithCommand(commandId) ? LOGVIEWID_UserChooseView : LOGVIEWID.Primary_guid; + IntPtr docData = IntPtr.Zero; + + ErrorHandler.ThrowOnFailure( + _uiShellOpenDocument.Value.OpenStandardEditor( + (uint)__VSOSEFLAGS.OSE_ChooseBestStdEditor, + item.FilePath, + ref logicalView, + item.Caption, + hierarchy, + item.GetHierarchyId(), + docData, + _oleServiceProvider.Value, + out windowFrame)); + + RunningDocumentInfo rdtInfo = rdt.GetDocumentInfo(item.FilePath); + + // Set it as read only if necessary. + bool isReadOnly = item.Flags.Contains(ImportTreeProvider.ProjectImportImplicit); + + if (isReadOnly && rdtInfo.DocData is IVsTextBuffer textBuffer) { - // Open the document. - Guid logicalView = IsOpenWithCommand(commandId) ? LOGVIEWID_UserChooseView : LOGVIEWID.Primary_guid; - IntPtr docData = IntPtr.Zero; - - ErrorHandler.ThrowOnFailure( - _uiShellOpenDocument.Value.OpenStandardEditor( - (uint)__VSOSEFLAGS.OSE_ChooseBestStdEditor, - item.FilePath, - ref logicalView, - item.Caption, - hierarchy, - item.GetHierarchyId(), - docData, - _oleServiceProvider.Value, - out windowFrame)); - - RunningDocumentInfo rdtInfo = rdt.GetDocumentInfo(item.FilePath); - - // Set it as read only if necessary. - bool isReadOnly = item.Flags.Contains(ImportTreeProvider.ProjectImportImplicit); - - if (isReadOnly && rdtInfo.DocData is IVsTextBuffer textBuffer) - { - textBuffer.GetStateFlags(out uint flags); - textBuffer.SetStateFlags(flags | (uint)BUFFERSTATEFLAGS.BSF_USER_READONLY); - } - - // Detach the document from this project. - // Ignore failure. It may be that we've already transferred the item to Miscellaneous Files. - _externalFilesManager.Value.TransferDocument(item.FilePath, item.FilePath, windowFrame); - - // Show the document window - if (windowFrame is not null) - { - ErrorHandler.ThrowOnFailure(windowFrame.Show()); - } + textBuffer.GetStateFlags(out uint flags); + textBuffer.SetStateFlags(flags | (uint)BUFFERSTATEFLAGS.BSF_USER_READONLY); } - catch + + // Detach the document from this project. + // Ignore failure. It may be that we've already transferred the item to Miscellaneous Files. + _externalFilesManager.Value.TransferDocument(item.FilePath, item.FilePath, windowFrame); + + // Show the document window + if (windowFrame is not null) { - windowFrame?.CloseFrame(0); - throw; + ErrorHandler.ThrowOnFailure(windowFrame.Show()); } } + catch + { + windowFrame?.CloseFrame(0); + throw; + } } } + } - private static bool CanOpenFile(IProjectTree node) => node.Flags.Contains(ImportTreeProvider.ProjectImport); + private static bool CanOpenFile(IProjectTree node) => node.Flags.Contains(ImportTreeProvider.ProjectImport); - /// - /// Calls for each of . If any action - /// throws, its exception is caught and processing continues. When all items have been - /// handled, any exceptions are thrown either as a single exception or an - /// . - /// - private static void RunAllAndAggregateExceptions(IEnumerable items, Action action) - { - List? exceptions = null; + /// + /// Calls for each of . If any action + /// throws, its exception is caught and processing continues. When all items have been + /// handled, any exceptions are thrown either as a single exception or an + /// . + /// + private static void RunAllAndAggregateExceptions(IEnumerable items, Action action) + { + List? exceptions = null; - foreach (T item in items) + foreach (T item in items) + { + try { - try - { - action(item); - } - catch (Exception ex) - { - exceptions ??= new List(); - exceptions.Add(ex); - } + action(item); } - - if (exceptions is not null) + catch (Exception ex) { - if (exceptions.Count == 1) - { - ExceptionDispatchInfo.Capture(exceptions[0]).Throw(); - } - else - { - throw new AggregateException(exceptions); - } + exceptions ??= new List(); + exceptions.Add(ex); } } - [ExportCommandGroup(CMDSETID.UIHierarchyWindowCommandSet_string)] - [AppliesTo(ProjectCapability.ProjectImportsTree)] - [Order(Order.BeforeDefault)] - private sealed class UIHierarchyWindowCommandSetGroupHandler : ProjectImportsCommandGroupHandlerBase + if (exceptions is not null) { - [ImportingConstructor] - public UIHierarchyWindowCommandSetGroupHandler( -#pragma warning disable RS0030 // Do not used banned APIs - [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, -#pragma warning restore RS0030 // Do not used banned APIs - ConfiguredProject configuredProject, - IVsUIService uiShellOpenDocument, - IVsUIService externalFilesManager, - IVsUIService oleServiceProvider) - : base(serviceProvider, configuredProject, uiShellOpenDocument, externalFilesManager, oleServiceProvider) + if (exceptions.Count == 1) { + ExceptionDispatchInfo.Capture(exceptions[0]).Throw(); } - - protected override bool IsOpenCommand(long commandId) + else { - return (VsUIHierarchyWindowCmdIds)commandId switch - { - VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick => true, - VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey => true, - _ => false - }; + throw new AggregateException(exceptions); } + } + } - protected override bool IsOpenWithCommand(long commandId) => false; + [ExportCommandGroup(CMDSETID.UIHierarchyWindowCommandSet_string)] + [AppliesTo(ProjectCapability.ProjectImportsTree)] + [Order(Order.BeforeDefault)] + private sealed class UIHierarchyWindowCommandSetGroupHandler : ProjectImportsCommandGroupHandlerBase + { + [ImportingConstructor] + public UIHierarchyWindowCommandSetGroupHandler( +#pragma warning disable RS0030 // Do not used banned APIs + [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, +#pragma warning restore RS0030 // Do not used banned APIs + ConfiguredProject configuredProject, + IVsUIService uiShellOpenDocument, + IVsUIService externalFilesManager, + IVsUIService oleServiceProvider) + : base(serviceProvider, configuredProject, uiShellOpenDocument, externalFilesManager, oleServiceProvider) + { } - [ExportCommandGroup(CMDSETID.StandardCommandSet97_string)] - [AppliesTo(ProjectCapability.ProjectImportsTree)] - [Order(Order.BeforeDefault)] - private sealed class StandardCommandSet97GroupHandler : ProjectImportsCommandGroupHandlerBase + protected override bool IsOpenCommand(long commandId) { - [ImportingConstructor] - public StandardCommandSet97GroupHandler( + return (VsUIHierarchyWindowCmdIds)commandId switch + { + VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick => true, + VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey => true, + _ => false + }; + } + + protected override bool IsOpenWithCommand(long commandId) => false; + } + + [ExportCommandGroup(CMDSETID.StandardCommandSet97_string)] + [AppliesTo(ProjectCapability.ProjectImportsTree)] + [Order(Order.BeforeDefault)] + private sealed class StandardCommandSet97GroupHandler : ProjectImportsCommandGroupHandlerBase + { + [ImportingConstructor] + public StandardCommandSet97GroupHandler( #pragma warning disable RS0030 // Do not used banned APIs - [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, + [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, #pragma warning restore RS0030 // Do not used banned APIs - ConfiguredProject configuredProject, - IVsUIService uiShellOpenDocument, - IVsUIService externalFilesManager, - IVsUIService oleServiceProvider) - : base(serviceProvider, configuredProject, uiShellOpenDocument, externalFilesManager, oleServiceProvider) - { - } + ConfiguredProject configuredProject, + IVsUIService uiShellOpenDocument, + IVsUIService externalFilesManager, + IVsUIService oleServiceProvider) + : base(serviceProvider, configuredProject, uiShellOpenDocument, externalFilesManager, oleServiceProvider) + { + } - protected override bool IsOpenCommand(long commandId) + protected override bool IsOpenCommand(long commandId) + { + return (VSStd97CmdID)commandId switch { - return (VSStd97CmdID)commandId switch - { - VSStd97CmdID.Open => true, - VSStd97CmdID.OpenWith => true, - _ => false - }; - } - - protected override bool IsOpenWithCommand(long commandId) => (VSStd97CmdID)commandId == VSStd97CmdID.OpenWith; + VSStd97CmdID.Open => true, + VSStd97CmdID.OpenWith => true, + _ => false + }; } + + protected override bool IsOpenWithCommand(long commandId) => (VSStd97CmdID)commandId == VSStd97CmdID.OpenWith; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/AddItemDialogService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/AddItemDialogService.cs index 2095e7c136..d765e58dfc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/AddItemDialogService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/AddItemDialogService.cs @@ -2,94 +2,93 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UI +namespace Microsoft.VisualStudio.ProjectSystem.VS.UI; + +/// +/// Provides an implementation of that wraps . +/// +[Export(typeof(IAddItemDialogService))] +internal class AddItemDialogService : IAddItemDialogService { - /// - /// Provides an implementation of that wraps . - /// - [Export(typeof(IAddItemDialogService))] - internal class AddItemDialogService : IAddItemDialogService + private readonly IUnconfiguredProjectVsServices _projectVsServices; + private readonly IPhysicalProjectTree _projectTree; + private readonly IVsUIService _addProjectItemDialog; + + [ImportingConstructor] + public AddItemDialogService(IUnconfiguredProjectVsServices unconfiguredProjectVsServices, IPhysicalProjectTree projectTree, IVsUIService addProjectItemDialog) + { + _projectVsServices = unconfiguredProjectVsServices; + _projectTree = projectTree; + _addProjectItemDialog = addProjectItemDialog; + } + + public Task ShowAddNewItemDialogAsync(IProjectTree node) + { + return ShowDialogAsync(node, + __VSADDITEMFLAGS.VSADDITEM_AddNewItems | + __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | + __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView); + } + + public Task ShowAddNewItemDialogAsync(IProjectTree node, string directoryLocalizedName, string templateLocalizedName) + { + Requires.NotNullOrEmpty(directoryLocalizedName); + Requires.NotNullOrEmpty(templateLocalizedName); + + return ShowDialogAsync(node, + __VSADDITEMFLAGS.VSADDITEM_AddNewItems | + __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | + __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView, + directoryLocalizedName, + templateLocalizedName); + } + + public Task ShowAddExistingItemsDialogAsync(IProjectTree node) + { + return ShowDialogAsync(node, + __VSADDITEMFLAGS.VSADDITEM_AddExistingItems | + __VSADDITEMFLAGS.VSADDITEM_AllowMultiSelect | + __VSADDITEMFLAGS.VSADDITEM_AllowStickyFilter | + __VSADDITEMFLAGS.VSADDITEM_ProjectHandlesLinks); + } + + private async Task ShowDialogAsync(IProjectTree node, __VSADDITEMFLAGS flags, string? localizedDirectoryName = null, string? localizedTemplateName = null) + { + string? path = _projectTree.TreeProvider.GetAddNewItemDirectory(node); + if (path is null) + throw new ArgumentException("Node is marked with DisableAddItemFolder or DisableAddItemRecursiveFolder, call CanAddNewOrExistingItemTo before calling this method.", nameof(node)); + + await _projectVsServices.ThreadingService.SwitchToUIThread(); + + string filter = string.Empty; + Guid addItemTemplateGuid = Guid.Empty; // Let the dialog ask the hierarchy itself + + IVsAddProjectItemDlg? addProjectItemDialog = _addProjectItemDialog.Value; + if (addProjectItemDialog is null) + return false; + + HResult result = addProjectItemDialog.AddProjectItemDlg( + node.GetHierarchyId(), + ref addItemTemplateGuid, + _projectVsServices.VsProject, + (uint)flags, + localizedDirectoryName, + localizedTemplateName, + ref path, + ref filter, + out _); + + if (result == HResult.Ole.PromptSaveCancelled) + return false; + + if (result.Failed) + throw result.Exception!; + + return true; + } + + public bool CanAddNewOrExistingItemTo(IProjectTree node) { - private readonly IUnconfiguredProjectVsServices _projectVsServices; - private readonly IPhysicalProjectTree _projectTree; - private readonly IVsUIService _addProjectItemDialog; - - [ImportingConstructor] - public AddItemDialogService(IUnconfiguredProjectVsServices unconfiguredProjectVsServices, IPhysicalProjectTree projectTree, IVsUIService addProjectItemDialog) - { - _projectVsServices = unconfiguredProjectVsServices; - _projectTree = projectTree; - _addProjectItemDialog = addProjectItemDialog; - } - - public Task ShowAddNewItemDialogAsync(IProjectTree node) - { - return ShowDialogAsync(node, - __VSADDITEMFLAGS.VSADDITEM_AddNewItems | - __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | - __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView); - } - - public Task ShowAddNewItemDialogAsync(IProjectTree node, string directoryLocalizedName, string templateLocalizedName) - { - Requires.NotNullOrEmpty(directoryLocalizedName); - Requires.NotNullOrEmpty(templateLocalizedName); - - return ShowDialogAsync(node, - __VSADDITEMFLAGS.VSADDITEM_AddNewItems | - __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | - __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView, - directoryLocalizedName, - templateLocalizedName); - } - - public Task ShowAddExistingItemsDialogAsync(IProjectTree node) - { - return ShowDialogAsync(node, - __VSADDITEMFLAGS.VSADDITEM_AddExistingItems | - __VSADDITEMFLAGS.VSADDITEM_AllowMultiSelect | - __VSADDITEMFLAGS.VSADDITEM_AllowStickyFilter | - __VSADDITEMFLAGS.VSADDITEM_ProjectHandlesLinks); - } - - private async Task ShowDialogAsync(IProjectTree node, __VSADDITEMFLAGS flags, string? localizedDirectoryName = null, string? localizedTemplateName = null) - { - string? path = _projectTree.TreeProvider.GetAddNewItemDirectory(node); - if (path is null) - throw new ArgumentException("Node is marked with DisableAddItemFolder or DisableAddItemRecursiveFolder, call CanAddNewOrExistingItemTo before calling this method.", nameof(node)); - - await _projectVsServices.ThreadingService.SwitchToUIThread(); - - string filter = string.Empty; - Guid addItemTemplateGuid = Guid.Empty; // Let the dialog ask the hierarchy itself - - IVsAddProjectItemDlg? addProjectItemDialog = _addProjectItemDialog.Value; - if (addProjectItemDialog is null) - return false; - - HResult result = addProjectItemDialog.AddProjectItemDlg( - node.GetHierarchyId(), - ref addItemTemplateGuid, - _projectVsServices.VsProject, - (uint)flags, - localizedDirectoryName, - localizedTemplateName, - ref path, - ref filter, - out _); - - if (result == HResult.Ole.PromptSaveCancelled) - return false; - - if (result.Failed) - throw result.Exception!; - - return true; - } - - public bool CanAddNewOrExistingItemTo(IProjectTree node) - { - return _projectTree.TreeProvider.GetAddNewItemDirectory(node) is not null; - } + return _projectTree.TreeProvider.GetAddNewItemDirectory(node) is not null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/IAddItemDialogService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/IAddItemDialogService.cs index fc6572a1d2..16986966ca 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/IAddItemDialogService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/IAddItemDialogService.cs @@ -1,79 +1,78 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UI +namespace Microsoft.VisualStudio.ProjectSystem.VS.UI; + +/// +/// Provides methods for opening the Add New Item or Add Existing Items dialogs. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IAddItemDialogService { /// - /// Provides methods for opening the Add New Item or Add Existing Items dialogs. + /// Returns a value indicating whether the specified node can have new or existing items added to it. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IAddItemDialogService - { - /// - /// Returns a value indicating whether the specified node can have new or existing items added to it. - /// - bool CanAddNewOrExistingItemTo(IProjectTree node); + bool CanAddNewOrExistingItemTo(IProjectTree node); - /// - /// Shows the "Add New Item" dialog for the specified node. - /// - /// - /// The that the new item will be added to. - /// - /// - /// if the user selected an item and clicked OK, otherwise, if the cancelled the dialog. - /// - /// - /// is marked with or . - /// - Task ShowAddNewItemDialogAsync(IProjectTree node); + /// + /// Shows the "Add New Item" dialog for the specified node. + /// + /// + /// The that the new item will be added to. + /// + /// + /// if the user selected an item and clicked OK, otherwise, if the cancelled the dialog. + /// + /// + /// is marked with or . + /// + Task ShowAddNewItemDialogAsync(IProjectTree node); - /// - /// Shows the "Add New Item" dialog for the specified node and selecting the specified template. - /// - /// - /// The that the new item will be added to. - /// - /// - /// The localized name of the directory that contains the template to select. - /// - /// - /// The localized name of the template to select. - /// - /// - /// if the user selected an item and clicked OK, otherwise, if the cancelled the dialog. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// is an empty string (""). - /// - /// -or- - /// - /// is an empty string (""). - /// - /// -or- - /// - /// is marked with or . - /// - Task ShowAddNewItemDialogAsync(IProjectTree node, string localizedDirectoryName, string localizedTemplateName); + /// + /// Shows the "Add New Item" dialog for the specified node and selecting the specified template. + /// + /// + /// The that the new item will be added to. + /// + /// + /// The localized name of the directory that contains the template to select. + /// + /// + /// The localized name of the template to select. + /// + /// + /// if the user selected an item and clicked OK, otherwise, if the cancelled the dialog. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is an empty string (""). + /// + /// -or- + /// + /// is an empty string (""). + /// + /// -or- + /// + /// is marked with or . + /// + Task ShowAddNewItemDialogAsync(IProjectTree node, string localizedDirectoryName, string localizedTemplateName); - /// - /// Shows the "Add Existing Items" dialog for the specified node. - /// - /// - /// The that the existing items will be added to. - /// - /// - /// if the user selected an item and clicked OK, otherwise, if the cancelled the dialog. - /// - /// - /// is marked with or . - /// - Task ShowAddExistingItemsDialogAsync(IProjectTree node); - } + /// + /// Shows the "Add Existing Items" dialog for the specified node. + /// + /// + /// The that the existing items will be added to. + /// + /// + /// if the user selected an item and clicked OK, otherwise, if the cancelled the dialog. + /// + /// + /// is marked with or . + /// + Task ShowAddExistingItemsDialogAsync(IProjectTree node); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UnconfiguredProjectVsServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UnconfiguredProjectVsServices.cs index da2a44ae1a..57eee6458b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UnconfiguredProjectVsServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UnconfiguredProjectVsServices.cs @@ -2,71 +2,70 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides an implementation of that delegates onto +/// its and underlying . +/// +[Export(typeof(IUnconfiguredProjectVsServices))] +internal class UnconfiguredProjectVsServices : IUnconfiguredProjectVsServices { - /// - /// Provides an implementation of that delegates onto - /// its and underlying . - /// - [Export(typeof(IUnconfiguredProjectVsServices))] - internal class UnconfiguredProjectVsServices : IUnconfiguredProjectVsServices - { - private readonly IUnconfiguredProjectCommonServices _commonServices; - private readonly Lazy _projectTree; + private readonly IUnconfiguredProjectCommonServices _commonServices; + private readonly Lazy _projectTree; - [ImportingConstructor] - public UnconfiguredProjectVsServices(IUnconfiguredProjectCommonServices commonServices, Lazy projectTree) - { - _commonServices = commonServices; - _projectTree = projectTree; - } + [ImportingConstructor] + public UnconfiguredProjectVsServices(IUnconfiguredProjectCommonServices commonServices, Lazy projectTree) + { + _commonServices = commonServices; + _projectTree = projectTree; + } - public IVsHierarchy VsHierarchy + public IVsHierarchy VsHierarchy + { + get { - get - { - Assumes.NotNull(_commonServices.Project.Services.HostObject); - return (IVsHierarchy)_commonServices.Project.Services.HostObject; - } + Assumes.NotNull(_commonServices.Project.Services.HostObject); + return (IVsHierarchy)_commonServices.Project.Services.HostObject; } + } - public IVsProject4 VsProject + public IVsProject4 VsProject + { + get { - get - { - Assumes.NotNull(_commonServices.Project.Services.HostObject); - return (IVsProject4)_commonServices.Project.Services.HostObject; - } + Assumes.NotNull(_commonServices.Project.Services.HostObject); + return (IVsProject4)_commonServices.Project.Services.HostObject; } + } - public IProjectThreadingService ThreadingService - { - get { return _commonServices.ThreadingService; } - } + public IProjectThreadingService ThreadingService + { + get { return _commonServices.ThreadingService; } + } - public UnconfiguredProject Project - { - get { return _commonServices.Project; } - } + public UnconfiguredProject Project + { + get { return _commonServices.Project; } + } - public IPhysicalProjectTree ProjectTree - { - get { return _projectTree.Value; } - } + public IPhysicalProjectTree ProjectTree + { + get { return _projectTree.Value; } + } - public ConfiguredProject ActiveConfiguredProject - { - get { return _commonServices.ActiveConfiguredProject; } - } + public ConfiguredProject ActiveConfiguredProject + { + get { return _commonServices.ActiveConfiguredProject; } + } - public ProjectProperties ActiveConfiguredProjectProperties - { - get { return _commonServices.ActiveConfiguredProjectProperties; } - } + public ProjectProperties ActiveConfiguredProjectProperties + { + get { return _commonServices.ActiveConfiguredProjectProperties; } + } - public IProjectAccessor ProjectAccessor - { - get { return _commonServices.ProjectAccessor; } - } + public IProjectAccessor ProjectAccessor + { + get { return _commonServices.ProjectAccessor; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.CopyType.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.CopyType.cs index 6cb10eb675..67fc4d3be7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.CopyType.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.CopyType.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +internal sealed partial class BuildUpToDateCheck { - internal sealed partial class BuildUpToDateCheck + /// + /// Potential values for the CopyToOutputDirectory metadata on items. + /// + internal enum CopyType { - /// - /// Potential values for the CopyToOutputDirectory metadata on items. - /// - internal enum CopyType - { - PreserveNewest, - Always - } + PreserveNewest, + Always } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.ItemHashing.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.ItemHashing.cs index 2702a2fa7e..0fb966d355 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.ItemHashing.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.ItemHashing.cs @@ -1,72 +1,71 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +internal sealed partial class BuildUpToDateCheck { - internal sealed partial class BuildUpToDateCheck + public static int ComputeItemHash(ImmutableDictionary> itemsByItemType) { - public static int ComputeItemHash(ImmutableDictionary> itemsByItemType) - { - int hash = 0; + int hash = 0; - // Use XOR so the order of items is not important. We are using string hash codes which are - // quite well distributed. This approach might not work as well for other types, such as integers. - // - // This approach also assumes each path is only included once in the data structure. If a path - // were to exist twice, its hash would be XORed with itself, which produces zero net change. - - foreach ((string itemType, ImmutableArray items) in itemsByItemType) - { - int itemHash = 0; + // Use XOR so the order of items is not important. We are using string hash codes which are + // quite well distributed. This approach might not work as well for other types, such as integers. + // + // This approach also assumes each path is only included once in the data structure. If a path + // were to exist twice, its hash would be XORed with itself, which produces zero net change. - foreach (string item in items) - { - itemHash ^= GetStableHashCode(item); - } + foreach ((string itemType, ImmutableArray items) in itemsByItemType) + { + int itemHash = 0; - // Multiply by the item type hash, so that if an item changes type the hash will change. - // The rest of the system does not really need this though, as it is assumed the only way - // an item can change type is if a project file changes, which would be detected via - // file timestamp changes. - hash ^= itemHash * GetStableHashCode(itemType); + foreach (string item in items) + { + itemHash ^= GetStableHashCode(item); } - return hash; + // Multiply by the item type hash, so that if an item changes type the hash will change. + // The rest of the system does not really need this though, as it is assumed the only way + // an item can change type is if a project file changes, which would be detected via + // file timestamp changes. + hash ^= itemHash * GetStableHashCode(itemType); } - /// - /// Returns the hash code of the string - /// - /// - /// Please, do not make changes to this hash algorithm. - /// Current hash value is persisted in a file in the .vs folder, - /// changing this algorithm may regress performance and break compatibility. - /// - /// The original code was taken from string.GetHashCode() with some minor changes - /// https://github.com/microsoft/referencesource/blob/master/mscorlib/system/string.cs - /// - internal static int GetStableHashCode(string str) - { - int hash1 = 5381; - int hash2 = hash1; + return hash; + } - int i = 0; - while (i < str.Length) - { - char c = str[i]; + /// + /// Returns the hash code of the string + /// + /// + /// Please, do not make changes to this hash algorithm. + /// Current hash value is persisted in a file in the .vs folder, + /// changing this algorithm may regress performance and break compatibility. + /// + /// The original code was taken from string.GetHashCode() with some minor changes + /// https://github.com/microsoft/referencesource/blob/master/mscorlib/system/string.cs + /// + internal static int GetStableHashCode(string str) + { + int hash1 = 5381; + int hash2 = hash1; - hash1 = ((hash1 << 5) + hash1) ^ c; + int i = 0; + while (i < str.Length) + { + char c = str[i]; - i++; - if (i == str.Length) - break; + hash1 = ((hash1 << 5) + hash1) ^ c; - c = str[i]; - hash2 = ((hash2 << 5) + hash2) ^ c; + i++; + if (i == str.Length) + break; - i++; - } + c = str[i]; + hash2 = ((hash2 << 5) + hash2) ^ c; - return hash1 + (hash2 * 1566083941); + i++; } + + return hash1 + (hash2 * 1566083941); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Log.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Log.cs index 2cd8514f1c..6d5a770a4b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Log.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Log.cs @@ -4,300 +4,299 @@ using Microsoft.VisualStudio.ProjectSystem.Build; using Microsoft.VisualStudio.Telemetry; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +internal sealed partial class BuildUpToDateCheck { - internal sealed partial class BuildUpToDateCheck + internal sealed class Log { - internal sealed class Log + private readonly TextWriter _writer; + private readonly IBuildUpToDateCheckProvider _check; + private readonly ICollection> _checkers; + private readonly ISolutionBuildEventListener _solutionBuildEventListener; + private readonly Stopwatch _stopwatch; + private readonly TimeSpan _waitTime; + private readonly TimestampCache _timestampCache; + private readonly Guid _projectGuid; + private readonly string _fileName; + private readonly ITelemetryService? _telemetryService; // null for validation runs + private readonly UpToDateCheckConfiguredInput _upToDateCheckConfiguredInput; + private readonly string? _ignoreKinds; + private readonly int _checkNumber; + + public LogLevel Level { get; } + + public int Indent { get; set; } + + public string? FailureReason { get; private set; } + public string? FailureDescription { get; private set; } + public FileSystemOperationAggregator? FileSystemOperations { get; set; } + + public Log(TextWriter writer, IBuildUpToDateCheckProvider check, ICollection> checkers, LogLevel requestedLogLevel, ISolutionBuildEventListener solutionBuildEventListener, Stopwatch stopwatch, TimeSpan waitTime, TimestampCache timestampCache, string projectPath, Guid projectGuid, ITelemetryService? telemetryService, UpToDateCheckConfiguredInput upToDateCheckConfiguredInput, string? ignoreKinds, int checkNumber) { - private readonly TextWriter _writer; - private readonly IBuildUpToDateCheckProvider _check; - private readonly ICollection> _checkers; - private readonly ISolutionBuildEventListener _solutionBuildEventListener; - private readonly Stopwatch _stopwatch; - private readonly TimeSpan _waitTime; - private readonly TimestampCache _timestampCache; - private readonly Guid _projectGuid; - private readonly string _fileName; - private readonly ITelemetryService? _telemetryService; // null for validation runs - private readonly UpToDateCheckConfiguredInput _upToDateCheckConfiguredInput; - private readonly string? _ignoreKinds; - private readonly int _checkNumber; - - public LogLevel Level { get; } - - public int Indent { get; set; } - - public string? FailureReason { get; private set; } - public string? FailureDescription { get; private set; } - public FileSystemOperationAggregator? FileSystemOperations { get; set; } - - public Log(TextWriter writer, IBuildUpToDateCheckProvider check, ICollection> checkers, LogLevel requestedLogLevel, ISolutionBuildEventListener solutionBuildEventListener, Stopwatch stopwatch, TimeSpan waitTime, TimestampCache timestampCache, string projectPath, Guid projectGuid, ITelemetryService? telemetryService, UpToDateCheckConfiguredInput upToDateCheckConfiguredInput, string? ignoreKinds, int checkNumber) - { - _writer = writer; - _check = check; - _checkers = checkers; - Level = requestedLogLevel; - _solutionBuildEventListener = solutionBuildEventListener; - _stopwatch = stopwatch; - _waitTime = waitTime; - _timestampCache = timestampCache; - _projectGuid = projectGuid; - _telemetryService = telemetryService; - _upToDateCheckConfiguredInput = upToDateCheckConfiguredInput; - _ignoreKinds = ignoreKinds; - _checkNumber = checkNumber; - - _fileName = Path.GetFileNameWithoutExtension(projectPath); - } - - public Scope IndentScope() => new(this); + _writer = writer; + _check = check; + _checkers = checkers; + Level = requestedLogLevel; + _solutionBuildEventListener = solutionBuildEventListener; + _stopwatch = stopwatch; + _waitTime = waitTime; + _timestampCache = timestampCache; + _projectGuid = projectGuid; + _telemetryService = telemetryService; + _upToDateCheckConfiguredInput = upToDateCheckConfiguredInput; + _ignoreKinds = ignoreKinds; + _checkNumber = checkNumber; + + _fileName = Path.GetFileNameWithoutExtension(projectPath); + } - private string Preamble() - { - return Indent switch - { - <= 0 => "FastUpToDate: ", - 1 => "FastUpToDate: ", - 2 => "FastUpToDate: ", - 3 => "FastUpToDate: ", - _ => "FastUpToDate: " + new string(' ', Indent * 4) - }; - } + public Scope IndentScope() => new(this); - private static string GetResourceString(string resourceName) + private string Preamble() + { + return Indent switch { - string? message = VSResources.ResourceManager.GetString(resourceName, VSResources.Culture); + <= 0 => "FastUpToDate: ", + 1 => "FastUpToDate: ", + 2 => "FastUpToDate: ", + 3 => "FastUpToDate: ", + _ => "FastUpToDate: " + new string(' ', Indent * 4) + }; + } - if (message is null) - { - Assumes.Fail($"Resource with name '{resourceName}' not found."); - } + private static string GetResourceString(string resourceName) + { + string? message = VSResources.ResourceManager.GetString(resourceName, VSResources.Culture); - return message; + if (message is null) + { + Assumes.Fail($"Resource with name '{resourceName}' not found."); } - private void Write(LogLevel level, string resourceName, object arg0) + return message; + } + + private void Write(LogLevel level, string resourceName, object arg0) + { + if (level <= Level) { - if (level <= Level) - { - // These are user visible, so we want them in local times so that - // they correspond with dates/times that Explorer, etc shows - ConvertToLocalTime(ref arg0); + // These are user visible, so we want them in local times so that + // they correspond with dates/times that Explorer, etc shows + ConvertToLocalTime(ref arg0); - string message = GetResourceString(resourceName); + string message = GetResourceString(resourceName); - _writer.WriteLine($"{Preamble()}{string.Format(message, arg0)} ({_fileName})"); - } + _writer.WriteLine($"{Preamble()}{string.Format(message, arg0)} ({_fileName})"); } + } - private void Write(LogLevel level, string resourceName, object arg0, object arg1) + private void Write(LogLevel level, string resourceName, object arg0, object arg1) + { + if (level <= Level) { - if (level <= Level) - { - // These are user visible, so we want them in local times so that - // they correspond with dates/times that Explorer, etc shows - ConvertToLocalTime(ref arg0); - ConvertToLocalTime(ref arg1); + // These are user visible, so we want them in local times so that + // they correspond with dates/times that Explorer, etc shows + ConvertToLocalTime(ref arg0); + ConvertToLocalTime(ref arg1); - string message = GetResourceString(resourceName); + string message = GetResourceString(resourceName); - _writer.WriteLine($"{Preamble()}{string.Format(message, arg0, arg1)} ({_fileName})"); - } + _writer.WriteLine($"{Preamble()}{string.Format(message, arg0, arg1)} ({_fileName})"); } + } - private void Write(LogLevel level, string resourceName, params object[] values) + private void Write(LogLevel level, string resourceName, params object[] values) + { + if (level <= Level) { - if (level <= Level) - { - // These are user visible, so we want them in local times so that - // they correspond with dates/times that Explorer, etc shows - ConvertToLocalTimes(values); + // These are user visible, so we want them in local times so that + // they correspond with dates/times that Explorer, etc shows + ConvertToLocalTimes(values); - string message = GetResourceString(resourceName); + string message = GetResourceString(resourceName); - _writer.WriteLine($"{Preamble()}{string.Format(message, values)} ({_fileName})"); - } + _writer.WriteLine($"{Preamble()}{string.Format(message, values)} ({_fileName})"); } + } - private void Write(LogLevel level, string resourceName) + private void Write(LogLevel level, string resourceName) + { + if (level <= Level) { - if (level <= Level) - { - string message = GetResourceString(resourceName); + string message = GetResourceString(resourceName); - _writer.WriteLine($"{Preamble()}{message} ({_fileName})"); - } + _writer.WriteLine($"{Preamble()}{message} ({_fileName})"); } + } - private void WriteLiteral(LogLevel level, string message) + private void WriteLiteral(LogLevel level, string message) + { + if (level <= Level) { - if (level <= Level) - { - _writer.WriteLine($"{Preamble()}{message} ({_fileName})"); - } + _writer.WriteLine($"{Preamble()}{message} ({_fileName})"); } + } - private static void ConvertToLocalTimes(object[] values) + private static void ConvertToLocalTimes(object[] values) + { + for (int i = 0; i < values.Length; i++) { - for (int i = 0; i < values.Length; i++) - { - ConvertToLocalTime(ref values[i]); - } + ConvertToLocalTime(ref values[i]); } + } - private static void ConvertToLocalTime(ref object value) + private static void ConvertToLocalTime(ref object value) + { + if (value is DateTime time) { - if (value is DateTime time) - { - value = time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"); - } + value = time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"); } + } - public void Minimal(string resourceName) => Write(LogLevel.Minimal, resourceName); - public void Minimal(string resourceName, params object[] values) => Write(LogLevel.Minimal, resourceName, values); - - public void Info(string resourceName) => Write(LogLevel.Info, resourceName); - public void Info(string resourceName, object arg0) => Write(LogLevel.Info, resourceName, arg0); - public void Info(string resourceName, object arg0, object arg1) => Write(LogLevel.Info, resourceName, arg0, arg1); - public void Info(string resourceName, params object[] values) => Write(LogLevel.Info, resourceName, values); - - public void Verbose(string resourceName) => Write(LogLevel.Verbose, resourceName); - public void Verbose(string resourceName, object arg0) => Write(LogLevel.Verbose, resourceName, arg0); - public void Verbose(string resourceName, params object[] values) => Write(LogLevel.Verbose, resourceName, values); - public void VerboseLiteral(string message) => WriteLiteral(LogLevel.Verbose, message); - - /// - /// Publishes the up-to-date failure via telemetry and the output window. - /// - /// A string that uniquely identifies the kind of failure. Must not contain any PII such as file system data. - /// The name of a resource string that clearly describes the reason for the failure. That string may contain PII, as this string is only displayed on screen. - /// Optional format arguments to be applied to the string resource with name . - /// , which may be returned directly in . - public bool Fail(string reason, string resourceName, params object[] values) - { - Assumes.NotNull(FileSystemOperations); - - _stopwatch.Stop(); - - // We may be indented when a failure is identified. Set the indent to zero so - // we always flush-align the failure message. - Indent = 0; - - // Minimal logging only includes failures. - Minimal(resourceName, values); - - _solutionBuildEventListener.NotifyProjectChecked( - upToDate: false, - buildAccelerationEnabled: FileSystemOperations.IsAccelerationEnabled, - result: FileSystemOperations.AccelerationResult, - configurationCount: _upToDateCheckConfiguredInput.ImplicitInputs.Length, - copyCount: 0, - fileCount: _timestampCache.Count, - waitTime: _waitTime, - checkTime: _stopwatch.Elapsed, - logLevel: Level); - - // Send telemetry. - _telemetryService?.PostProperties(TelemetryEventName.UpToDateCheckFail, - [ - (TelemetryPropertyName.UpToDateCheck.FailReason, reason), - (TelemetryPropertyName.UpToDateCheck.DurationMillis, _stopwatch.Elapsed.TotalMilliseconds), - (TelemetryPropertyName.UpToDateCheck.WaitDurationMillis, _waitTime.TotalMilliseconds), - (TelemetryPropertyName.UpToDateCheck.FileCount, _timestampCache.Count), - (TelemetryPropertyName.UpToDateCheck.ConfigurationCount, _upToDateCheckConfiguredInput.ImplicitInputs.Length), - (TelemetryPropertyName.UpToDateCheck.LogLevel, Level), - (TelemetryPropertyName.UpToDateCheck.Project, _projectGuid), - (TelemetryPropertyName.UpToDateCheck.CheckNumber, _checkNumber), - (TelemetryPropertyName.UpToDateCheck.IgnoreKinds, _ignoreKinds ?? ""), - (TelemetryPropertyName.UpToDateCheck.AccelerationResult, FileSystemOperations.AccelerationResult) - ]); - - // Remember the failure reason and description for use in IncrementalBuildFailureDetector. - // First, ensure times are converted to local time (in case logging is not enabled). - ConvertToLocalTimes(values); - FailureReason = reason; - FailureDescription = string.Format(GetResourceString(resourceName), values); - - return false; - } + public void Minimal(string resourceName) => Write(LogLevel.Minimal, resourceName); + public void Minimal(string resourceName, params object[] values) => Write(LogLevel.Minimal, resourceName, values); + + public void Info(string resourceName) => Write(LogLevel.Info, resourceName); + public void Info(string resourceName, object arg0) => Write(LogLevel.Info, resourceName, arg0); + public void Info(string resourceName, object arg0, object arg1) => Write(LogLevel.Info, resourceName, arg0, arg1); + public void Info(string resourceName, params object[] values) => Write(LogLevel.Info, resourceName, values); + + public void Verbose(string resourceName) => Write(LogLevel.Verbose, resourceName); + public void Verbose(string resourceName, object arg0) => Write(LogLevel.Verbose, resourceName, arg0); + public void Verbose(string resourceName, params object[] values) => Write(LogLevel.Verbose, resourceName, values); + public void VerboseLiteral(string message) => WriteLiteral(LogLevel.Verbose, message); + + /// + /// Publishes the up-to-date failure via telemetry and the output window. + /// + /// A string that uniquely identifies the kind of failure. Must not contain any PII such as file system data. + /// The name of a resource string that clearly describes the reason for the failure. That string may contain PII, as this string is only displayed on screen. + /// Optional format arguments to be applied to the string resource with name . + /// , which may be returned directly in . + public bool Fail(string reason, string resourceName, params object[] values) + { + Assumes.NotNull(FileSystemOperations); + + _stopwatch.Stop(); + + // We may be indented when a failure is identified. Set the indent to zero so + // we always flush-align the failure message. + Indent = 0; + + // Minimal logging only includes failures. + Minimal(resourceName, values); + + _solutionBuildEventListener.NotifyProjectChecked( + upToDate: false, + buildAccelerationEnabled: FileSystemOperations.IsAccelerationEnabled, + result: FileSystemOperations.AccelerationResult, + configurationCount: _upToDateCheckConfiguredInput.ImplicitInputs.Length, + copyCount: 0, + fileCount: _timestampCache.Count, + waitTime: _waitTime, + checkTime: _stopwatch.Elapsed, + logLevel: Level); + + // Send telemetry. + _telemetryService?.PostProperties(TelemetryEventName.UpToDateCheckFail, + [ + (TelemetryPropertyName.UpToDateCheck.FailReason, reason), + (TelemetryPropertyName.UpToDateCheck.DurationMillis, _stopwatch.Elapsed.TotalMilliseconds), + (TelemetryPropertyName.UpToDateCheck.WaitDurationMillis, _waitTime.TotalMilliseconds), + (TelemetryPropertyName.UpToDateCheck.FileCount, _timestampCache.Count), + (TelemetryPropertyName.UpToDateCheck.ConfigurationCount, _upToDateCheckConfiguredInput.ImplicitInputs.Length), + (TelemetryPropertyName.UpToDateCheck.LogLevel, Level), + (TelemetryPropertyName.UpToDateCheck.Project, _projectGuid), + (TelemetryPropertyName.UpToDateCheck.CheckNumber, _checkNumber), + (TelemetryPropertyName.UpToDateCheck.IgnoreKinds, _ignoreKinds ?? ""), + (TelemetryPropertyName.UpToDateCheck.AccelerationResult, FileSystemOperations.AccelerationResult) + ]); + + // Remember the failure reason and description for use in IncrementalBuildFailureDetector. + // First, ensure times are converted to local time (in case logging is not enabled). + ConvertToLocalTimes(values); + FailureReason = reason; + FailureDescription = string.Format(GetResourceString(resourceName), values); + + return false; + } - /// - /// Publishes that the project is up-to-date via telemetry and the output window. - /// - public void UpToDate(int copyCount) + /// + /// Publishes that the project is up-to-date via telemetry and the output window. + /// + public void UpToDate(int copyCount) + { + Assumes.Null(FailureReason); + Assumes.Null(FailureDescription); + Assumes.NotNull(FileSystemOperations); + + _stopwatch.Stop(); + + _solutionBuildEventListener.NotifyProjectChecked( + upToDate: true, + buildAccelerationEnabled: FileSystemOperations.IsAccelerationEnabled, + result: FileSystemOperations.AccelerationResult, + copyCount: copyCount, + configurationCount: _upToDateCheckConfiguredInput.ImplicitInputs.Length, + fileCount: _timestampCache.Count, + waitTime: _waitTime, + checkTime: _stopwatch.Elapsed, + logLevel: Level); + + // Send telemetry. + _telemetryService?.PostProperties(TelemetryEventName.UpToDateCheckSuccess, + [ + (TelemetryPropertyName.UpToDateCheck.DurationMillis, _stopwatch.Elapsed.TotalMilliseconds), + (TelemetryPropertyName.UpToDateCheck.WaitDurationMillis, _waitTime.TotalMilliseconds), + (TelemetryPropertyName.UpToDateCheck.FileCount, _timestampCache.Count), + (TelemetryPropertyName.UpToDateCheck.ConfigurationCount, _upToDateCheckConfiguredInput.ImplicitInputs.Length), + (TelemetryPropertyName.UpToDateCheck.LogLevel, Level), + (TelemetryPropertyName.UpToDateCheck.Project, _projectGuid), + (TelemetryPropertyName.UpToDateCheck.CheckNumber, _checkNumber), + (TelemetryPropertyName.UpToDateCheck.IgnoreKinds, _ignoreKinds ?? ""), + (TelemetryPropertyName.UpToDateCheck.AccelerationResult, FileSystemOperations.AccelerationResult), + (TelemetryPropertyName.UpToDateCheck.AcceleratedCopyCount, copyCount) + ]); + + Info(nameof(VSResources.FUTD_UpToDate)); + + System.Diagnostics.Debug.Assert(_checkers.Any(check => ReferenceEquals(check.Value, _check)), "Expected the current IBuildUpToDateCheckProvider to be present in the set of known checks for this configured project"); + + if (_checkers.Count > 1 && Level >= LogLevel.Info) { - Assumes.Null(FailureReason); - Assumes.Null(FailureDescription); - Assumes.NotNull(FileSystemOperations); - - _stopwatch.Stop(); - - _solutionBuildEventListener.NotifyProjectChecked( - upToDate: true, - buildAccelerationEnabled: FileSystemOperations.IsAccelerationEnabled, - result: FileSystemOperations.AccelerationResult, - copyCount: copyCount, - configurationCount: _upToDateCheckConfiguredInput.ImplicitInputs.Length, - fileCount: _timestampCache.Count, - waitTime: _waitTime, - checkTime: _stopwatch.Elapsed, - logLevel: Level); - - // Send telemetry. - _telemetryService?.PostProperties(TelemetryEventName.UpToDateCheckSuccess, - [ - (TelemetryPropertyName.UpToDateCheck.DurationMillis, _stopwatch.Elapsed.TotalMilliseconds), - (TelemetryPropertyName.UpToDateCheck.WaitDurationMillis, _waitTime.TotalMilliseconds), - (TelemetryPropertyName.UpToDateCheck.FileCount, _timestampCache.Count), - (TelemetryPropertyName.UpToDateCheck.ConfigurationCount, _upToDateCheckConfiguredInput.ImplicitInputs.Length), - (TelemetryPropertyName.UpToDateCheck.LogLevel, Level), - (TelemetryPropertyName.UpToDateCheck.Project, _projectGuid), - (TelemetryPropertyName.UpToDateCheck.CheckNumber, _checkNumber), - (TelemetryPropertyName.UpToDateCheck.IgnoreKinds, _ignoreKinds ?? ""), - (TelemetryPropertyName.UpToDateCheck.AccelerationResult, FileSystemOperations.AccelerationResult), - (TelemetryPropertyName.UpToDateCheck.AcceleratedCopyCount, copyCount) - ]); - - Info(nameof(VSResources.FUTD_UpToDate)); - - System.Diagnostics.Debug.Assert(_checkers.Any(check => ReferenceEquals(check.Value, _check)), "Expected the current IBuildUpToDateCheckProvider to be present in the set of known checks for this configured project"); - - if (_checkers.Count > 1 && Level >= LogLevel.Info) + // There's more than one IBuildUpToDateCheckProvider for this configured project, so we are + // not the only up-to-date check. Other checks may also declare this project as out-of-date. + // When that happens, someone looking at the logs could be confused. The logs would say + // "Project is up to date", followed immediately by details of a build (assuming the other + // check doesn't log anything). To improve this situation, we log some information about + // other checks we detect that apply to the project, so that someone investigating this + // situation is not confused and knows where to move their investigation. + Info(nameof(VSResources.OtherUpToDateCheckProvidersPresent)); + + foreach (Lazy check in _checkers) { - // There's more than one IBuildUpToDateCheckProvider for this configured project, so we are - // not the only up-to-date check. Other checks may also declare this project as out-of-date. - // When that happens, someone looking at the logs could be confused. The logs would say - // "Project is up to date", followed immediately by details of a build (assuming the other - // check doesn't log anything). To improve this situation, we log some information about - // other checks we detect that apply to the project, so that someone investigating this - // situation is not confused and knows where to move their investigation. - Info(nameof(VSResources.OtherUpToDateCheckProvidersPresent)); - - foreach (Lazy check in _checkers) + if (!ReferenceEquals(check.Value, _check)) { - if (!ReferenceEquals(check.Value, _check)) - { - Info(nameof(VSResources.OtherUpToDateCheckProviderInfo_1), check.Value.GetType()); - } + Info(nameof(VSResources.OtherUpToDateCheckProviderInfo_1), check.Value.GetType()); } } } + } - public readonly struct Scope : IDisposable - { - private readonly Log _log; + public readonly struct Scope : IDisposable + { + private readonly Log _log; - public Scope(Log log) - { - _log = log; - _log.Indent++; - } + public Scope(Log log) + { + _log = log; + _log.Indent++; + } - public void Dispose() - { - _log.Indent--; - } + public void Dispose() + { + _log.Indent--; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs index f97a3ba176..f11a1241e8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.Subscription.cs @@ -3,246 +3,245 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +internal sealed partial class BuildUpToDateCheck { - internal sealed partial class BuildUpToDateCheck + /// + /// Contains and tracks state related to a lifetime instance of . + /// + /// + /// + /// As the parent is an , it may have multiple lifetimes. + /// This class contains all the state associated with such a lifetime: its Dataflow subscription, tracking the first value to arrive, + /// and the instance. + /// + /// + /// Destruction of the Dataflow subscription happens when the parent component is disposed or unloaded. + /// + /// + private sealed class Subscription : ISubscription { + private readonly IUpToDateCheckConfiguredInputDataSource _inputDataSource; + /// - /// Contains and tracks state related to a lifetime instance of . + /// The active configured project. For a multi-targeting project, there will be only one instance of this + /// subscription, and this field will be the active configuration (usually the first). /// - /// - /// - /// As the parent is an , it may have multiple lifetimes. - /// This class contains all the state associated with such a lifetime: its Dataflow subscription, tracking the first value to arrive, - /// and the instance. - /// - /// - /// Destruction of the Dataflow subscription happens when the parent component is disposed or unloaded. - /// - /// - private sealed class Subscription : ISubscription - { - private readonly IUpToDateCheckConfiguredInputDataSource _inputDataSource; + private readonly ConfiguredProject _configuredProject; - /// - /// The active configured project. For a multi-targeting project, there will be only one instance of this - /// subscription, and this field will be the active configuration (usually the first). - /// - private readonly ConfiguredProject _configuredProject; + /// + /// Used to synchronise updates to and . + /// + private readonly object _lock = new(); - /// - /// Used to synchronise updates to and . - /// - private readonly object _lock = new(); + private readonly IUpToDateCheckHost _host; - private readonly IUpToDateCheckHost _host; + private readonly IUpToDateCheckStatePersistence _persistence; - private readonly IUpToDateCheckStatePersistence _persistence; + /// + /// Prevent overlapping requests. + /// + private readonly ReentrantSemaphore _semaphore; - /// - /// Prevent overlapping requests. - /// - private readonly ReentrantSemaphore _semaphore; + private int _disposed; - private int _disposed; + /// + /// Lazily constructed Dataflow subscription. Set back to in . + /// + private IDisposable? _link; - /// - /// Lazily constructed Dataflow subscription. Set back to in . - /// - private IDisposable? _link; + private ImmutableArray _lastCheckedConfigurations = ImmutableArray.Empty; - private ImmutableArray _lastCheckedConfigurations = ImmutableArray.Empty; + /// + /// Cancelled when this instance is disposed. + /// + private readonly CancellationTokenSource _disposeTokenSource = new(); - /// - /// Cancelled when this instance is disposed. - /// - private readonly CancellationTokenSource _disposeTokenSource = new(); + public Subscription(IUpToDateCheckConfiguredInputDataSource inputDataSource, ConfiguredProject configuredProject, IUpToDateCheckHost host, IUpToDateCheckStatePersistence persistence) + { + Requires.NotNull(inputDataSource); + Requires.NotNull(configuredProject); + + _inputDataSource = inputDataSource; + _configuredProject = configuredProject; + _host = host; + _persistence = persistence; + + _semaphore = ReentrantSemaphore.Create( + initialCount: 1, + joinableTaskContext: configuredProject.UnconfiguredProject.Services.ThreadingPolicy.JoinableTaskContext.Context, + mode: ReentrantSemaphore.ReentrancyMode.NotAllowed); + } - public Subscription(IUpToDateCheckConfiguredInputDataSource inputDataSource, ConfiguredProject configuredProject, IUpToDateCheckHost host, IUpToDateCheckStatePersistence persistence) - { - Requires.NotNull(inputDataSource); - Requires.NotNull(configuredProject); - - _inputDataSource = inputDataSource; - _configuredProject = configuredProject; - _host = host; - _persistence = persistence; - - _semaphore = ReentrantSemaphore.Create( - initialCount: 1, - joinableTaskContext: configuredProject.UnconfiguredProject.Services.ThreadingPolicy.JoinableTaskContext.Context, - mode: ReentrantSemaphore.ReentrancyMode.NotAllowed); - } + public async Task RunAsync( + Func CheckedConfigurations)>> func, + CancellationToken cancellationToken) + { + using var cts = CancellationTokenExtensions.CombineWith(cancellationToken, _disposeTokenSource.Token); - public async Task RunAsync( - Func CheckedConfigurations)>> func, - CancellationToken cancellationToken) - { - using var cts = CancellationTokenExtensions.CombineWith(cancellationToken, _disposeTokenSource.Token); + CancellationToken token = cts.Token; - CancellationToken token = cts.Token; + // Throws if the subscription has been disposed, or the caller's token cancelled. + token.ThrowIfCancellationRequested(); - // Throws if the subscription has been disposed, or the caller's token cancelled. - token.ThrowIfCancellationRequested(); + if (!await _host.HasDesignTimeBuildsAsync(token)) + { + // Design time builds aren't available in the host. This can happen when running in command line mode, for example. + // In such a case we will not have the data we need. Presume the project is not up-to-date. + return false; + } - if (!await _host.HasDesignTimeBuildsAsync(token)) - { - // Design time builds aren't available in the host. This can happen when running in command line mode, for example. - // In such a case we will not have the data we need. Presume the project is not up-to-date. - return false; - } + EnsureInitialized(); - EnsureInitialized(); + if (_disposed != 0) + { + // We have been disposed + return false; + } - if (_disposed != 0) - { - // We have been disposed - return false; - } + token.ThrowIfCancellationRequested(); - token.ThrowIfCancellationRequested(); + // Prevent overlapping requests + return await _semaphore.ExecuteAsync( + async () => + { + IProjectVersionedValue state; - // Prevent overlapping requests - return await _semaphore.ExecuteAsync( - async () => + using (_inputDataSource.Join()) { - IProjectVersionedValue state; + // Wait for our state to be up to date with that of the project + state = await _inputDataSource.SourceBlock.GetLatestVersionAsync( + _configuredProject, + cancellationToken: token); + } - using (_inputDataSource.Join()) - { - // Wait for our state to be up to date with that of the project - state = await _inputDataSource.SourceBlock.GetLatestVersionAsync( - _configuredProject, - cancellationToken: token); - } + (bool upToDate, _lastCheckedConfigurations) = await func(state.Value, _persistence, token); - (bool upToDate, _lastCheckedConfigurations) = await func(state.Value, _persistence, token); + return upToDate; + }, + token); + } - return upToDate; - }, - token); - } + public async Task UpdateLastSuccessfulBuildStartTimeUtcAsync(DateTime lastSuccessfulBuildStartTimeUtc, bool isRebuild) + { + IEnumerable configurations; - public async Task UpdateLastSuccessfulBuildStartTimeUtcAsync(DateTime lastSuccessfulBuildStartTimeUtc, bool isRebuild) + if (isRebuild) { - IEnumerable configurations; - - if (isRebuild) - { - // During a rebuild the fast up-to-date check is not called, so we cannot rely upon - // _lastCheckedConfigurations having been updated. We currently only support building - // a subset of configurations for build, not for rebuild, so replace _lastCheckedConfigurations - // with the set of active configurations, as that is what was built. - IConfigurationGroup activeConfiguredProjects = await _configuredProject.UnconfiguredProject.Services.ActiveConfigurationGroupService.GetActiveLoadedConfiguredProjectGroupAsync(); - configurations = activeConfiguredProjects.Select(p => p.ProjectConfiguration); - } - else - { - configurations = _lastCheckedConfigurations; - } + // During a rebuild the fast up-to-date check is not called, so we cannot rely upon + // _lastCheckedConfigurations having been updated. We currently only support building + // a subset of configurations for build, not for rebuild, so replace _lastCheckedConfigurations + // with the set of active configurations, as that is what was built. + IConfigurationGroup activeConfiguredProjects = await _configuredProject.UnconfiguredProject.Services.ActiveConfigurationGroupService.GetActiveLoadedConfiguredProjectGroupAsync(); + configurations = activeConfiguredProjects.Select(p => p.ProjectConfiguration); + } + else + { + configurations = _lastCheckedConfigurations; + } - await Task.WhenAll(configurations.Select(StoreConfigurationAsync)); + await Task.WhenAll(configurations.Select(StoreConfigurationAsync)); - Task StoreConfigurationAsync(ProjectConfiguration configuration) - { - return _persistence.StoreLastSuccessfulBuildStateAsync( - _configuredProject.UnconfiguredProject.FullPath, - configuration.Dimensions, - lastSuccessfulBuildStartTimeUtc, - CancellationToken.None); - } + Task StoreConfigurationAsync(ProjectConfiguration configuration) + { + return _persistence.StoreLastSuccessfulBuildStateAsync( + _configuredProject.UnconfiguredProject.FullPath, + configuration.Dimensions, + lastSuccessfulBuildStartTimeUtc, + CancellationToken.None); } + } - public void EnsureInitialized() + public void EnsureInitialized() + { + if (_link is not null || _disposed != 0) { - if (_link is not null || _disposed != 0) - { - // Already initialized or disposed - return; - } + // Already initialized or disposed + return; + } - // Double check within lock - lock (_lock) + // Double check within lock + lock (_lock) + { + if (_link is null && _disposed == 0) { - if (_link is null && _disposed == 0) - { - // Link to a null target so that data will flow. The null target drops all values. - // When we need a value we use GetLatestVersionAsync to ensure the UpToDateCheckConfiguredInput - // is in sync with the current configured project. + // Link to a null target so that data will flow. The null target drops all values. + // When we need a value we use GetLatestVersionAsync to ensure the UpToDateCheckConfiguredInput + // is in sync with the current configured project. - ITargetBlock> target - = DataflowBlock.NullTarget>(); + ITargetBlock> target + = DataflowBlock.NullTarget>(); - _link = _inputDataSource.SourceBlock.LinkTo(target, DataflowOption.PropagateCompletion); - } + _link = _inputDataSource.SourceBlock.LinkTo(target, DataflowOption.PropagateCompletion); } } + } - /// - /// Cancel any ongoing query and release Dataflow subscription. - /// - public void Dispose() + /// + /// Cancel any ongoing query and release Dataflow subscription. + /// + public void Dispose() + { + if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0) { - if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0) - { - // Already disposed - return; - } + // Already disposed + return; + } - lock (_lock) - { - _link?.Dispose(); - _link = null; + lock (_lock) + { + _link?.Dispose(); + _link = null; - _disposeTokenSource.Cancel(); - _disposeTokenSource.Dispose(); - } + _disposeTokenSource.Cancel(); + _disposeTokenSource.Dispose(); } } + } + /// + /// Holds state in the active configuration. + /// In a multi-targeting project (or one with any extra dimensions) this will be the first, in evaluation order. + /// Per-target state is modelled in individual UpToDateCheckConfiguredInput values. + /// + internal interface ISubscription : IDisposable + { /// - /// Holds state in the active configuration. - /// In a multi-targeting project (or one with any extra dimensions) this will be the first, in evaluation order. - /// Per-target state is modelled in individual UpToDateCheckConfiguredInput values. + /// Ensures the subscription has been initialized. Has no effect if the object has already been disposed. /// - internal interface ISubscription : IDisposable - { - /// - /// Ensures the subscription has been initialized. Has no effect if the object has already been disposed. - /// - void EnsureInitialized(); - - /// - /// Notifies the subscription of the most recent time a successful build started. - /// - /// - /// Implementation must ensure only the most recently built configurations are updated. - /// The most recently built configurations should be obtained from the return value of the - /// function passed to . - /// - Task UpdateLastSuccessfulBuildStartTimeUtcAsync(DateTime lastSuccessfulBuildStartTimeUtc, bool isRebuild); - - /// - /// Calls to determine whether the project is up-to-date or not. - /// - /// - /// A function that accepts three arguments: - /// - /// The current project state as an instance of . - /// An object that stores persisted data, such as the last successful build start time. - /// A cancellation token that may indicate a loss of interest in the result. - /// - /// And returns a tuple of: - /// - /// A boolean indicating whether the project is up-to-date or not. - /// The list of project configurations actually checked. - /// - /// - /// Indicates a loss of interest in the result. - /// if the project is up-to-date, otherwise . - Task RunAsync( - Func CheckedConfigurations)>> func, - CancellationToken cancellationToken); - } + void EnsureInitialized(); + + /// + /// Notifies the subscription of the most recent time a successful build started. + /// + /// + /// Implementation must ensure only the most recently built configurations are updated. + /// The most recently built configurations should be obtained from the return value of the + /// function passed to . + /// + Task UpdateLastSuccessfulBuildStartTimeUtcAsync(DateTime lastSuccessfulBuildStartTimeUtc, bool isRebuild); + + /// + /// Calls to determine whether the project is up-to-date or not. + /// + /// + /// A function that accepts three arguments: + /// + /// The current project state as an instance of . + /// An object that stores persisted data, such as the last successful build start time. + /// A cancellation token that may indicate a loss of interest in the result. + /// + /// And returns a tuple of: + /// + /// A boolean indicating whether the project is up-to-date or not. + /// The list of project configurations actually checked. + /// + /// + /// Indicates a loss of interest in the result. + /// if the project is up-to-date, otherwise . + Task RunAsync( + Func CheckedConfigurations)>> func, + CancellationToken cancellationToken); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.TimestampCache.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.TimestampCache.cs index 40c566ef8f..2b2f028ea7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.TimestampCache.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.TimestampCache.cs @@ -2,46 +2,45 @@ using Microsoft.VisualStudio.IO; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +internal sealed partial class BuildUpToDateCheck { - internal sealed partial class BuildUpToDateCheck + internal readonly struct TimestampCache { - internal readonly struct TimestampCache + private readonly Dictionary _timestampCache = new(StringComparers.Paths); + private readonly IFileSystem _fileSystem; + + public TimestampCache(IFileSystem fileSystem) { - private readonly Dictionary _timestampCache = new(StringComparers.Paths); - private readonly IFileSystem _fileSystem; + _fileSystem = fileSystem; + } - public TimestampCache(IFileSystem fileSystem) - { - _fileSystem = fileSystem; - } + /// + /// Gets the number of unique files added to this cache. + /// + public int Count => _timestampCache.Count; - /// - /// Gets the number of unique files added to this cache. - /// - public int Count => _timestampCache.Count; - - /// - /// Attempts to get the last write time of the specified file. - /// If the value already exists in this cache, return the cached value. - /// Otherwise, query the filesystem, cache the result, then return it. - /// If the file is not found, return . - /// - public DateTime? GetTimestampUtc(string path) + /// + /// Attempts to get the last write time of the specified file. + /// If the value already exists in this cache, return the cached value. + /// Otherwise, query the filesystem, cache the result, then return it. + /// If the file is not found, return . + /// + public DateTime? GetTimestampUtc(string path) + { + if (!_timestampCache.TryGetValue(path, out DateTime time)) { - if (!_timestampCache.TryGetValue(path, out DateTime time)) + if (!_fileSystem.TryGetLastFileWriteTimeUtc(path, out DateTime? newTime)) { - if (!_fileSystem.TryGetLastFileWriteTimeUtc(path, out DateTime? newTime)) - { - return null; - } - - time = newTime.Value; - _timestampCache[path] = time; + return null; } - return time; + time = newTime.Value; + _timestampCache[path] = time; } + + return time; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.cs index f02aada9f3..8413a1e88f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/BuildUpToDateCheck.cs @@ -7,1237 +7,1236 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +[AppliesTo(AppliesToExpression)] +[Export(typeof(IBuildUpToDateCheckProvider))] +[Export(typeof(IBuildUpToDateCheckValidator))] +[Export(typeof(IProjectBuildEventListener))] +[Export(typeof(IActiveConfigurationComponent))] +[ExportMetadata("BeforeDrainCriticalTasks", true)] +internal sealed partial class BuildUpToDateCheck + : IBuildUpToDateCheckProvider2, + IBuildUpToDateCheckValidator, + IProjectBuildEventListener, + IActiveConfigurationComponent, + IDisposable { - [AppliesTo(AppliesToExpression)] - [Export(typeof(IBuildUpToDateCheckProvider))] - [Export(typeof(IBuildUpToDateCheckValidator))] - [Export(typeof(IProjectBuildEventListener))] - [Export(typeof(IActiveConfigurationComponent))] - [ExportMetadata("BeforeDrainCriticalTasks", true)] - internal sealed partial class BuildUpToDateCheck - : IBuildUpToDateCheckProvider2, - IBuildUpToDateCheckValidator, - IProjectBuildEventListener, - IActiveConfigurationComponent, - IDisposable + // TODO: Remove the dependency on RunningInVisualStudio when IBuildUpToDateCheckProvider2 is supported in the project system server. + internal const string AppliesToExpression = $"{ProjectCapability.DotNet} + !{ProjectCapabilities.SharedAssetsProject} + {ProjectCapabilities.RunningInVisualStudio}"; + + internal const string FastUpToDateCheckIgnoresKindsGlobalPropertyName = "FastUpToDateCheckIgnoresKinds"; + internal const string TargetFrameworkGlobalPropertyName = "TargetFramework"; + + // This analyzer fires for comparisons against the following constants. Disable it in this file. + #pragma warning disable CA1820 // Test for empty strings using string length + + internal const string DefaultSetName = ""; + internal const string DefaultKindName = ""; + + internal static readonly StringComparer SetNameComparer = StringComparers.ItemNames; + internal static readonly StringComparer KindNameComparer = StringComparers.ItemNames; + + private readonly ISolutionBuildContextProvider _solutionBuildContextProvider; + private readonly ISolutionBuildEventListener _solutionBuildEventListener; + private readonly IUpToDateCheckConfiguredInputDataSource _inputDataSource; + private readonly IProjectSystemOptions _projectSystemOptions; + private readonly ConfiguredProject _configuredProject; + private readonly IUpToDateCheckStatePersistence _persistence; + private readonly IProjectAsynchronousTasksService _tasksService; + private readonly ITelemetryService _telemetryService; + private readonly IFileSystem _fileSystem; + private readonly ISafeProjectGuidService _guidService; + private readonly IUpToDateCheckHost _upToDateCheckHost; + private readonly ICopyItemAggregator _copyItemAggregator; + + private IImmutableDictionary _lastGlobalProperties = ImmutableStringDictionary.EmptyOrdinal; + private string? _lastFailureReason; + private string? _lastFailureDescription; + private DateTime _lastBuildStartTimeUtc = DateTime.MinValue; + + private ISubscription _subscription; + private int _isDisposed; + private int _checkNumber; + private IEnumerable? _lastCopyTargetsFromThisProject; + + /// + /// Gets the set of up-to-date checkers that apply to this project. + /// + /// + /// We can use this information in log output if multiple up-to-date checks are present, so that users can + /// observe that even if we find the project up-to-date, another check may not, which will lead to the + /// project being built. If other checkers exist, we log that fact and include their type names, which + /// can avoid confusion and assist with further debugging. + /// + [ImportMany] + internal OrderPrecedenceImportCollection UpToDateCheckers { get; } + + [ImportingConstructor] + public BuildUpToDateCheck( + ISolutionBuildContextProvider solutionBuildContextProvider, + ISolutionBuildEventListener solutionBuildEventListener, + IUpToDateCheckConfiguredInputDataSource inputDataSource, + IProjectSystemOptions projectSystemOptions, + ConfiguredProject configuredProject, + IUpToDateCheckStatePersistence persistence, + [Import(ExportContractNames.Scopes.ConfiguredProject)] IProjectAsynchronousTasksService tasksService, + ITelemetryService telemetryService, + IFileSystem fileSystem, + ISafeProjectGuidService guidService, + IUpToDateCheckHost upToDateCheckHost, + ICopyItemAggregator copyItemAggregator) { - // TODO: Remove the dependency on RunningInVisualStudio when IBuildUpToDateCheckProvider2 is supported in the project system server. - internal const string AppliesToExpression = $"{ProjectCapability.DotNet} + !{ProjectCapabilities.SharedAssetsProject} + {ProjectCapabilities.RunningInVisualStudio}"; - - internal const string FastUpToDateCheckIgnoresKindsGlobalPropertyName = "FastUpToDateCheckIgnoresKinds"; - internal const string TargetFrameworkGlobalPropertyName = "TargetFramework"; - - // This analyzer fires for comparisons against the following constants. Disable it in this file. - #pragma warning disable CA1820 // Test for empty strings using string length - - internal const string DefaultSetName = ""; - internal const string DefaultKindName = ""; - - internal static readonly StringComparer SetNameComparer = StringComparers.ItemNames; - internal static readonly StringComparer KindNameComparer = StringComparers.ItemNames; - - private readonly ISolutionBuildContextProvider _solutionBuildContextProvider; - private readonly ISolutionBuildEventListener _solutionBuildEventListener; - private readonly IUpToDateCheckConfiguredInputDataSource _inputDataSource; - private readonly IProjectSystemOptions _projectSystemOptions; - private readonly ConfiguredProject _configuredProject; - private readonly IUpToDateCheckStatePersistence _persistence; - private readonly IProjectAsynchronousTasksService _tasksService; - private readonly ITelemetryService _telemetryService; - private readonly IFileSystem _fileSystem; - private readonly ISafeProjectGuidService _guidService; - private readonly IUpToDateCheckHost _upToDateCheckHost; - private readonly ICopyItemAggregator _copyItemAggregator; - - private IImmutableDictionary _lastGlobalProperties = ImmutableStringDictionary.EmptyOrdinal; - private string? _lastFailureReason; - private string? _lastFailureDescription; - private DateTime _lastBuildStartTimeUtc = DateTime.MinValue; - - private ISubscription _subscription; - private int _isDisposed; - private int _checkNumber; - private IEnumerable? _lastCopyTargetsFromThisProject; - - /// - /// Gets the set of up-to-date checkers that apply to this project. - /// - /// - /// We can use this information in log output if multiple up-to-date checks are present, so that users can - /// observe that even if we find the project up-to-date, another check may not, which will lead to the - /// project being built. If other checkers exist, we log that fact and include their type names, which - /// can avoid confusion and assist with further debugging. - /// - [ImportMany] - internal OrderPrecedenceImportCollection UpToDateCheckers { get; } - - [ImportingConstructor] - public BuildUpToDateCheck( - ISolutionBuildContextProvider solutionBuildContextProvider, - ISolutionBuildEventListener solutionBuildEventListener, - IUpToDateCheckConfiguredInputDataSource inputDataSource, - IProjectSystemOptions projectSystemOptions, - ConfiguredProject configuredProject, - IUpToDateCheckStatePersistence persistence, - [Import(ExportContractNames.Scopes.ConfiguredProject)] IProjectAsynchronousTasksService tasksService, - ITelemetryService telemetryService, - IFileSystem fileSystem, - ISafeProjectGuidService guidService, - IUpToDateCheckHost upToDateCheckHost, - ICopyItemAggregator copyItemAggregator) - { - _solutionBuildContextProvider = solutionBuildContextProvider; - _solutionBuildEventListener = solutionBuildEventListener; - _inputDataSource = inputDataSource; - _projectSystemOptions = projectSystemOptions; - _configuredProject = configuredProject; - _persistence = persistence; - _tasksService = tasksService; - _telemetryService = telemetryService; - _fileSystem = fileSystem; - _guidService = guidService; - _upToDateCheckHost = upToDateCheckHost; - _copyItemAggregator = copyItemAggregator; - - UpToDateCheckers = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: configuredProject); - - _subscription = new Subscription(inputDataSource, configuredProject, upToDateCheckHost, persistence); - } + _solutionBuildContextProvider = solutionBuildContextProvider; + _solutionBuildEventListener = solutionBuildEventListener; + _inputDataSource = inputDataSource; + _projectSystemOptions = projectSystemOptions; + _configuredProject = configuredProject; + _persistence = persistence; + _tasksService = tasksService; + _telemetryService = telemetryService; + _fileSystem = fileSystem; + _guidService = guidService; + _upToDateCheckHost = upToDateCheckHost; + _copyItemAggregator = copyItemAggregator; + + UpToDateCheckers = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: configuredProject); + + _subscription = new Subscription(inputDataSource, configuredProject, upToDateCheckHost, persistence); + } - public Task ActivateAsync() - { - _subscription.EnsureInitialized(); + public Task ActivateAsync() + { + _subscription.EnsureInitialized(); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task DeactivateAsync() - { - RecycleSubscription(); + public Task DeactivateAsync() + { + RecycleSubscription(); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public void Dispose() + public void Dispose() + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) != 0) { - if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) != 0) - { - return; - } - - RecycleSubscription(); + return; } - private void RecycleSubscription() + RecycleSubscription(); + } + + private void RecycleSubscription() + { + ISubscription subscription = Interlocked.Exchange(ref _subscription, new Subscription(_inputDataSource, _configuredProject, _upToDateCheckHost, _persistence)); + + subscription.Dispose(); + } + + private bool CheckGlobalConditions(Log log, DateTime? lastSuccessfulBuildStartTimeUtc, bool validateFirstRun, UpToDateCheckImplicitConfiguredInput state) + { + if (!_tasksService.IsTaskQueueEmpty(ProjectCriticalOperation.Build)) { - ISubscription subscription = Interlocked.Exchange(ref _subscription, new Subscription(_inputDataSource, _configuredProject, _upToDateCheckHost, _persistence)); + return log.Fail("CriticalTasks", nameof(VSResources.FUTD_CriticalBuildTasksRunning)); + } - subscription.Dispose(); + if (validateFirstRun && lastSuccessfulBuildStartTimeUtc is null) + { + // We haven't observed a successful built yet. Therefore we don't know whether the set + // of input items we have actually built the outputs we observe on disk. It's possible + // that an input has been deleted since then. So we schedule a build. + // + // Despite the name, "FirstRun" can occur on the second run if the first build didn't + // complete correctly. The name is kept though as it allows easier correlation between + // older and newer data. + return log.Fail("FirstRun", nameof(VSResources.FUTD_FirstRun)); } - private bool CheckGlobalConditions(Log log, DateTime? lastSuccessfulBuildStartTimeUtc, bool validateFirstRun, UpToDateCheckImplicitConfiguredInput state) + if (lastSuccessfulBuildStartTimeUtc < state.LastItemsChangedAtUtc) { - if (!_tasksService.IsTaskQueueEmpty(ProjectCriticalOperation.Build)) - { - return log.Fail("CriticalTasks", nameof(VSResources.FUTD_CriticalBuildTasksRunning)); - } + Assumes.NotNull(lastSuccessfulBuildStartTimeUtc); + Assumes.NotNull(state.LastItemsChangedAtUtc); - if (validateFirstRun && lastSuccessfulBuildStartTimeUtc is null) - { - // We haven't observed a successful built yet. Therefore we don't know whether the set - // of input items we have actually built the outputs we observe on disk. It's possible - // that an input has been deleted since then. So we schedule a build. - // - // Despite the name, "FirstRun" can occur on the second run if the first build didn't - // complete correctly. The name is kept though as it allows easier correlation between - // older and newer data. - return log.Fail("FirstRun", nameof(VSResources.FUTD_FirstRun)); - } + log.Fail("ProjectItemsChangedSinceLastSuccessfulBuildStart", nameof(VSResources.FUTD_SetOfItemsChangedMoreRecentlyThanOutput_2), state.LastItemsChangedAtUtc, lastSuccessfulBuildStartTimeUtc); - if (lastSuccessfulBuildStartTimeUtc < state.LastItemsChangedAtUtc) + if (log.Level >= LogLevel.Info) { - Assumes.NotNull(lastSuccessfulBuildStartTimeUtc); - Assumes.NotNull(state.LastItemsChangedAtUtc); - - log.Fail("ProjectItemsChangedSinceLastSuccessfulBuildStart", nameof(VSResources.FUTD_SetOfItemsChangedMoreRecentlyThanOutput_2), state.LastItemsChangedAtUtc, lastSuccessfulBuildStartTimeUtc); + using Log.Scope _ = log.IndentScope(); - if (log.Level >= LogLevel.Info) + if (state.LastItemChanges.Length == 0) { - using Log.Scope _ = log.IndentScope(); - - if (state.LastItemChanges.Length == 0) - { - log.Info(nameof(VSResources.FUTD_SetOfChangedItemsIsEmpty)); - } - else + log.Info(nameof(VSResources.FUTD_SetOfChangedItemsIsEmpty)); + } + else + { + foreach ((bool isAdd, string itemType, string item) in state.LastItemChanges.OrderBy(change => change.ItemType).ThenBy(change => change.Item)) { - foreach ((bool isAdd, string itemType, string item) in state.LastItemChanges.OrderBy(change => change.ItemType).ThenBy(change => change.Item)) - { - log.Info(isAdd ? nameof(VSResources.FUTD_ChangedItemsAddition_2) : nameof(VSResources.FUTD_ChangedItemsRemoval_2), itemType, item); - } + log.Info(isAdd ? nameof(VSResources.FUTD_ChangedItemsAddition_2) : nameof(VSResources.FUTD_ChangedItemsRemoval_2), itemType, item); } } - - return false; } - return true; + return false; } - private bool CheckInputsAndOutputs(Log log, DateTime? lastSuccessfulBuildStartTimeUtc, in TimestampCache timestampCache, UpToDateCheckImplicitConfiguredInput state, HashSet? ignoreKinds, CancellationToken token) - { - // UpToDateCheckInput/Output/Built items have optional 'Set' metadata that determine whether they - // are treated separately or not. If omitted, such inputs/outputs are included in the default set, - // which also includes other items such as project files, compilation items, analyzer references, etc. + return true; + } + + private bool CheckInputsAndOutputs(Log log, DateTime? lastSuccessfulBuildStartTimeUtc, in TimestampCache timestampCache, UpToDateCheckImplicitConfiguredInput state, HashSet? ignoreKinds, CancellationToken token) + { + // UpToDateCheckInput/Output/Built items have optional 'Set' metadata that determine whether they + // are treated separately or not. If omitted, such inputs/outputs are included in the default set, + // which also includes other items such as project files, compilation items, analyzer references, etc. - log.Info(nameof(VSResources.FUTD_ComparingInputOutputTimestamps)); + log.Info(nameof(VSResources.FUTD_ComparingInputOutputTimestamps)); - using (log.IndentScope()) + using (log.IndentScope()) + { + // First, validate the relationship between inputs and outputs within the default set. + if (!CheckInputsAndOutputs(CollectDefaultInputs(), CollectDefaultOutputs(), timestampCache, DefaultSetName)) { - // First, validate the relationship between inputs and outputs within the default set. - if (!CheckInputsAndOutputs(CollectDefaultInputs(), CollectDefaultOutputs(), timestampCache, DefaultSetName)) - { - return false; - } + return false; } + } - // Second, validate the relationships between inputs and outputs in specific sets, if any. - foreach (string setName in state.SetNames) + // Second, validate the relationships between inputs and outputs in specific sets, if any. + foreach (string setName in state.SetNames) + { + if (log.Level >= LogLevel.Verbose) { - if (log.Level >= LogLevel.Verbose) - { - log.Verbose(nameof(VSResources.FUTD_ComparingInputOutputTimestampsInSet_1), setName); - log.Indent++; - } - - if (!CheckInputsAndOutputs(CollectSetInputs(setName), CollectSetOutputs(setName), timestampCache, setName)) - { - return false; - } - - if (log.Level >= LogLevel.Verbose) - { - log.Indent--; - } + log.Verbose(nameof(VSResources.FUTD_ComparingInputOutputTimestampsInSet_1), setName); + log.Indent++; } - // Validation passed - return true; + if (!CheckInputsAndOutputs(CollectSetInputs(setName), CollectSetOutputs(setName), timestampCache, setName)) + { + return false; + } - bool CheckInputsAndOutputs(IEnumerable<(string Path, string? ItemType, bool IsRequired)> inputs, IEnumerable outputs, in TimestampCache timestampCache, string setName) + if (log.Level >= LogLevel.Verbose) { - // We assume there are fewer outputs than inputs, so perform a full scan of outputs to find the earliest. - // This increases the chance that we may return sooner in the case we are not up to date. - DateTime earliestOutputTime = DateTime.MaxValue; - string? earliestOutputPath = null; - bool hasOutput = false; + log.Indent--; + } + } - foreach (string output in outputs) - { - System.Diagnostics.Debug.Assert(Path.IsPathRooted(output), "Output path must be rooted", output); + // Validation passed + return true; - token.ThrowIfCancellationRequested(); + bool CheckInputsAndOutputs(IEnumerable<(string Path, string? ItemType, bool IsRequired)> inputs, IEnumerable outputs, in TimestampCache timestampCache, string setName) + { + // We assume there are fewer outputs than inputs, so perform a full scan of outputs to find the earliest. + // This increases the chance that we may return sooner in the case we are not up to date. + DateTime earliestOutputTime = DateTime.MaxValue; + string? earliestOutputPath = null; + bool hasOutput = false; - log.VerboseLiteral(output); + foreach (string output in outputs) + { + System.Diagnostics.Debug.Assert(Path.IsPathRooted(output), "Output path must be rooted", output); - DateTime? outputTime = timestampCache.GetTimestampUtc(output); + token.ThrowIfCancellationRequested(); - if (outputTime is null) - { - return log.Fail("OutputNotFound", nameof(VSResources.FUTD_OutputDoesNotExist_1), output); - } + log.VerboseLiteral(output); - if (outputTime < earliestOutputTime) - { - earliestOutputTime = outputTime.Value; - earliestOutputPath = output; - } + DateTime? outputTime = timestampCache.GetTimestampUtc(output); - hasOutput = true; + if (outputTime is null) + { + return log.Fail("OutputNotFound", nameof(VSResources.FUTD_OutputDoesNotExist_1), output); } - if (!hasOutput) + if (outputTime < earliestOutputTime) { - log.Info(setName == DefaultSetName ? nameof(VSResources.FUTD_NoBuildOutputDefined) : nameof(VSResources.FUTD_NoBuildOutputDefinedInSet_1), setName); - - return true; + earliestOutputTime = outputTime.Value; + earliestOutputPath = output; } - Assumes.NotNull(earliestOutputPath); - - (string Path, DateTime? Time)? latestInput = null; + hasOutput = true; + } - foreach ((string input, string? itemType, bool isRequired) in inputs) - { - System.Diagnostics.Debug.Assert(Path.IsPathRooted(input), "Output path must be rooted", input); + if (!hasOutput) + { + log.Info(setName == DefaultSetName ? nameof(VSResources.FUTD_NoBuildOutputDefined) : nameof(VSResources.FUTD_NoBuildOutputDefinedInSet_1), setName); - token.ThrowIfCancellationRequested(); + return true; + } - log.VerboseLiteral(input); + Assumes.NotNull(earliestOutputPath); - DateTime? inputTime = timestampCache.GetTimestampUtc(input); + (string Path, DateTime? Time)? latestInput = null; - if (inputTime is null) - { - if (isRequired) - { - return log.Fail("InputNotFound", itemType is null ? nameof(VSResources.FUTD_RequiredInputNotFound_1) : nameof(VSResources.FUTD_RequiredTypedInputNotFound_2), input, itemType ?? ""); - } - else - { - log.Verbose(itemType is null ? nameof(VSResources.FUTD_NonRequiredInputNotFound_1) : nameof(VSResources.FUTD_NonRequiredTypedInputNotFound_2), input, itemType ?? ""); - } - } + foreach ((string input, string? itemType, bool isRequired) in inputs) + { + System.Diagnostics.Debug.Assert(Path.IsPathRooted(input), "Output path must be rooted", input); - if (inputTime > earliestOutputTime) - { - return log.Fail("InputNewerThanEarliestOutput", itemType is null ? nameof(VSResources.FUTD_InputNewerThanOutput_4) : nameof(VSResources.FUTD_TypedInputNewerThanOutput_5), input, inputTime.Value, earliestOutputPath, earliestOutputTime, itemType ?? ""); - } + token.ThrowIfCancellationRequested(); - if (inputTime > lastSuccessfulBuildStartTimeUtc) - { - // Bypass this test if no check has yet been performed. We handle that in CheckGlobalConditions. - Assumes.NotNull(inputTime); - Assumes.NotNull(lastSuccessfulBuildStartTimeUtc); - return log.Fail("InputModifiedSinceLastSuccessfulBuildStart", itemType is null ? nameof(VSResources.FUTD_InputModifiedSinceLastSuccessfulBuildStart_3) : nameof(VSResources.FUTD_TypedInputModifiedSinceLastSuccessfulBuildStart_4), input, inputTime, lastSuccessfulBuildStartTimeUtc, itemType ?? ""); - } + log.VerboseLiteral(input); - if (latestInput is null || inputTime > latestInput.Value.Time) - { - latestInput = (input, inputTime); - } - } + DateTime? inputTime = timestampCache.GetTimestampUtc(input); - if (log.Level >= LogLevel.Info) + if (inputTime is null) { - if (latestInput is null) + if (isRequired) { - log.Info(setName == DefaultSetName ? nameof(VSResources.FUTD_NoInputsDefined) : nameof(VSResources.FUTD_NoInputsDefinedInSet_1), setName); + return log.Fail("InputNotFound", itemType is null ? nameof(VSResources.FUTD_RequiredInputNotFound_1) : nameof(VSResources.FUTD_RequiredTypedInputNotFound_2), input, itemType ?? ""); } else { - log.Info(setName == DefaultSetName ? nameof(VSResources.FUTD_NoInputsNewerThanEarliestOutput_4) : nameof(VSResources.FUTD_NoInputsNewerThanEarliestOutputInSet_5), earliestOutputPath, earliestOutputTime, latestInput.Value.Path, latestInput.Value.Time ?? (object)"null", setName); + log.Verbose(itemType is null ? nameof(VSResources.FUTD_NonRequiredInputNotFound_1) : nameof(VSResources.FUTD_NonRequiredTypedInputNotFound_2), input, itemType ?? ""); } } - return true; + if (inputTime > earliestOutputTime) + { + return log.Fail("InputNewerThanEarliestOutput", itemType is null ? nameof(VSResources.FUTD_InputNewerThanOutput_4) : nameof(VSResources.FUTD_TypedInputNewerThanOutput_5), input, inputTime.Value, earliestOutputPath, earliestOutputTime, itemType ?? ""); + } + + if (inputTime > lastSuccessfulBuildStartTimeUtc) + { + // Bypass this test if no check has yet been performed. We handle that in CheckGlobalConditions. + Assumes.NotNull(inputTime); + Assumes.NotNull(lastSuccessfulBuildStartTimeUtc); + return log.Fail("InputModifiedSinceLastSuccessfulBuildStart", itemType is null ? nameof(VSResources.FUTD_InputModifiedSinceLastSuccessfulBuildStart_3) : nameof(VSResources.FUTD_TypedInputModifiedSinceLastSuccessfulBuildStart_4), input, inputTime, lastSuccessfulBuildStartTimeUtc, itemType ?? ""); + } + + if (latestInput is null || inputTime > latestInput.Value.Time) + { + latestInput = (input, inputTime); + } } - IEnumerable<(string Path, string? ItemType, bool IsRequired)> CollectDefaultInputs() + if (log.Level >= LogLevel.Info) { - if (state.NewestImportInput is not null) + if (latestInput is null) { - log.Verbose(nameof(VSResources.FUTD_AddingNewestImportInput)); - using Log.Scope _ = log.IndentScope(); - yield return (Path: state.NewestImportInput, ItemType: null, IsRequired: true); + log.Info(setName == DefaultSetName ? nameof(VSResources.FUTD_NoInputsDefined) : nameof(VSResources.FUTD_NoInputsDefinedInSet_1), setName); } - - foreach ((string itemType, ImmutableArray items) in state.InputSourceItemsByItemType) + else { - log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), itemType); + log.Info(setName == DefaultSetName ? nameof(VSResources.FUTD_NoInputsNewerThanEarliestOutput_4) : nameof(VSResources.FUTD_NoInputsNewerThanEarliestOutputInSet_5), earliestOutputPath, earliestOutputTime, latestInput.Value.Path, latestInput.Value.Time ?? (object)"null", setName); + } + } - using Log.Scope _ = log.IndentScope(); + return true; + } - foreach (string item in items) - { - string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(item); - yield return (Path: absolutePath, itemType, IsRequired: true); - } - } + IEnumerable<(string Path, string? ItemType, bool IsRequired)> CollectDefaultInputs() + { + if (state.NewestImportInput is not null) + { + log.Verbose(nameof(VSResources.FUTD_AddingNewestImportInput)); + using Log.Scope _ = log.IndentScope(); + yield return (Path: state.NewestImportInput, ItemType: null, IsRequired: true); + } - if (!state.ResolvedAnalyzerReferencePaths.IsEmpty) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), ResolvedAnalyzerReference.SchemaName); + foreach ((string itemType, ImmutableArray items) in state.InputSourceItemsByItemType) + { + log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), itemType); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string path in state.ResolvedAnalyzerReferencePaths) - { - string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); - yield return (Path: absolutePath, ItemType: ResolvedAnalyzerReference.SchemaName, IsRequired: true); - } + foreach (string item in items) + { + string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(item); + yield return (Path: absolutePath, itemType, IsRequired: true); } + } - if (!state.ResolvedCompilationReferencePaths.IsEmpty) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), ResolvedCompilationReference.SchemaName); + if (!state.ResolvedAnalyzerReferencePaths.IsEmpty) + { + log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), ResolvedAnalyzerReference.SchemaName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string path in state.ResolvedCompilationReferencePaths) - { - System.Diagnostics.Debug.Assert(Path.IsPathRooted(path), "ResolvedCompilationReference path should be rooted"); - yield return (Path: path, ItemType: ResolvedCompilationReference.SchemaName, IsRequired: true); - } + foreach (string path in state.ResolvedAnalyzerReferencePaths) + { + string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); + yield return (Path: absolutePath, ItemType: ResolvedAnalyzerReference.SchemaName, IsRequired: true); } + } - if (state.UpToDateCheckInputItemsByKindBySetName.TryGetValue(DefaultSetName, out ImmutableDictionary>? upToDateCheckInputItems)) + if (!state.ResolvedCompilationReferencePaths.IsEmpty) + { + log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), ResolvedCompilationReference.SchemaName); + + using Log.Scope _ = log.IndentScope(); + + foreach (string path in state.ResolvedCompilationReferencePaths) { - log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), UpToDateCheckInput.SchemaName); + System.Diagnostics.Debug.Assert(Path.IsPathRooted(path), "ResolvedCompilationReference path should be rooted"); + yield return (Path: path, ItemType: ResolvedCompilationReference.SchemaName, IsRequired: true); + } + } + + if (state.UpToDateCheckInputItemsByKindBySetName.TryGetValue(DefaultSetName, out ImmutableDictionary>? upToDateCheckInputItems)) + { + log.Verbose(nameof(VSResources.FUTD_AddingTypedInputs_1), UpToDateCheckInput.SchemaName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string kind in state.KindNames) + foreach (string kind in state.KindNames) + { + if (upToDateCheckInputItems.TryGetValue(kind, out ImmutableArray items)) { - if (upToDateCheckInputItems.TryGetValue(kind, out ImmutableArray items)) + if (ShouldIgnoreItems(kind, items)) { - if (ShouldIgnoreItems(kind, items)) - { - continue; - } + continue; + } - foreach (string path in items) - { - string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); - yield return (Path: absolutePath, ItemType: UpToDateCheckInput.SchemaName, IsRequired: true); - } + foreach (string path in items) + { + string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); + yield return (Path: absolutePath, ItemType: UpToDateCheckInput.SchemaName, IsRequired: true); } } } } + } - IEnumerable CollectDefaultOutputs() + IEnumerable CollectDefaultOutputs() + { + if (state.UpToDateCheckOutputItemsByKindBySetName.TryGetValue(DefaultSetName, out ImmutableDictionary>? upToDateCheckOutputItems)) { - if (state.UpToDateCheckOutputItemsByKindBySetName.TryGetValue(DefaultSetName, out ImmutableDictionary>? upToDateCheckOutputItems)) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputs_1), UpToDateCheckOutput.SchemaName); + log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputs_1), UpToDateCheckOutput.SchemaName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string kind in state.KindNames) + foreach (string kind in state.KindNames) + { + if (upToDateCheckOutputItems.TryGetValue(kind, out ImmutableArray items)) { - if (upToDateCheckOutputItems.TryGetValue(kind, out ImmutableArray items)) + if (ShouldIgnoreItems(kind, items)) { - if (ShouldIgnoreItems(kind, items)) - { - continue; - } + continue; + } - foreach (string path in items) - { - yield return _configuredProject.UnconfiguredProject.MakeRooted(path); - } + foreach (string path in items) + { + yield return _configuredProject.UnconfiguredProject.MakeRooted(path); } } } + } - if (state.UpToDateCheckBuiltItemsByKindBySetName.TryGetValue(DefaultSetName, out ImmutableDictionary>? upToDateCheckBuiltItems)) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputs_1), UpToDateCheckBuilt.SchemaName); + if (state.UpToDateCheckBuiltItemsByKindBySetName.TryGetValue(DefaultSetName, out ImmutableDictionary>? upToDateCheckBuiltItems)) + { + log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputs_1), UpToDateCheckBuilt.SchemaName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string kind in state.KindNames) + foreach (string kind in state.KindNames) + { + if (upToDateCheckBuiltItems.TryGetValue(kind, out ImmutableArray items)) { - if (upToDateCheckBuiltItems.TryGetValue(kind, out ImmutableArray items)) + if (ShouldIgnoreItems(kind, items)) { - if (ShouldIgnoreItems(kind, items)) - { - continue; - } + continue; + } - foreach (string path in items) - { - yield return _configuredProject.UnconfiguredProject.MakeRooted(path); - } + foreach (string path in items) + { + yield return _configuredProject.UnconfiguredProject.MakeRooted(path); } } } } + } - IEnumerable<(string Path, string? ItemType, bool IsRequired)> CollectSetInputs(string setName) + IEnumerable<(string Path, string? ItemType, bool IsRequired)> CollectSetInputs(string setName) + { + if (state.UpToDateCheckInputItemsByKindBySetName.TryGetValue(setName, out ImmutableDictionary>? upToDateCheckInputItems)) { - if (state.UpToDateCheckInputItemsByKindBySetName.TryGetValue(setName, out ImmutableDictionary>? upToDateCheckInputItems)) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedInputsInSet_2), UpToDateCheckInput.SchemaName, setName); + log.Verbose(nameof(VSResources.FUTD_AddingTypedInputsInSet_2), UpToDateCheckInput.SchemaName, setName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string kind in state.KindNames) + foreach (string kind in state.KindNames) + { + if (upToDateCheckInputItems.TryGetValue(kind, out ImmutableArray items)) { - if (upToDateCheckInputItems.TryGetValue(kind, out ImmutableArray items)) + if (ShouldIgnoreItems(kind, items)) { - if (ShouldIgnoreItems(kind, items)) - { - continue; - } + continue; + } - foreach (string path in items) - { - string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); - yield return (Path: absolutePath, ItemType: UpToDateCheckInput.SchemaName, IsRequired: true); - } + foreach (string path in items) + { + string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); + yield return (Path: absolutePath, ItemType: UpToDateCheckInput.SchemaName, IsRequired: true); } } } } + } - IEnumerable CollectSetOutputs(string setName) + IEnumerable CollectSetOutputs(string setName) + { + if (state.UpToDateCheckOutputItemsByKindBySetName.TryGetValue(setName, out ImmutableDictionary>? upToDateCheckOutputItems)) { - if (state.UpToDateCheckOutputItemsByKindBySetName.TryGetValue(setName, out ImmutableDictionary>? upToDateCheckOutputItems)) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputsInSet_2), UpToDateCheckOutput.SchemaName, setName); + log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputsInSet_2), UpToDateCheckOutput.SchemaName, setName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string kind in state.KindNames) + foreach (string kind in state.KindNames) + { + if (upToDateCheckOutputItems.TryGetValue(kind, out ImmutableArray items)) { - if (upToDateCheckOutputItems.TryGetValue(kind, out ImmutableArray items)) + if (ShouldIgnoreItems(kind, items)) { - if (ShouldIgnoreItems(kind, items)) - { - continue; - } + continue; + } - foreach (string path in items) - { - yield return _configuredProject.UnconfiguredProject.MakeRooted(path); - } + foreach (string path in items) + { + yield return _configuredProject.UnconfiguredProject.MakeRooted(path); } } } + } - if (state.UpToDateCheckBuiltItemsByKindBySetName.TryGetValue(setName, out ImmutableDictionary>? upToDateCheckBuiltItems)) - { - log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputsInSet_2), UpToDateCheckBuilt.SchemaName, setName); + if (state.UpToDateCheckBuiltItemsByKindBySetName.TryGetValue(setName, out ImmutableDictionary>? upToDateCheckBuiltItems)) + { + log.Verbose(nameof(VSResources.FUTD_AddingTypedOutputsInSet_2), UpToDateCheckBuilt.SchemaName, setName); - using Log.Scope _ = log.IndentScope(); + using Log.Scope _ = log.IndentScope(); - foreach (string kind in state.KindNames) + foreach (string kind in state.KindNames) + { + if (upToDateCheckBuiltItems.TryGetValue(kind, out ImmutableArray items)) { - if (upToDateCheckBuiltItems.TryGetValue(kind, out ImmutableArray items)) + if (ShouldIgnoreItems(kind, items)) { - if (ShouldIgnoreItems(kind, items)) - { - continue; - } + continue; + } - foreach (string path in items) - { - yield return _configuredProject.UnconfiguredProject.MakeRooted(path); - } + foreach (string path in items) + { + yield return _configuredProject.UnconfiguredProject.MakeRooted(path); } } } } + } - bool ShouldIgnoreItems(string kind, ImmutableArray items) + bool ShouldIgnoreItems(string kind, ImmutableArray items) + { + if (ignoreKinds?.Contains(kind) != true) { - if (ignoreKinds?.Contains(kind) != true) - { - return false; - } + return false; + } - if (log.Level >= LogLevel.Verbose) + if (log.Level >= LogLevel.Verbose) + { + foreach (string path in items) { - foreach (string path in items) - { - string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); - log.Verbose(nameof(VSResources.FUTD_SkippingIgnoredKindItem_2), absolutePath, kind); - } + string absolutePath = _configuredProject.UnconfiguredProject.MakeRooted(path); + log.Verbose(nameof(VSResources.FUTD_SkippingIgnoredKindItem_2), absolutePath, kind); } - - return true; } + + return true; } + } - private bool CheckMarkers(Log log, in TimestampCache timestampCache, UpToDateCheckImplicitConfiguredInput state, bool? isBuildAccelerationEnabled, FileSystemOperationAggregator fileSystemOperations) + private bool CheckMarkers(Log log, in TimestampCache timestampCache, UpToDateCheckImplicitConfiguredInput state, bool? isBuildAccelerationEnabled, FileSystemOperationAggregator fileSystemOperations) + { + if (isBuildAccelerationEnabled is true) { - if (isBuildAccelerationEnabled is true) - { - // Build acceleration replaces the need to check the copy marker. We will be checking the items reported - // by each project directly on disk, and will catch specific instances where they are out of date. - return true; - } + // Build acceleration replaces the need to check the copy marker. We will be checking the items reported + // by each project directly on disk, and will catch specific instances where they are out of date. + return true; + } - // Copy markers support the use of reference assemblies, which in turn help avoid redundant compilation. - // - // Building a project that produces reference assemblies may update both: - // - // 1. The implementation assembly (bin/Debug/MyApp.dll), used at runtime, containing the full implementation. - // 2. The reference assembly (obj/Debug/ref/MyApp.dll), used at compile time, containing only public API. - // - // The reference assembly is only modified when the public API surface of the implementation assembly changes. - // A referencing project can use this information to avoid recompiling itself in response to the referenced - // project change. In such cases, the implementation assembly can be simply be copied. - // - // A big part of the build acceleration feature is being able to have VS copy the implementation assembly - // and report the project as up-to-date. It is much cheaper for VS to just copy the file than to call MSBuild - // to do the same. - // - // When determining whether to build a referencing project, we examine the timestamps of: - // - // 1. `state.CopyUpToDateMarkerItem` - // - // - Example: `MyApp/obj/Debug/net7.0/MyApp.csproj.CopyComplete`. - // - From `CopyUpToDateMarker` MSBuild item. Requires a single instance of this item. - // - Always present for SDK-based projects, regardless of whether `ProduceReferenceAssembly` is true. - // - // 2. `state.CopyReferenceInputs` - // - // - Example: - // - `MyLibrary/bin/Debug/net6.0/MyLibrary.dll` - // - `MyLibrary/obj/Debug/net6.0/MyLibrary.csproj.CopyComplete` - // - From the `ResolvedPath` and `CopyUpToDateMarker` metadata on `ResolvedCompilationReference` items. - // - // If either are empty, there is no check to perform and we return immediately. This path would be taken for - // projects having `ProduceReferenceAssembly` set to false. - // - // During build, if a project has references marked as "CopyLocal" (such dependencies, PDBs, XMLs, satellite assemblies) - // that are copied to the output directory, the build touches its own CopyMarker item. Referencing projects - // consider this CopyMarker as an input, which allows the copy to trigger builds in those referencing projects. - // - // When the project build copies a file into its output directory, it touches its own `.csproj.CopyComplete` - // file. This gives future builds a timestamp to compare against. - // - // If a project does not have any project references, it will not produce a `.csproj.CopyComplete` file. - // - // When values exist, they will resemble: - // - // Comparing timestamps of copy marker inputs and output: - // Write timestamp on output marker is 2022-11-10 13:42:01.665 on 'C:/Users/drnoakes/source/repos/MyApp/MyApp/obj/Debug/net7.0/MyApp.csproj.CopyComplete'. - // Adding input reference copy markers: - // C:/Users/drnoakes/source/repos/MyApp/MyLibrary/bin/Debug/net6.0/MyLibrary.dll - // C:/Users/drnoakes/source/repos/MyApp/MyLibrary/obj/Debug/net6.0/MyLibrary.csproj.CopyComplete - // Input marker does not exist. - // - // Reference assembly copy markers are strange. The property is always going to be present on - // references to SDK-based projects, regardless of whether or not those referenced projects - // will actually produce a marker. And an item always will be present in an SDK-based project, - // regardless of whether or not the project produces a marker. So, basically, we only check - // here if the project actually produced a marker and we only check it against references that - // actually produced a marker. - - if (Strings.IsNullOrWhiteSpace(state.CopyUpToDateMarkerItem) || state.CopyReferenceInputs.IsEmpty) + // Copy markers support the use of reference assemblies, which in turn help avoid redundant compilation. + // + // Building a project that produces reference assemblies may update both: + // + // 1. The implementation assembly (bin/Debug/MyApp.dll), used at runtime, containing the full implementation. + // 2. The reference assembly (obj/Debug/ref/MyApp.dll), used at compile time, containing only public API. + // + // The reference assembly is only modified when the public API surface of the implementation assembly changes. + // A referencing project can use this information to avoid recompiling itself in response to the referenced + // project change. In such cases, the implementation assembly can be simply be copied. + // + // A big part of the build acceleration feature is being able to have VS copy the implementation assembly + // and report the project as up-to-date. It is much cheaper for VS to just copy the file than to call MSBuild + // to do the same. + // + // When determining whether to build a referencing project, we examine the timestamps of: + // + // 1. `state.CopyUpToDateMarkerItem` + // + // - Example: `MyApp/obj/Debug/net7.0/MyApp.csproj.CopyComplete`. + // - From `CopyUpToDateMarker` MSBuild item. Requires a single instance of this item. + // - Always present for SDK-based projects, regardless of whether `ProduceReferenceAssembly` is true. + // + // 2. `state.CopyReferenceInputs` + // + // - Example: + // - `MyLibrary/bin/Debug/net6.0/MyLibrary.dll` + // - `MyLibrary/obj/Debug/net6.0/MyLibrary.csproj.CopyComplete` + // - From the `ResolvedPath` and `CopyUpToDateMarker` metadata on `ResolvedCompilationReference` items. + // + // If either are empty, there is no check to perform and we return immediately. This path would be taken for + // projects having `ProduceReferenceAssembly` set to false. + // + // During build, if a project has references marked as "CopyLocal" (such dependencies, PDBs, XMLs, satellite assemblies) + // that are copied to the output directory, the build touches its own CopyMarker item. Referencing projects + // consider this CopyMarker as an input, which allows the copy to trigger builds in those referencing projects. + // + // When the project build copies a file into its output directory, it touches its own `.csproj.CopyComplete` + // file. This gives future builds a timestamp to compare against. + // + // If a project does not have any project references, it will not produce a `.csproj.CopyComplete` file. + // + // When values exist, they will resemble: + // + // Comparing timestamps of copy marker inputs and output: + // Write timestamp on output marker is 2022-11-10 13:42:01.665 on 'C:/Users/drnoakes/source/repos/MyApp/MyApp/obj/Debug/net7.0/MyApp.csproj.CopyComplete'. + // Adding input reference copy markers: + // C:/Users/drnoakes/source/repos/MyApp/MyLibrary/bin/Debug/net6.0/MyLibrary.dll + // C:/Users/drnoakes/source/repos/MyApp/MyLibrary/obj/Debug/net6.0/MyLibrary.csproj.CopyComplete + // Input marker does not exist. + // + // Reference assembly copy markers are strange. The property is always going to be present on + // references to SDK-based projects, regardless of whether or not those referenced projects + // will actually produce a marker. And an item always will be present in an SDK-based project, + // regardless of whether or not the project produces a marker. So, basically, we only check + // here if the project actually produced a marker and we only check it against references that + // actually produced a marker. + + if (Strings.IsNullOrWhiteSpace(state.CopyUpToDateMarkerItem) || state.CopyReferenceInputs.IsEmpty) + { + return true; + } + + log.Info(nameof(VSResources.FUTD_ComparingCopyMarkerTimestamps)); + + using Log.Scope _ = log.IndentScope(); + + string outputMarkerFile = _configuredProject.UnconfiguredProject.MakeRooted(state.CopyUpToDateMarkerItem); + + DateTime? outputMarkerTime = timestampCache.GetTimestampUtc(outputMarkerFile); + + if (outputMarkerTime is null) + { + // No output marker exists, so we can't be out of date. + log.Info(nameof(VSResources.FUTD_NoOutputMarkerExists_1), outputMarkerFile); + return true; + } + + log.Info(nameof(VSResources.FUTD_WriteTimeOnOutputMarker_2), outputMarkerTime, outputMarkerFile); + + log.Verbose(nameof(VSResources.FUTD_AddingInputReferenceCopyMarkers)); + + bool inputMarkerExists = false; + + using (log.IndentScope()) + { + foreach (string inputMarker in state.CopyReferenceInputs) { - return true; - } + log.VerboseLiteral(inputMarker); - log.Info(nameof(VSResources.FUTD_ComparingCopyMarkerTimestamps)); + DateTime? inputMarkerTime = timestampCache.GetTimestampUtc(inputMarker); - using Log.Scope _ = log.IndentScope(); + if (inputMarkerTime is null) + { + using (log.IndentScope()) + { + log.Verbose(nameof(VSResources.FUTD_InputMarkerDoesNotExist)); + continue; + } + } - string outputMarkerFile = _configuredProject.UnconfiguredProject.MakeRooted(state.CopyUpToDateMarkerItem); + inputMarkerExists = true; - DateTime? outputMarkerTime = timestampCache.GetTimestampUtc(outputMarkerFile); + // See if input marker is newer than output marker + if (outputMarkerTime < inputMarkerTime) + { + fileSystemOperations.IsAccelerationCandidate = true; - if (outputMarkerTime is null) - { - // No output marker exists, so we can't be out of date. - log.Info(nameof(VSResources.FUTD_NoOutputMarkerExists_1), outputMarkerFile); - return true; + return log.Fail("InputMarkerNewerThanOutputMarker", nameof(VSResources.FUTD_InputMarkerNewerThanOutputMarker_4), inputMarker, inputMarkerTime, outputMarkerFile, outputMarkerTime); + } } + } - log.Info(nameof(VSResources.FUTD_WriteTimeOnOutputMarker_2), outputMarkerTime, outputMarkerFile); + if (!inputMarkerExists) + { + log.Info(nameof(VSResources.FUTD_NoInputMarkersExist)); + } - log.Verbose(nameof(VSResources.FUTD_AddingInputReferenceCopyMarkers)); + return true; + } - bool inputMarkerExists = false; + private bool CheckBuiltFromInputFiles(Log log, in TimestampCache timestampCache, UpToDateCheckImplicitConfiguredInput state, CancellationToken token) + { + // Note we cannot accelerate builds by copying these items. The input is potentially transformed + // in some way, during build, to produce the output. It's not always a straight copy. We have to + // call ths build to satisfy these items, unlike our CopyToOutputDirectory items. - using (log.IndentScope()) - { - foreach (string inputMarker in state.CopyReferenceInputs) - { - log.VerboseLiteral(inputMarker); + foreach ((string destinationRelative, string sourceRelative) in state.BuiltFromInputFileItems) + { + token.ThrowIfCancellationRequested(); - DateTime? inputMarkerTime = timestampCache.GetTimestampUtc(inputMarker); + string sourcePath = _configuredProject.UnconfiguredProject.MakeRooted(sourceRelative); + string destinationPath = _configuredProject.UnconfiguredProject.MakeRooted(destinationRelative); - if (inputMarkerTime is null) - { - using (log.IndentScope()) - { - log.Verbose(nameof(VSResources.FUTD_InputMarkerDoesNotExist)); - continue; - } - } + log.Info(nameof(VSResources.FUTD_CheckingBuiltOutputFile), sourcePath); - inputMarkerExists = true; + using Log.Scope _ = log.IndentScope(); - // See if input marker is newer than output marker - if (outputMarkerTime < inputMarkerTime) - { - fileSystemOperations.IsAccelerationCandidate = true; + DateTime? sourceTime = timestampCache.GetTimestampUtc(sourcePath); - return log.Fail("InputMarkerNewerThanOutputMarker", nameof(VSResources.FUTD_InputMarkerNewerThanOutputMarker_4), inputMarker, inputMarkerTime, outputMarkerFile, outputMarkerTime); - } - } + if (sourceTime is null) + { + // We don't generally expect the source to be unavailable. + // If this occurs, schedule a build to be on the safe side. + return log.Fail("CopySourceNotFound", nameof(VSResources.FUTD_CheckingBuiltOutputFileSourceNotFound_2), sourcePath, destinationPath); } - if (!inputMarkerExists) + log.Info(nameof(VSResources.FUTD_SourceFileTimeAndPath_2), sourceTime, sourcePath); + + DateTime? destinationTime = timestampCache.GetTimestampUtc(destinationPath); + + if (destinationTime is null) { - log.Info(nameof(VSResources.FUTD_NoInputMarkersExist)); + return log.Fail("CopyDestinationNotFound", nameof(VSResources.FUTD_CheckingBuiltOutputFileDestinationNotFound_2), destinationPath, sourcePath); } - return true; + log.Info(nameof(VSResources.FUTD_DestinationFileTimeAndPath_2), destinationTime, destinationPath); + + if (destinationTime < sourceTime) + { + return log.Fail("CopySourceNewer", nameof(VSResources.FUTD_CheckingBuiltOutputFileSourceNewer)); + } } - private bool CheckBuiltFromInputFiles(Log log, in TimestampCache timestampCache, UpToDateCheckImplicitConfiguredInput state, CancellationToken token) - { - // Note we cannot accelerate builds by copying these items. The input is potentially transformed - // in some way, during build, to produce the output. It's not always a straight copy. We have to - // call ths build to satisfy these items, unlike our CopyToOutputDirectory items. + return true; + } - foreach ((string destinationRelative, string sourceRelative) in state.BuiltFromInputFileItems) - { - token.ThrowIfCancellationRequested(); + private bool CheckCopyToOutputDirectoryItems(Log log, UpToDateCheckImplicitConfiguredInput state, IEnumerable<(string Path, ImmutableArray CopyItems)> copyItemsByProject, ConfiguredFileSystemOperationAggregator fileSystemAggregator, bool? isBuildAccelerationEnabled, SolutionBuildContext solutionBuildContext, CancellationToken token) + { + ITimestampCache timestampCache = solutionBuildContext.CopyItemTimestamps; - string sourcePath = _configuredProject.UnconfiguredProject.MakeRooted(sourceRelative); - string destinationPath = _configuredProject.UnconfiguredProject.MakeRooted(destinationRelative); + string outputFullPath = Path.Combine(state.MSBuildProjectDirectory, state.OutputRelativeOrFullPath); - log.Info(nameof(VSResources.FUTD_CheckingBuiltOutputFile), sourcePath); + Log.Scope? scope1 = null; - using Log.Scope _ = log.IndentScope(); + foreach ((string project, ImmutableArray copyItems) in copyItemsByProject) + { + Log.Scope? scope2 = null; - DateTime? sourceTime = timestampCache.GetTimestampUtc(sourcePath); + foreach ((string sourcePath, string targetPath, CopyType copyType, bool isBuildAccelerationOnly) in copyItems) + { + token.ThrowIfCancellationRequested(); - if (sourceTime is null) + if (isBuildAccelerationEnabled is not true && isBuildAccelerationOnly) { - // We don't generally expect the source to be unavailable. - // If this occurs, schedule a build to be on the safe side. - return log.Fail("CopySourceNotFound", nameof(VSResources.FUTD_CheckingBuiltOutputFileSourceNotFound_2), sourcePath, destinationPath); + // This item should only be checked when build acceleration is enabled. + // For example, we only check referenced output assemblies when enabled. + // When not accelerating builds, checking these is unnecessary overhead. + continue; } - log.Info(nameof(VSResources.FUTD_SourceFileTimeAndPath_2), sourceTime, sourcePath); - - DateTime? destinationTime = timestampCache.GetTimestampUtc(destinationPath); + string destinationPath = Path.Combine(outputFullPath, targetPath); - if (destinationTime is null) + if (StringComparers.Paths.Equals(sourcePath, destinationPath)) { - return log.Fail("CopyDestinationNotFound", nameof(VSResources.FUTD_CheckingBuiltOutputFileDestinationNotFound_2), destinationPath, sourcePath); + // This can occur when a project is checking its own items, and the item already + // exists in the output directory. + continue; } - log.Info(nameof(VSResources.FUTD_DestinationFileTimeAndPath_2), destinationTime, destinationPath); - - if (destinationTime < sourceTime) + if (scope1 is null) { - return log.Fail("CopySourceNewer", nameof(VSResources.FUTD_CheckingBuiltOutputFileSourceNewer)); + log.Verbose(nameof(VSResources.FUTD_CheckingCopyToOutputDirectoryItems)); + scope1 = log.IndentScope(); } - } - - return true; - } - - private bool CheckCopyToOutputDirectoryItems(Log log, UpToDateCheckImplicitConfiguredInput state, IEnumerable<(string Path, ImmutableArray CopyItems)> copyItemsByProject, ConfiguredFileSystemOperationAggregator fileSystemAggregator, bool? isBuildAccelerationEnabled, SolutionBuildContext solutionBuildContext, CancellationToken token) - { - ITimestampCache timestampCache = solutionBuildContext.CopyItemTimestamps; - - string outputFullPath = Path.Combine(state.MSBuildProjectDirectory, state.OutputRelativeOrFullPath); - Log.Scope? scope1 = null; - - foreach ((string project, ImmutableArray copyItems) in copyItemsByProject) - { - Log.Scope? scope2 = null; - - foreach ((string sourcePath, string targetPath, CopyType copyType, bool isBuildAccelerationOnly) in copyItems) + if (scope2 is null) { - token.ThrowIfCancellationRequested(); + log.Verbose(nameof(VSResources.FUTDC_CheckingCopyItemsForProject_1), project); + scope2 = log.IndentScope(); + } - if (isBuildAccelerationEnabled is not true && isBuildAccelerationOnly) - { - // This item should only be checked when build acceleration is enabled. - // For example, we only check referenced output assemblies when enabled. - // When not accelerating builds, checking these is unnecessary overhead. - continue; - } + log.Verbose(nameof(VSResources.FUTD_CheckingCopyToOutputDirectoryItem_1), copyType.ToString()); - string destinationPath = Path.Combine(outputFullPath, targetPath); + DateTime? sourceTime = timestampCache.GetTimestampUtc(sourcePath); - if (StringComparers.Paths.Equals(sourcePath, destinationPath)) - { - // This can occur when a project is checking its own items, and the item already - // exists in the output directory. - continue; - } + if (sourceTime is null) + { + // We don't generally expect the source to be unavailable. + // If this occurs, schedule a build to be on the safe side. + return log.Fail("CopyToOutputDirectorySourceNotFound", nameof(VSResources.FUTD_CheckingCopyToOutputDirectorySourceNotFound_1), sourcePath); + } - if (scope1 is null) - { - log.Verbose(nameof(VSResources.FUTD_CheckingCopyToOutputDirectoryItems)); - scope1 = log.IndentScope(); - } + using Log.Scope _ = log.IndentScope(); - if (scope2 is null) - { - log.Verbose(nameof(VSResources.FUTDC_CheckingCopyItemsForProject_1), project); - scope2 = log.IndentScope(); - } + log.Verbose(nameof(VSResources.FUTD_SourceFileTimeAndPath_2), sourceTime, sourcePath); - log.Verbose(nameof(VSResources.FUTD_CheckingCopyToOutputDirectoryItem_1), copyType.ToString()); + DateTime? destinationTime = timestampCache.GetTimestampUtc(destinationPath); - DateTime? sourceTime = timestampCache.GetTimestampUtc(sourcePath); + if (destinationTime is null) + { + log.Verbose(nameof(VSResources.FUTD_DestinationDoesNotExist_1), destinationPath); - if (sourceTime is null) + if (!fileSystemAggregator.AddCopy(sourcePath, destinationPath)) { - // We don't generally expect the source to be unavailable. - // If this occurs, schedule a build to be on the safe side. - return log.Fail("CopyToOutputDirectorySourceNotFound", nameof(VSResources.FUTD_CheckingCopyToOutputDirectorySourceNotFound_1), sourcePath); + return log.Fail("CopyToOutputDirectoryDestinationNotFound", nameof(VSResources.FUTD_CheckingCopyToOutputDirectoryItemDestinationNotFound_1), destinationPath); } - using Log.Scope _ = log.IndentScope(); - - log.Verbose(nameof(VSResources.FUTD_SourceFileTimeAndPath_2), sourceTime, sourcePath); + continue; + } - DateTime? destinationTime = timestampCache.GetTimestampUtc(destinationPath); + log.Verbose(nameof(VSResources.FUTD_DestinationFileTimeAndPath_2), destinationTime, destinationPath); - if (destinationTime is null) + switch (copyType) + { + case CopyType.Always: { - log.Verbose(nameof(VSResources.FUTD_DestinationDoesNotExist_1), destinationPath); - - if (!fileSystemAggregator.AddCopy(sourcePath, destinationPath)) + // We have already validated the presence of these files, so we don't expect these to return + // false. If one of them does, the corresponding size would be zero, so we would schedule a build. + // The odds of both source and destination disappearing between the gathering of the timestamps + // above and these following statements is vanishingly small, and would suggest bigger problems + // such as the entire project directory having been deleted. + _fileSystem.TryGetFileSizeBytes(sourcePath, out long sourceSizeBytes); + _fileSystem.TryGetFileSizeBytes(destinationPath, out long destinationSizeBytes); + + if (sourceTime != destinationTime || sourceSizeBytes != destinationSizeBytes) { - return log.Fail("CopyToOutputDirectoryDestinationNotFound", nameof(VSResources.FUTD_CheckingCopyToOutputDirectoryItemDestinationNotFound_1), destinationPath); + if (!fileSystemAggregator.AddCopy(sourcePath, destinationPath)) + { + return log.Fail("CopyAlwaysItemDiffers", nameof(VSResources.FUTD_CopyAlwaysItemsDiffer_6), sourcePath, sourceTime, sourceSizeBytes, destinationPath, destinationTime, destinationSizeBytes); + } } - continue; + break; } - log.Verbose(nameof(VSResources.FUTD_DestinationFileTimeAndPath_2), destinationTime, destinationPath); - - switch (copyType) + case CopyType.PreserveNewest: { - case CopyType.Always: + if (destinationTime < sourceTime) { - // We have already validated the presence of these files, so we don't expect these to return - // false. If one of them does, the corresponding size would be zero, so we would schedule a build. - // The odds of both source and destination disappearing between the gathering of the timestamps - // above and these following statements is vanishingly small, and would suggest bigger problems - // such as the entire project directory having been deleted. - _fileSystem.TryGetFileSizeBytes(sourcePath, out long sourceSizeBytes); - _fileSystem.TryGetFileSizeBytes(destinationPath, out long destinationSizeBytes); - - if (sourceTime != destinationTime || sourceSizeBytes != destinationSizeBytes) + if (!fileSystemAggregator.AddCopy(sourcePath, destinationPath)) { - if (!fileSystemAggregator.AddCopy(sourcePath, destinationPath)) - { - return log.Fail("CopyAlwaysItemDiffers", nameof(VSResources.FUTD_CopyAlwaysItemsDiffer_6), sourcePath, sourceTime, sourceSizeBytes, destinationPath, destinationTime, destinationSizeBytes); - } + return log.Fail("CopyToOutputDirectorySourceNewer", nameof(VSResources.FUTD_CheckingCopyToOutputDirectorySourceNewerThanDestination_3), CopyType.PreserveNewest.ToString(), sourcePath, destinationPath); } - - break; } - case CopyType.PreserveNewest: - { - if (destinationTime < sourceTime) - { - if (!fileSystemAggregator.AddCopy(sourcePath, destinationPath)) - { - return log.Fail("CopyToOutputDirectorySourceNewer", nameof(VSResources.FUTD_CheckingCopyToOutputDirectorySourceNewerThanDestination_3), CopyType.PreserveNewest.ToString(), sourcePath, destinationPath); - } - } - - break; - } + break; + } - default: - { - System.Diagnostics.Debug.Fail("Project copy items should only contain copyable items."); - break; - } + default: + { + System.Diagnostics.Debug.Fail("Project copy items should only contain copyable items."); + break; } } - - scope2?.Dispose(); } - scope1?.Dispose(); + scope2?.Dispose(); + } - return true; + scope1?.Dispose(); + + return true; + } + + void IProjectBuildEventListener.NotifyBuildStarting(DateTime buildStartTimeUtc) + { + _lastBuildStartTimeUtc = buildStartTimeUtc; + } + + async Task IProjectBuildEventListener.NotifyBuildCompletedAsync(bool wasSuccessful, bool isRebuild) + { + if (_lastCopyTargetsFromThisProject is not null) + { + // The project build has completed. We must assume this project modified its outputs, + // so we remove the outputs that were likely modified from our cache. The next requests + // for these files will perform a fresh query. + _solutionBuildContextProvider.CurrentSolutionBuildContext?.CopyItemTimestamps?.ClearTimestamps(_lastCopyTargetsFromThisProject); + + // We don't use this again after clearing the cache, so release it for GC. + _lastCopyTargetsFromThisProject = null; } - void IProjectBuildEventListener.NotifyBuildStarting(DateTime buildStartTimeUtc) + if (_lastBuildStartTimeUtc == default) { - _lastBuildStartTimeUtc = buildStartTimeUtc; + // This should not happen + System.Diagnostics.Debug.Fail("Notification of build completion should follow notification of build starting."); + + return; } - async Task IProjectBuildEventListener.NotifyBuildCompletedAsync(bool wasSuccessful, bool isRebuild) + if (wasSuccessful) { - if (_lastCopyTargetsFromThisProject is not null) - { - // The project build has completed. We must assume this project modified its outputs, - // so we remove the outputs that were likely modified from our cache. The next requests - // for these files will perform a fresh query. - _solutionBuildContextProvider.CurrentSolutionBuildContext?.CopyItemTimestamps?.ClearTimestamps(_lastCopyTargetsFromThisProject); + ISubscription subscription = Volatile.Read(ref _subscription); - // We don't use this again after clearing the cache, so release it for GC. - _lastCopyTargetsFromThisProject = null; - } + await subscription.UpdateLastSuccessfulBuildStartTimeUtcAsync(_lastBuildStartTimeUtc, isRebuild); + } - if (_lastBuildStartTimeUtc == default) - { - // This should not happen - System.Diagnostics.Debug.Fail("Notification of build completion should follow notification of build starting."); + _lastBuildStartTimeUtc = default; + } - return; - } + private static bool ConfiguredInputMatchesTargetFramework(UpToDateCheckImplicitConfiguredInput input, string buildTargetFramework) + { + return input.ProjectConfiguration.Dimensions.TryGetValue(ConfigurationGeneral.TargetFrameworkProperty, out string? configurationTargetFramework) + && buildTargetFramework.Equals(configurationTargetFramework, StringComparisons.ConfigurationDimensionValues); + } - if (wasSuccessful) - { - ISubscription subscription = Volatile.Read(ref _subscription); + Task IBuildUpToDateCheckProvider.IsUpToDateAsync(BuildAction buildAction, TextWriter logWriter, CancellationToken cancellationToken) + { + return IsUpToDateAsync(buildAction, logWriter, ImmutableDictionary.Empty, cancellationToken); + } - await subscription.UpdateLastSuccessfulBuildStartTimeUtcAsync(_lastBuildStartTimeUtc, isRebuild); - } + async Task<(bool IsUpToDate, string? FailureReason, string? FailureDescription)> IBuildUpToDateCheckValidator.ValidateUpToDateAsync(CancellationToken cancellationToken) + { + bool isUpToDate = await IsUpToDateInternalAsync(TextWriter.Null, _lastGlobalProperties, isValidationRun: true, cancellationToken); - _lastBuildStartTimeUtc = default; - } + string? failureReason = isUpToDate ? null : _lastFailureReason; + string? failureDescription = isUpToDate ? null : _lastFailureDescription; - private static bool ConfiguredInputMatchesTargetFramework(UpToDateCheckImplicitConfiguredInput input, string buildTargetFramework) - { - return input.ProjectConfiguration.Dimensions.TryGetValue(ConfigurationGeneral.TargetFrameworkProperty, out string? configurationTargetFramework) - && buildTargetFramework.Equals(configurationTargetFramework, StringComparisons.ConfigurationDimensionValues); - } + return (isUpToDate, failureReason, failureDescription); + } - Task IBuildUpToDateCheckProvider.IsUpToDateAsync(BuildAction buildAction, TextWriter logWriter, CancellationToken cancellationToken) + public Task IsUpToDateAsync( + BuildAction buildAction, + TextWriter logWriter, + IImmutableDictionary globalProperties, + CancellationToken cancellationToken = default) + { + if (Volatile.Read(ref _isDisposed) != 0) { - return IsUpToDateAsync(buildAction, logWriter, ImmutableDictionary.Empty, cancellationToken); + throw new ObjectDisposedException(nameof(BuildUpToDateCheck)); } - async Task<(bool IsUpToDate, string? FailureReason, string? FailureDescription)> IBuildUpToDateCheckValidator.ValidateUpToDateAsync(CancellationToken cancellationToken) + if (buildAction != BuildAction.Build) { - bool isUpToDate = await IsUpToDateInternalAsync(TextWriter.Null, _lastGlobalProperties, isValidationRun: true, cancellationToken); + return TaskResult.False; + } - string? failureReason = isUpToDate ? null : _lastFailureReason; - string? failureDescription = isUpToDate ? null : _lastFailureDescription; + // Cache the last-used set of global properties. We may be asked to validate this up-to-date check + // once the build has completed (in ValidateUpToDateAsync), and will re-use the same set of global + // properties to ensure parity. + _lastGlobalProperties = globalProperties; - return (isUpToDate, failureReason, failureDescription); - } + return IsUpToDateInternalAsync(logWriter, globalProperties, isValidationRun: false, cancellationToken); + } - public Task IsUpToDateAsync( - BuildAction buildAction, - TextWriter logWriter, - IImmutableDictionary globalProperties, - CancellationToken cancellationToken = default) - { - if (Volatile.Read(ref _isDisposed) != 0) - { - throw new ObjectDisposedException(nameof(BuildUpToDateCheck)); - } + private async Task IsUpToDateInternalAsync( + TextWriter logWriter, + IImmutableDictionary globalProperties, + bool isValidationRun, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - if (buildAction != BuildAction.Build) - { - return TaskResult.False; - } + // Start the stopwatch now, so we include any lock acquisition in the timing + var sw = Stopwatch.StartNew(); - // Cache the last-used set of global properties. We may be asked to validate this up-to-date check - // once the build has completed (in ValidateUpToDateAsync), and will re-use the same set of global - // properties to ensure parity. - _lastGlobalProperties = globalProperties; + ISubscription subscription = Volatile.Read(ref _subscription); - return IsUpToDateInternalAsync(logWriter, globalProperties, isValidationRun: false, cancellationToken); - } + return await subscription.RunAsync(CheckAsync, cancellationToken); - private async Task IsUpToDateInternalAsync( - TextWriter logWriter, - IImmutableDictionary globalProperties, - bool isValidationRun, - CancellationToken cancellationToken) + async Task<(bool, ImmutableArray)> CheckAsync(UpToDateCheckConfiguredInput state, IUpToDateCheckStatePersistence persistence, CancellationToken token) { - cancellationToken.ThrowIfCancellationRequested(); - - // Start the stopwatch now, so we include any lock acquisition in the timing - var sw = Stopwatch.StartNew(); + // The subscription object calls GetLatestVersionAsync for project data, which can take a while. + // We separate out the wait time from the overall time, so we can more easily identify when the + // wait is long, versus the check's actual execution time. + TimeSpan waitTime = sw.Elapsed; - ISubscription subscription = Volatile.Read(ref _subscription); + token.ThrowIfCancellationRequested(); - return await subscription.RunAsync(CheckAsync, cancellationToken); + // Short-lived cache of timestamp by path + var timestampCache = new TimestampCache(_fileSystem); - async Task<(bool, ImmutableArray)> CheckAsync(UpToDateCheckConfiguredInput state, IUpToDateCheckStatePersistence persistence, CancellationToken token) + // Ensure we have a context object for the current solution build. + // + // Ordinarily, this is created when the SBM calls ISolutionBuildEventListener.NotifySolutionBuildStarting, + // and cleared again later when the SBM calls ISolutionBuildEventListener.NotifySolutionBuildCompleted. + // + // However there are two cases where it may be null here: + // + // 1. When performing a validation run that continues after the solution build completed, or + // 2. When the build occurs in response to debugging (e.g. F5) in which case the SBM calls the + // FUTDC *before* it invokes any solution build events. + // + // In either case, we construct an event here lazily so that we can correctly test for the + // existence of copy items in CheckCopyToOutputDirectoryItems. + SolutionBuildContext? solutionBuildContext = _solutionBuildContextProvider.CurrentSolutionBuildContext; + if (solutionBuildContext is null) { - // The subscription object calls GetLatestVersionAsync for project data, which can take a while. - // We separate out the wait time from the overall time, so we can more easily identify when the - // wait is long, versus the check's actual execution time. - TimeSpan waitTime = sw.Elapsed; - - token.ThrowIfCancellationRequested(); - - // Short-lived cache of timestamp by path - var timestampCache = new TimestampCache(_fileSystem); - - // Ensure we have a context object for the current solution build. - // - // Ordinarily, this is created when the SBM calls ISolutionBuildEventListener.NotifySolutionBuildStarting, - // and cleared again later when the SBM calls ISolutionBuildEventListener.NotifySolutionBuildCompleted. - // - // However there are two cases where it may be null here: - // - // 1. When performing a validation run that continues after the solution build completed, or - // 2. When the build occurs in response to debugging (e.g. F5) in which case the SBM calls the - // FUTDC *before* it invokes any solution build events. - // - // In either case, we construct an event here lazily so that we can correctly test for the - // existence of copy items in CheckCopyToOutputDirectoryItems. - SolutionBuildContext? solutionBuildContext = _solutionBuildContextProvider.CurrentSolutionBuildContext; - if (solutionBuildContext is null) - { - _solutionBuildEventListener.NotifySolutionBuildStarting(); - solutionBuildContext = _solutionBuildContextProvider.CurrentSolutionBuildContext; - Assumes.NotNull(solutionBuildContext); - } + _solutionBuildEventListener.NotifySolutionBuildStarting(); + solutionBuildContext = _solutionBuildContextProvider.CurrentSolutionBuildContext; + Assumes.NotNull(solutionBuildContext); + } - globalProperties.TryGetValue(FastUpToDateCheckIgnoresKindsGlobalPropertyName, out string? ignoreKindsString); + globalProperties.TryGetValue(FastUpToDateCheckIgnoresKindsGlobalPropertyName, out string? ignoreKindsString); - (LogLevel requestedLogLevel, Guid projectGuid) = await ( - _projectSystemOptions.GetFastUpToDateLoggingLevelAsync(token), - _guidService.GetProjectGuidAsync(token)); + (LogLevel requestedLogLevel, Guid projectGuid) = await ( + _projectSystemOptions.GetFastUpToDateLoggingLevelAsync(token), + _guidService.GetProjectGuidAsync(token)); - var logger = new Log( - logWriter, - this, - UpToDateCheckers, - requestedLogLevel, - _solutionBuildEventListener, - sw, - waitTime, - timestampCache, - _configuredProject.UnconfiguredProject.FullPath ?? "", - projectGuid, - isValidationRun ? null : _telemetryService, - state, - ignoreKindsString, - isValidationRun ? -1 : Interlocked.Increment(ref _checkNumber)); + var logger = new Log( + logWriter, + this, + UpToDateCheckers, + requestedLogLevel, + _solutionBuildEventListener, + sw, + waitTime, + timestampCache, + _configuredProject.UnconfiguredProject.FullPath ?? "", + projectGuid, + isValidationRun ? null : _telemetryService, + state, + ignoreKindsString, + isValidationRun ? -1 : Interlocked.Increment(ref _checkNumber)); - var fileSystemOperations = new FileSystemOperationAggregator(_fileSystem, logger); + var fileSystemOperations = new FileSystemOperationAggregator(_fileSystem, logger); - logger.FileSystemOperations = fileSystemOperations; + logger.FileSystemOperations = fileSystemOperations; - HashSet copyItemPaths = new(StringComparers.Paths); + HashSet copyItemPaths = new(StringComparers.Paths); - try + try + { + HashSet? ignoreKinds = null; + if (ignoreKindsString is not null) { - HashSet? ignoreKinds = null; - if (ignoreKindsString is not null) - { - ignoreKinds = new HashSet(new LazyStringSplit(ignoreKindsString, ';'), KindNameComparer); + ignoreKinds = new HashSet(new LazyStringSplit(ignoreKindsString, ';'), KindNameComparer); - if (requestedLogLevel >= LogLevel.Info && ignoreKinds.Count != 0) - { - logger.Info(nameof(VSResources.FUTD_IgnoringKinds_1), ignoreKindsString); - } + if (requestedLogLevel >= LogLevel.Info && ignoreKinds.Count != 0) + { + logger.Info(nameof(VSResources.FUTD_IgnoringKinds_1), ignoreKindsString); } + } - // If we're limiting the build to a particular target framework, limit the set of - // configured inputs we check to those that match the framework. - globalProperties.TryGetValue(TargetFrameworkGlobalPropertyName, out string? buildTargetFramework); - IEnumerable implicitStatesToCheck = Strings.IsNullOrEmpty(buildTargetFramework) - ? state.ImplicitInputs - : state.ImplicitInputs.Where(input => ConfiguredInputMatchesTargetFramework(input, buildTargetFramework)); + // If we're limiting the build to a particular target framework, limit the set of + // configured inputs we check to those that match the framework. + globalProperties.TryGetValue(TargetFrameworkGlobalPropertyName, out string? buildTargetFramework); + IEnumerable implicitStatesToCheck = Strings.IsNullOrEmpty(buildTargetFramework) + ? state.ImplicitInputs + : state.ImplicitInputs.Where(input => ConfiguredInputMatchesTargetFramework(input, buildTargetFramework)); - // Note that if we find a particular configuration is out of date and exit early, - // all the configurations we're going to build still count as checked. - ImmutableArray checkedConfigurations = implicitStatesToCheck.Select(state => state.ProjectConfiguration).ToImmutableArray(); + // Note that if we find a particular configuration is out of date and exit early, + // all the configurations we're going to build still count as checked. + ImmutableArray checkedConfigurations = implicitStatesToCheck.Select(state => state.ProjectConfiguration).ToImmutableArray(); - bool logConfigurations = state.ImplicitInputs.Length > 1 && logger.Level >= LogLevel.Info; + bool logConfigurations = state.ImplicitInputs.Length > 1 && logger.Level >= LogLevel.Info; - // Loop over target frameworks (or whatever other implicit configurations exist) - foreach (UpToDateCheckImplicitConfiguredInput implicitState in implicitStatesToCheck) + // Loop over target frameworks (or whatever other implicit configurations exist) + foreach (UpToDateCheckImplicitConfiguredInput implicitState in implicitStatesToCheck) + { + token.ThrowIfCancellationRequested(); + + if (logConfigurations) { - token.ThrowIfCancellationRequested(); + logger.Info(nameof(VSResources.FUTD_CheckingConfiguration_1), implicitState.ProjectConfiguration.GetDisplayString()); + logger.Indent++; + } - if (logConfigurations) + if (implicitState.IsDisabled) + { + if (isValidationRun) { - logger.Info(nameof(VSResources.FUTD_CheckingConfiguration_1), implicitState.ProjectConfiguration.GetDisplayString()); - logger.Indent++; + // We don't run validation if the FUTDC is disabled. So pretend we're up to date. + return (true, checkedConfigurations); } - - if (implicitState.IsDisabled) + else { - if (isValidationRun) - { - // We don't run validation if the FUTDC is disabled. So pretend we're up to date. - return (true, checkedConfigurations); - } - else - { - logger.Fail("Disabled", nameof(VSResources.FUTD_DisableFastUpToDateCheckTrue)); - return (false, checkedConfigurations); - } + logger.Fail("Disabled", nameof(VSResources.FUTD_DisableFastUpToDateCheckTrue)); + return (false, checkedConfigurations); } + } - string? path = _configuredProject.UnconfiguredProject.FullPath; - - DateTime? lastSuccessfulBuildStartTimeUtc = path is null - ? null - : await persistence.RestoreLastSuccessfulBuildStateAsync( - path, - implicitState.ProjectConfiguration.Dimensions, - CancellationToken.None); + string? path = _configuredProject.UnconfiguredProject.FullPath; - Assumes.NotNull(implicitState.ProjectTargetPath); + DateTime? lastSuccessfulBuildStartTimeUtc = path is null + ? null + : await persistence.RestoreLastSuccessfulBuildStateAsync( + path, + implicitState.ProjectConfiguration.Dimensions, + CancellationToken.None); - // We may have an incomplete set of copy items. - // We check timestamps of whatever items we can find, but only perform acceleration when the full set is available. - CopyItemsResult copyInfo = _copyItemAggregator.TryGatherCopyItemsForProject(implicitState.ProjectTargetPath, logger); + Assumes.NotNull(implicitState.ProjectTargetPath); - bool? isBuildAccelerationEnabled = await IsBuildAccelerationEnabledAsync(copyInfo.IsComplete, copyInfo.DuplicateCopyItemRelativeTargetPaths, implicitState); + // We may have an incomplete set of copy items. + // We check timestamps of whatever items we can find, but only perform acceleration when the full set is available. + CopyItemsResult copyInfo = _copyItemAggregator.TryGatherCopyItemsForProject(implicitState.ProjectTargetPath, logger); - var configuredFileSystemOperations = new ConfiguredFileSystemOperationAggregator(fileSystemOperations, isBuildAccelerationEnabled, copyInfo.TargetsWithoutReferenceAssemblies); + bool? isBuildAccelerationEnabled = await IsBuildAccelerationEnabledAsync(copyInfo.IsComplete, copyInfo.DuplicateCopyItemRelativeTargetPaths, implicitState); - string outputFullPath = Path.Combine(implicitState.MSBuildProjectDirectory, implicitState.OutputRelativeOrFullPath); + var configuredFileSystemOperations = new ConfiguredFileSystemOperationAggregator(fileSystemOperations, isBuildAccelerationEnabled, copyInfo.TargetsWithoutReferenceAssemblies); - copyItemPaths.UnionWith(implicitState.ProjectCopyData.CopyItems.Select(copyItem => Path.Combine(outputFullPath, copyItem.RelativeTargetPath))); + string outputFullPath = Path.Combine(implicitState.MSBuildProjectDirectory, implicitState.OutputRelativeOrFullPath); - if (!CheckGlobalConditions(logger, lastSuccessfulBuildStartTimeUtc, validateFirstRun: !isValidationRun, implicitState) || - !CheckInputsAndOutputs(logger, lastSuccessfulBuildStartTimeUtc, timestampCache, implicitState, ignoreKinds, token) || - !CheckBuiltFromInputFiles(logger, timestampCache, implicitState, token) || - !CheckMarkers(logger, timestampCache, implicitState, isBuildAccelerationEnabled, fileSystemOperations) || - !CheckCopyToOutputDirectoryItems(logger, implicitState, copyInfo.ItemsByProject, configuredFileSystemOperations, isBuildAccelerationEnabled, solutionBuildContext, token)) - { - return (false, checkedConfigurations); - } + copyItemPaths.UnionWith(implicitState.ProjectCopyData.CopyItems.Select(copyItem => Path.Combine(outputFullPath, copyItem.RelativeTargetPath))); - if (logConfigurations) - { - logger.Indent--; - } + if (!CheckGlobalConditions(logger, lastSuccessfulBuildStartTimeUtc, validateFirstRun: !isValidationRun, implicitState) || + !CheckInputsAndOutputs(logger, lastSuccessfulBuildStartTimeUtc, timestampCache, implicitState, ignoreKinds, token) || + !CheckBuiltFromInputFiles(logger, timestampCache, implicitState, token) || + !CheckMarkers(logger, timestampCache, implicitState, isBuildAccelerationEnabled, fileSystemOperations) || + !CheckCopyToOutputDirectoryItems(logger, implicitState, copyInfo.ItemsByProject, configuredFileSystemOperations, isBuildAccelerationEnabled, solutionBuildContext, token)) + { + return (false, checkedConfigurations); } - if (!isValidationRun) + if (logConfigurations) { - (bool success, int copyCount) = fileSystemOperations.TryApplyFileSystemOperations(); + logger.Indent--; + } + } - if (!success) - { - // Details of the failure will already have been logged. - return (false, checkedConfigurations); - } + if (!isValidationRun) + { + (bool success, int copyCount) = fileSystemOperations.TryApplyFileSystemOperations(); - if (copyCount != 0) - { - logger.Info(nameof(VSResources.FUTD_BuildAccelerationSummary_1), copyCount); - } + if (!success) + { + // Details of the failure will already have been logged. + return (false, checkedConfigurations); + } - logger.UpToDate(copyCount); + if (copyCount != 0) + { + logger.Info(nameof(VSResources.FUTD_BuildAccelerationSummary_1), copyCount); } - return (true, checkedConfigurations); + logger.UpToDate(copyCount); } - catch (Exception ex) + + return (true, checkedConfigurations); + } + catch (Exception ex) + { + return (logger.Fail("Exception", nameof(VSResources.FUTD_Exception_1), ex), ImmutableArray.Empty); + } + finally + { + if (fileSystemOperations.IsAccelerationCandidate && fileSystemOperations.IsAccelerationEnabled is null) { - return (logger.Fail("Exception", nameof(VSResources.FUTD_Exception_1), ex), ImmutableArray.Empty); + // We didn't copy anything, but we did find a candidate for build acceleration, + // and the project does not specify AccelerateBuildsInVisualStudio. Log a message to + // let the user know that their project might benefit from Build Acceleration. + logger.Minimal(nameof(VSResources.FUTD_AccelerationCandidate)); } - finally + + if (fileSystemOperations.IsAccelerationEnabled is true && fileSystemOperations.TargetsWithoutReferenceAssemblies is { Count: > 0 }) { - if (fileSystemOperations.IsAccelerationCandidate && fileSystemOperations.IsAccelerationEnabled is null) - { - // We didn't copy anything, but we did find a candidate for build acceleration, - // and the project does not specify AccelerateBuildsInVisualStudio. Log a message to - // let the user know that their project might benefit from Build Acceleration. - logger.Minimal(nameof(VSResources.FUTD_AccelerationCandidate)); - } + // This project is configured to use build acceleration, but some of its references do not + // produce reference assemblies. Log a message to let the user know that they may be able + // to improve their build performance by enabling the production of reference assemblies. + logger.Minimal(nameof(VSResources.FUTD_NotAllReferencesProduceReferenceAssemblies_1), string.Join(", ", fileSystemOperations.TargetsWithoutReferenceAssemblies.Select(s => $"'{s}'"))); + } - if (fileSystemOperations.IsAccelerationEnabled is true && fileSystemOperations.TargetsWithoutReferenceAssemblies is { Count: > 0 }) - { - // This project is configured to use build acceleration, but some of its references do not - // produce reference assemblies. Log a message to let the user know that they may be able - // to improve their build performance by enabling the production of reference assemblies. - logger.Minimal(nameof(VSResources.FUTD_NotAllReferencesProduceReferenceAssemblies_1), string.Join(", ", fileSystemOperations.TargetsWithoutReferenceAssemblies.Select(s => $"'{s}'"))); - } + logger.Verbose(nameof(VSResources.FUTD_Completed), sw.Elapsed.TotalMilliseconds); - logger.Verbose(nameof(VSResources.FUTD_Completed), sw.Elapsed.TotalMilliseconds); + _lastFailureReason = logger.FailureReason; + _lastFailureDescription = logger.FailureDescription; - _lastFailureReason = logger.FailureReason; - _lastFailureDescription = logger.FailureDescription; + _lastCopyTargetsFromThisProject = copyItemPaths; + } - _lastCopyTargetsFromThisProject = copyItemPaths; - } + async ValueTask IsBuildAccelerationEnabledAsync(bool isCopyItemsComplete, IReadOnlyList? duplicateCopyItemRelativeTargetPaths, UpToDateCheckImplicitConfiguredInput implicitState) + { + // Build acceleration requires: + // + // 1. being enabled, either in the project or via feature flags, and + // 2. having a full set of copy items, and + // 3. not having any project references known to be incompatible with Build Acceleration, and + // 4. not having any duplicate copy items that would overwrite one another in the output directory (due to ordering issues). + // + // Being explicitly disabled in the project overrides any feature flag. - async ValueTask IsBuildAccelerationEnabledAsync(bool isCopyItemsComplete, IReadOnlyList? duplicateCopyItemRelativeTargetPaths, UpToDateCheckImplicitConfiguredInput implicitState) + if (implicitState.PresentBuildAccelerationIncompatiblePackages.Any()) { - // Build acceleration requires: - // - // 1. being enabled, either in the project or via feature flags, and - // 2. having a full set of copy items, and - // 3. not having any project references known to be incompatible with Build Acceleration, and - // 4. not having any duplicate copy items that would overwrite one another in the output directory (due to ordering issues). - // - // Being explicitly disabled in the project overrides any feature flag. - - if (implicitState.PresentBuildAccelerationIncompatiblePackages.Any()) - { - // At least one package reference exists that is incompatible with build acceleration. + // At least one package reference exists that is incompatible with build acceleration. - // Check the log level to avoid the allocating string.Join unless needed. - if (logger.Level >= LogLevel.Info) - { - logger.Info( - nameof(VSResources.BuildAccelerationDisabledDueToIncompatiblePackageReferences_1), - string.Join(", ", implicitState.PresentBuildAccelerationIncompatiblePackages.Select(id => $"'{id}'"))); - } - - return false; + // Check the log level to avoid the allocating string.Join unless needed. + if (logger.Level >= LogLevel.Info) + { + logger.Info( + nameof(VSResources.BuildAccelerationDisabledDueToIncompatiblePackageReferences_1), + string.Join(", ", implicitState.PresentBuildAccelerationIncompatiblePackages.Select(id => $"'{id}'"))); } - // Start with the preference specified in the project. - bool? isEnabledInProject = implicitState.IsBuildAccelerationEnabled; + return false; + } + + // Start with the preference specified in the project. + bool? isEnabledInProject = implicitState.IsBuildAccelerationEnabled; - bool isEnabled; + bool isEnabled; - if (isEnabledInProject is bool b) + if (isEnabledInProject is bool b) + { + isEnabled = b; + } + else + { + // No value has been specified in the project. Query the environment to decide (e.g. feature flag). + if (await _projectSystemOptions.IsBuildAccelerationEnabledByDefaultAsync(cancellationToken)) { - isEnabled = b; + // The user has opted-in via feature flag. Set this to true and carry on with further checks. + logger.Info(nameof(VSResources.FUTD_BuildAccelerationEnabledViaFeatureFlag)); + isEnabled = true; } else { - // No value has been specified in the project. Query the environment to decide (e.g. feature flag). - if (await _projectSystemOptions.IsBuildAccelerationEnabledByDefaultAsync(cancellationToken)) - { - // The user has opted-in via feature flag. Set this to true and carry on with further checks. - logger.Info(nameof(VSResources.FUTD_BuildAccelerationEnabledViaFeatureFlag)); - isEnabled = true; - } - else - { - logger.Info(nameof(VSResources.FUTD_BuildAccelerationIsNotEnabledForThisProject)); - return null; - } + logger.Info(nameof(VSResources.FUTD_BuildAccelerationIsNotEnabledForThisProject)); + return null; } + } - if (isEnabled) + if (isEnabled) + { + if (!isCopyItemsComplete) { - if (!isCopyItemsComplete) - { - logger.Info(nameof(VSResources.FUTD_AccelerationDisabledCopyItemsIncomplete)); - return false; - } - - if (duplicateCopyItemRelativeTargetPaths is not null) - { - logger.Info(nameof(VSResources.FUTD_AccelerationDisabledDuplicateCopyItemsIncomplete_1), string.Join(", ", duplicateCopyItemRelativeTargetPaths.Select(path => $"'{path}'"))); - return false; - } - - if (isEnabledInProject is not null) - { - // Don't log if isEnabledInProject is null, as we already log that status above. - logger.Info(nameof(VSResources.FUTD_BuildAccelerationEnabledViaProperty)); - } - - return true; + logger.Info(nameof(VSResources.FUTD_AccelerationDisabledCopyItemsIncomplete)); + return false; } - else + + if (duplicateCopyItemRelativeTargetPaths is not null) { - // The project explicitly opts out. - logger.Info(nameof(VSResources.FUTD_AccelerationDisabledForProject)); + logger.Info(nameof(VSResources.FUTD_AccelerationDisabledDuplicateCopyItemsIncomplete_1), string.Join(", ", duplicateCopyItemRelativeTargetPaths.Select(path => $"'{path}'"))); return false; } + + if (isEnabledInProject is not null) + { + // Don't log if isEnabledInProject is null, as we already log that status above. + logger.Info(nameof(VSResources.FUTD_BuildAccelerationEnabledViaProperty)); + } + + return true; + } + else + { + // The project explicitly opts out. + logger.Info(nameof(VSResources.FUTD_AccelerationDisabledForProject)); + return false; } } } + } - public Task IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken = default) - { - return _projectSystemOptions.GetIsFastUpToDateCheckEnabledAsync(cancellationToken); - } + public Task IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken = default) + { + return _projectSystemOptions.GetIsFastUpToDateCheckEnabledAsync(cancellationToken); + } - internal readonly struct TestAccessor(BuildUpToDateCheck check) - { - public void SetSubscription(ISubscription subscription) => check._subscription = subscription; - } + internal readonly struct TestAccessor(BuildUpToDateCheck check) + { + public void SetSubscription(ISubscription subscription) => check._subscription = subscription; + } - /// For unit testing only. + /// For unit testing only. #pragma warning disable RS0043 // Do not call 'GetTestAccessor()' - internal TestAccessor TestAccess => new(this); + internal TestAccessor TestAccess => new(this); #pragma warning restore RS0043 - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IBuildUpToDateCheckValidator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IBuildUpToDateCheckValidator.cs index 6ef5c6e3f0..ad56800110 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IBuildUpToDateCheckValidator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IBuildUpToDateCheckValidator.cs @@ -2,22 +2,21 @@ using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private)] +internal interface IBuildUpToDateCheckValidator { - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private)] - internal interface IBuildUpToDateCheckValidator - { - /// - /// Validates that the project inputs and outputs are up-to-date with respect to one another. - /// - /// - /// This method is intended to determine whether the project is correctly up-to-date after build. - /// The difference between this method and is - /// that this method will not mutate any internal state. If in future that method is made idempotent, then - /// this method (and probably the whole interface) could be removed. - /// - /// A token that is cancelled if the caller loses interest in the result. - /// - Task<(bool IsUpToDate, string? FailureReason, string? FailureDescription)> ValidateUpToDateAsync(CancellationToken cancellationToken = default); - } + /// + /// Validates that the project inputs and outputs are up-to-date with respect to one another. + /// + /// + /// This method is intended to determine whether the project is correctly up-to-date after build. + /// The difference between this method and is + /// that this method will not mutate any internal state. If in future that method is made idempotent, then + /// this method (and probably the whole interface) could be removed. + /// + /// A token that is cancelled if the caller loses interest in the result. + /// + Task<(bool IsUpToDate, string? FailureReason, string? FailureDescription)> ValidateUpToDateAsync(CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckConfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckConfiguredInputDataSource.cs index d475b63d44..03679426dc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckConfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckConfiguredInputDataSource.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Project value data source for instances of . +/// +/// +/// Aggregates data from implicitly active configurations via +/// . +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IUpToDateCheckConfiguredInputDataSource : IProjectValueDataSource { - /// - /// Project value data source for instances of . - /// - /// - /// Aggregates data from implicitly active configurations via - /// . - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IUpToDateCheckConfiguredInputDataSource : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckHost.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckHost.cs index e69d4654b6..ea4850c174 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckHost.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckHost.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Allows the fast up-to-date check to query the host for relevant information. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IUpToDateCheckHost { /// - /// Allows the fast up-to-date check to query the host for relevant information. + /// Identifies whether design-time builds are available in the host. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IUpToDateCheckHost - { - /// - /// Identifies whether design-time builds are available in the host. - /// - /// - /// For example, when Visual Studio runs in "command line" mode, design-time builds do not occur. - /// - /// A token via which the operation may be cancelled. - /// A task that, when completed, indicates whether design time builds are available in the host. - ValueTask HasDesignTimeBuildsAsync(CancellationToken cancellationToken); - } + /// + /// For example, when Visual Studio runs in "command line" mode, design-time builds do not occur. + /// + /// A token via which the operation may be cancelled. + /// A task that, when completed, indicates whether design time builds are available in the host. + ValueTask HasDesignTimeBuildsAsync(CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckImplicitConfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckImplicitConfiguredInputDataSource.cs index d39aa74ba4..344859b33b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckImplicitConfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckImplicitConfiguredInputDataSource.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Project value data source for instances of . +/// +/// +/// Links several CPS-provided data sources together, producing a snapshot of data that the +/// up-to-date check will need in order to compute its result correctly. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IUpToDateCheckImplicitConfiguredInputDataSource : IProjectValueDataSource { - /// - /// Project value data source for instances of . - /// - /// - /// Links several CPS-provided data sources together, producing a snapshot of data that the - /// up-to-date check will need in order to compute its result correctly. - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IUpToDateCheckImplicitConfiguredInputDataSource : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckStatePersistence.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckStatePersistence.cs index 38482dee29..5de2104b0c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckStatePersistence.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/IUpToDateCheckStatePersistence.cs @@ -1,59 +1,58 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Persists fast up-to-date check state across solution lifetimes. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private)] +internal interface IUpToDateCheckStatePersistence { /// - /// Persists fast up-to-date check state across solution lifetimes. + /// Retrieves the stored up-to-date check state for a given configured project related to the set of project items. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private)] - internal interface IUpToDateCheckStatePersistence - { - /// - /// Retrieves the stored up-to-date check state for a given configured project related to the set of project items. - /// - /// The full path of the project. - /// The map of dimension names and values that describes the project configuration. - /// Allows cancelling this asynchronous operation. - /// The hash and time at which items were last known to have changed (in UTC), or if unknown. - Task<(int ItemHash, DateTime? ItemsChangedAtUtc)?> RestoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken); + /// The full path of the project. + /// The map of dimension names and values that describes the project configuration. + /// Allows cancelling this asynchronous operation. + /// The hash and time at which items were last known to have changed (in UTC), or if unknown. + Task<(int ItemHash, DateTime? ItemsChangedAtUtc)?> RestoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken); - /// - /// Retrieves the stored up-to-date check state for a given configured project. - /// - /// - /// This value is required in order to protect against the race condition described in - /// https://github.com/dotnet/project-system/issues/4014. If source files are modified - /// during a compilation, but before that compilation's outputs are produced, then the - /// changed input file's timestamp will be earlier than the compilation output, making - /// it seem as though the compilation is up to date when in fact the input was not - /// included in that compilation. Comparing against compilation start time fixes this - /// issue. - /// - /// The full path of the project. - /// The map of dimension names and values that describes the project configuration. - /// Allows cancelling this asynchronous operation. - /// The time as which the last successful build started (in UTC), or if unknown. - Task RestoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken); + /// + /// Retrieves the stored up-to-date check state for a given configured project. + /// + /// + /// This value is required in order to protect against the race condition described in + /// https://github.com/dotnet/project-system/issues/4014. If source files are modified + /// during a compilation, but before that compilation's outputs are produced, then the + /// changed input file's timestamp will be earlier than the compilation output, making + /// it seem as though the compilation is up to date when in fact the input was not + /// included in that compilation. Comparing against compilation start time fixes this + /// issue. + /// + /// The full path of the project. + /// The map of dimension names and values that describes the project configuration. + /// Allows cancelling this asynchronous operation. + /// The time as which the last successful build started (in UTC), or if unknown. + Task RestoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken); - /// - /// Stores up-to-date check state for a given configured project, related to the set of project items. - /// - /// The full path of the project. - /// The map of dimension names and values that describes the project configuration. - /// The hash of items to be stored. - /// The time at which items were last known to have changed (in UTC), or if we haven't observed them change. - /// Allows cancelling this asynchronous operation. - /// A task that completes when this operation has finished. - Task StoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, int itemHash, DateTime? itemsChangedAtUtc, CancellationToken cancellationToken); + /// + /// Stores up-to-date check state for a given configured project, related to the set of project items. + /// + /// The full path of the project. + /// The map of dimension names and values that describes the project configuration. + /// The hash of items to be stored. + /// The time at which items were last known to have changed (in UTC), or if we haven't observed them change. + /// Allows cancelling this asynchronous operation. + /// A task that completes when this operation has finished. + Task StoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, int itemHash, DateTime? itemsChangedAtUtc, CancellationToken cancellationToken); - /// - /// Stores up-to-date check state for a given configured project, related to the last successful build time. - /// - /// The full path of the project. - /// The map of dimension names and values that describes the project configuration. - /// The time at which the project's last successful build started (in UTC). - /// Allows cancelling this asynchronous operation. - /// A task that completes when this operation has finished. - Task StoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, DateTime lastSuccessfulBuildStartedAtUtc, CancellationToken cancellationToken); - } + /// + /// Stores up-to-date check state for a given configured project, related to the last successful build time. + /// + /// The full path of the project. + /// The map of dimension names and values that describes the project configuration. + /// The time at which the project's last successful build started (in UTC). + /// Allows cancelling this asynchronous operation. + /// A task that completes when this operation has finished. + Task StoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, DateTime lastSuccessfulBuildStartedAtUtc, CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckBuildEventNotifier.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckBuildEventNotifier.cs index 0e7b6602e8..b4312c7559 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckBuildEventNotifier.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckBuildEventNotifier.cs @@ -4,183 +4,182 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Listens for build events and notifies the fast up-to-date check of them +/// via . +/// +[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] +[AppliesTo(BuildUpToDateCheck.AppliesToExpression)] +internal sealed class UpToDateCheckBuildEventNotifier : OnceInitializedOnceDisposedAsync, IVsUpdateSolutionEvents2, IProjectDynamicLoadComponent { - /// - /// Listens for build events and notifies the fast up-to-date check of them - /// via . - /// - [Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - internal sealed class UpToDateCheckBuildEventNotifier : OnceInitializedOnceDisposedAsync, IVsUpdateSolutionEvents2, IProjectDynamicLoadComponent + private readonly IProjectService _projectService; + private readonly ISolutionBuildManager _solutionBuildManager; + private readonly ISolutionBuildEventListener _solutionBuildEventListener; + private readonly IProjectThreadingService _threadingService; + private readonly IProjectFaultHandlerService _faultHandlerService; + private IAsyncDisposable? _solutionBuildEventsSubscription; + + [ImportingConstructor] + public UpToDateCheckBuildEventNotifier( + JoinableTaskContext joinableTaskContext, + IProjectService projectService, + IProjectThreadingService threadingService, + IProjectFaultHandlerService faultHandlerService, + ISolutionBuildManager solutionBuildManager, + ISolutionBuildEventListener solutionBuildEventListener) + : base(new(joinableTaskContext)) { - private readonly IProjectService _projectService; - private readonly ISolutionBuildManager _solutionBuildManager; - private readonly ISolutionBuildEventListener _solutionBuildEventListener; - private readonly IProjectThreadingService _threadingService; - private readonly IProjectFaultHandlerService _faultHandlerService; - private IAsyncDisposable? _solutionBuildEventsSubscription; - - [ImportingConstructor] - public UpToDateCheckBuildEventNotifier( - JoinableTaskContext joinableTaskContext, - IProjectService projectService, - IProjectThreadingService threadingService, - IProjectFaultHandlerService faultHandlerService, - ISolutionBuildManager solutionBuildManager, - ISolutionBuildEventListener solutionBuildEventListener) - : base(new(joinableTaskContext)) - { - _projectService = projectService; - _threadingService = threadingService; - _faultHandlerService = faultHandlerService; - _solutionBuildManager = solutionBuildManager; - _solutionBuildEventListener = solutionBuildEventListener; - } + _projectService = projectService; + _threadingService = threadingService; + _faultHandlerService = faultHandlerService; + _solutionBuildManager = solutionBuildManager; + _solutionBuildEventListener = solutionBuildEventListener; + } - public Task LoadAsync() => InitializeAsync(); - public Task UnloadAsync() => DisposeAsync(); + public Task LoadAsync() => InitializeAsync(); + public Task UnloadAsync() => DisposeAsync(); - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _solutionBuildEventsSubscription = await _solutionBuildManager.SubscribeSolutionEventsAsync(this); - } + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _solutionBuildEventsSubscription = await _solutionBuildManager.SubscribeSolutionEventsAsync(this); + } - protected override Task DisposeCoreAsync(bool initialized) + protected override Task DisposeCoreAsync(bool initialized) + { + if (initialized) { - if (initialized) + if (_solutionBuildEventsSubscription is not null) { - if (_solutionBuildEventsSubscription is not null) - { - return _solutionBuildEventsSubscription.DisposeAsync().AsTask(); - } + return _solutionBuildEventsSubscription.DisposeAsync().AsTask(); } - - return Task.CompletedTask; } - /// - /// Called right before a project configuration starts building. Called even if the project is up-to-date. - /// - int IVsUpdateSolutionEvents2.UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) + return Task.CompletedTask; + } + + /// + /// Called right before a project configuration starts building. Called even if the project is up-to-date. + /// + int IVsUpdateSolutionEvents2.UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) + { + if (IsBuild(dwAction, out bool isRebuild)) { - if (IsBuild(dwAction, out bool isRebuild)) - { - IEnumerable? listeners = FindActiveConfiguredProviders(pHierProj, out _); + IEnumerable? listeners = FindActiveConfiguredProviders(pHierProj, out _); - // Notify the solution build listener that a project build is starting. - // Note there's no equivalent for build completion, as the fast up-to-date check handles - // that for the projects it tracks. We don't need to know when other project types complete. - _solutionBuildEventListener.NotifyProjectBuildStarting(isRebuild); + // Notify the solution build listener that a project build is starting. + // Note there's no equivalent for build completion, as the fast up-to-date check handles + // that for the projects it tracks. We don't need to know when other project types complete. + _solutionBuildEventListener.NotifyProjectBuildStarting(isRebuild); - if (listeners is not null) - { - var buildStartedTimeUtc = DateTime.UtcNow; + if (listeners is not null) + { + var buildStartedTimeUtc = DateTime.UtcNow; - foreach (IProjectBuildEventListener listener in listeners) - { - listener.NotifyBuildStarting(buildStartedTimeUtc); - } + foreach (IProjectBuildEventListener listener in listeners) + { + listener.NotifyBuildStarting(buildStartedTimeUtc); } } - - return HResult.OK; } - /// - /// Called right after a project configuration is finished building. Called even if the project is up-to-date. - /// - int IVsUpdateSolutionEvents2.UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) + return HResult.OK; + } + + /// + /// Called right after a project configuration is finished building. Called even if the project is up-to-date. + /// + int IVsUpdateSolutionEvents2.UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) + { + if (fCancel == 0 && IsBuild(dwAction, out bool isRebuild)) { - if (fCancel == 0 && IsBuild(dwAction, out bool isRebuild)) - { - IEnumerable? listeners = FindActiveConfiguredProviders(pHierProj, out UnconfiguredProject? unconfiguredProject); + IEnumerable? listeners = FindActiveConfiguredProviders(pHierProj, out UnconfiguredProject? unconfiguredProject); - if (listeners is not null) + if (listeners is not null) + { + JoinableTask task = _threadingService.JoinableTaskFactory.RunAsync(async () => { - JoinableTask task = _threadingService.JoinableTaskFactory.RunAsync(async () => - { - // Do this work off the main thread - await TaskScheduler.Default; + // Do this work off the main thread + await TaskScheduler.Default; - foreach (IProjectBuildEventListener listener in listeners) - { - await listener.NotifyBuildCompletedAsync(wasSuccessful: fSuccess != 0, isRebuild); - } - }); + foreach (IProjectBuildEventListener listener in listeners) + { + await listener.NotifyBuildCompletedAsync(wasSuccessful: fSuccess != 0, isRebuild); + } + }); - _faultHandlerService.Forget(task.Task, unconfiguredProject); - } + _faultHandlerService.Forget(task.Task, unconfiguredProject); } - - return HResult.OK; } - /// - /// Returns if indicates either a build or rebuild. - /// - private static bool IsBuild(uint options, out bool isRebuild) - { - const VSSOLNBUILDUPDATEFLAGS anyBuildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD; - const VSSOLNBUILDUPDATEFLAGS rebuildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD | VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_FORCE_UPDATE; + return HResult.OK; + } - var operation = (VSSOLNBUILDUPDATEFLAGS)options; + /// + /// Returns if indicates either a build or rebuild. + /// + private static bool IsBuild(uint options, out bool isRebuild) + { + const VSSOLNBUILDUPDATEFLAGS anyBuildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD; + const VSSOLNBUILDUPDATEFLAGS rebuildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD | VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_FORCE_UPDATE; - isRebuild = (operation & rebuildFlags) == rebuildFlags; + var operation = (VSSOLNBUILDUPDATEFLAGS)options; - return (operation & anyBuildFlags) == anyBuildFlags; - } + isRebuild = (operation & rebuildFlags) == rebuildFlags; - private IEnumerable? FindActiveConfiguredProviders(IVsHierarchy vsHierarchy, out UnconfiguredProject? unconfiguredProject) - { - unconfiguredProject = _projectService.GetUnconfiguredProject(vsHierarchy, appliesToExpression: BuildUpToDateCheck.AppliesToExpression); + return (operation & anyBuildFlags) == anyBuildFlags; + } - if (unconfiguredProject is not null) - { - IActiveConfiguredProjectProvider activeConfiguredProjectProvider = unconfiguredProject.Services.ExportProvider.GetExportedValue(); + private IEnumerable? FindActiveConfiguredProviders(IVsHierarchy vsHierarchy, out UnconfiguredProject? unconfiguredProject) + { + unconfiguredProject = _projectService.GetUnconfiguredProject(vsHierarchy, appliesToExpression: BuildUpToDateCheck.AppliesToExpression); - ConfiguredProject? configuredProject = activeConfiguredProjectProvider.ActiveConfiguredProject; + if (unconfiguredProject is not null) + { + IActiveConfiguredProjectProvider activeConfiguredProjectProvider = unconfiguredProject.Services.ExportProvider.GetExportedValue(); - if (configuredProject is not null) - { - return configuredProject.Services.ExportProvider.GetExportedValues(); - } - } + ConfiguredProject? configuredProject = activeConfiguredProjectProvider.ActiveConfiguredProject; - return null; + if (configuredProject is not null) + { + return configuredProject.Services.ExportProvider.GetExportedValues(); + } } - int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate) - { - _solutionBuildEventListener.NotifySolutionBuildStarting(); + return null; + } - return HResult.OK; - } - int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) - { - _solutionBuildEventListener.NotifySolutionBuildCompleted(cancelled: false); + int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate) + { + _solutionBuildEventListener.NotifySolutionBuildStarting(); - return HResult.OK; - } + return HResult.OK; + } + int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) + { + _solutionBuildEventListener.NotifySolutionBuildCompleted(cancelled: false); - int IVsUpdateSolutionEvents.UpdateSolution_Cancel() - { - _solutionBuildEventListener.NotifySolutionBuildCompleted(cancelled: true); + return HResult.OK; + } - return HResult.OK; - } + int IVsUpdateSolutionEvents.UpdateSolution_Cancel() + { + _solutionBuildEventListener.NotifySolutionBuildCompleted(cancelled: true); + + return HResult.OK; + } - #region IVsUpdateSolutionEvents stubs + #region IVsUpdateSolutionEvents stubs - int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; - int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; + int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; + int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; - int IVsUpdateSolutionEvents2.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_StartUpdate(ref int pfCancelUpdate) => HResult.OK; - int IVsUpdateSolutionEvents2.UpdateSolution_Cancel() => HResult.OK; + int IVsUpdateSolutionEvents2.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_Begin(ref int pfCancelUpdate) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_StartUpdate(ref int pfCancelUpdate) => HResult.OK; + int IVsUpdateSolutionEvents2.UpdateSolution_Cancel() => HResult.OK; - #endregion - } + #endregion } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInput.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInput.cs index dad4e030d0..1ef97c1f64 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInput.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInput.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Models all up-to-date check state for a single configuration. +/// +/// +/// Produced by . +/// +internal sealed class UpToDateCheckConfiguredInput { /// - /// Models all up-to-date check state for a single configuration. + /// Gets the up-to-date check state associated with each of the implicitly active + /// configurations. /// - /// - /// Produced by . - /// - internal sealed class UpToDateCheckConfiguredInput - { - /// - /// Gets the up-to-date check state associated with each of the implicitly active - /// configurations. - /// - public ImmutableArray ImplicitInputs { get; } + public ImmutableArray ImplicitInputs { get; } - /// - /// Initializes a new instance of the class. - /// - public UpToDateCheckConfiguredInput(ImmutableArray implicitInputs) - { - ImplicitInputs = implicitInputs; - } + /// + /// Initializes a new instance of the class. + /// + public UpToDateCheckConfiguredInput(ImmutableArray implicitInputs) + { + ImplicitInputs = implicitInputs; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInputDataSource.cs index 9687bdb22a..5ca6a8c309 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckConfiguredInputDataSource.cs @@ -3,55 +3,54 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +[Export(typeof(IUpToDateCheckConfiguredInputDataSource))] +[AppliesTo(BuildUpToDateCheck.AppliesToExpression)] +internal sealed class UpToDateCheckConfiguredInputDataSource : ChainedProjectValueDataSourceBase, IUpToDateCheckConfiguredInputDataSource { - /// - [Export(typeof(IUpToDateCheckConfiguredInputDataSource))] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - internal sealed class UpToDateCheckConfiguredInputDataSource : ChainedProjectValueDataSourceBase, IUpToDateCheckConfiguredInputDataSource + private readonly ConfiguredProject _configuredProject; + private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; + + [ImportingConstructor] + public UpToDateCheckConfiguredInputDataSource( + ConfiguredProject containingProject, + IActiveConfigurationGroupService activeConfigurationGroupService) + : base(containingProject, synchronousDisposal: false, registerDataSource: false) { - private readonly ConfiguredProject _configuredProject; - private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; - - [ImportingConstructor] - public UpToDateCheckConfiguredInputDataSource( - ConfiguredProject containingProject, - IActiveConfigurationGroupService activeConfigurationGroupService) - : base(containingProject, synchronousDisposal: false, registerDataSource: false) - { - _configuredProject = containingProject; - _activeConfigurationGroupService = activeConfigurationGroupService; - } + _configuredProject = containingProject; + _activeConfigurationGroupService = activeConfigurationGroupService; + } - protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) - { - // Aggregates implicitly active UpToDateCheckImplicitConfiguredInput inputs from their sources - var joinBlock = new ConfiguredProjectDataSourceJoinBlock( - project => project.Services.ExportProvider.GetExportedValueOrDefault(), - JoinableFactory, - _configuredProject.UnconfiguredProject); + protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) + { + // Aggregates implicitly active UpToDateCheckImplicitConfiguredInput inputs from their sources + var joinBlock = new ConfiguredProjectDataSourceJoinBlock( + project => project.Services.ExportProvider.GetExportedValueOrDefault(), + JoinableFactory, + _configuredProject.UnconfiguredProject); - // Merges UpToDateCheckImplicitConfiguredInputs into a UpToDateCheckConfiguredInput - DisposableValue>> - mergeBlock = joinBlock.TransformWithNoDelta(update => update.Derive(MergeInputs)); + // Merges UpToDateCheckImplicitConfiguredInputs into a UpToDateCheckConfiguredInput + DisposableValue>> + mergeBlock = joinBlock.TransformWithNoDelta(update => update.Derive(MergeInputs)); - JoinUpstreamDataSources(_activeConfigurationGroupService.ActiveConfiguredProjectGroupSource); + JoinUpstreamDataSources(_activeConfigurationGroupService.ActiveConfiguredProjectGroupSource); - // Set the link up so that we publish changes to target block - mergeBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + // Set the link up so that we publish changes to target block + mergeBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - return new DisposableBag - { - joinBlock, + return new DisposableBag + { + joinBlock, - // Link the active configured projects to our join block - _activeConfigurationGroupService.ActiveConfiguredProjectGroupSource.SourceBlock.LinkTo(joinBlock, DataflowOption.PropagateCompletion), - }; + // Link the active configured projects to our join block + _activeConfigurationGroupService.ActiveConfiguredProjectGroupSource.SourceBlock.LinkTo(joinBlock, DataflowOption.PropagateCompletion), + }; - static UpToDateCheckConfiguredInput MergeInputs(IReadOnlyCollection inputs) - { - return new(inputs.ToImmutableArray()); - } + static UpToDateCheckConfiguredInput MergeInputs(IReadOnlyCollection inputs) + { + return new(inputs.ToImmutableArray()); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckHost.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckHost.cs index e01ddb9954..2d7de925bb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckHost.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckHost.cs @@ -5,61 +5,60 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +[Export(typeof(IUpToDateCheckHost))] +internal sealed class UpToDateCheckHost : IUpToDateCheckHost { - [Export(typeof(IUpToDateCheckHost))] - internal sealed class UpToDateCheckHost : IUpToDateCheckHost + private readonly IVsService _vsAppId; + private readonly IVsService _vsAppCommandLine; + private readonly AsyncLazy _hasDesignTimeBuild; + + [ImportingConstructor] + public UpToDateCheckHost( + IVsService vsAppId, + IVsService vsAppCommandLine, + JoinableTaskContext joinableTaskContext) { - private readonly IVsService _vsAppId; - private readonly IVsService _vsAppCommandLine; - private readonly AsyncLazy _hasDesignTimeBuild; + _vsAppId = vsAppId; + _vsAppCommandLine = vsAppCommandLine; - [ImportingConstructor] - public UpToDateCheckHost( - IVsService vsAppId, - IVsService vsAppCommandLine, - JoinableTaskContext joinableTaskContext) - { - _vsAppId = vsAppId; - _vsAppCommandLine = vsAppCommandLine; + _hasDesignTimeBuild = new(HasDesignTimeBuildsInternalAsync, joinableTaskContext.Factory); + } - _hasDesignTimeBuild = new(HasDesignTimeBuildsInternalAsync, joinableTaskContext.Factory); - } + public async ValueTask HasDesignTimeBuildsAsync(CancellationToken cancellationToken) + { + return await _hasDesignTimeBuild.GetValueAsync(cancellationToken); + } - public async ValueTask HasDesignTimeBuildsAsync(CancellationToken cancellationToken) - { - return await _hasDesignTimeBuild.GetValueAsync(cancellationToken); - } + private async Task HasDesignTimeBuildsInternalAsync() + { + IVsAppCommandLine vsAppCommandLine = await _vsAppCommandLine.GetValueAsync(); - private async Task HasDesignTimeBuildsInternalAsync() + if (ErrorHandler.Succeeded(vsAppCommandLine.GetOption("populateSolutionCache", out int populateSolutionCachePresent, out string _))) { - IVsAppCommandLine vsAppCommandLine = await _vsAppCommandLine.GetValueAsync(); - - if (ErrorHandler.Succeeded(vsAppCommandLine.GetOption("populateSolutionCache", out int populateSolutionCachePresent, out string _))) + if (populateSolutionCachePresent != 0) { - if (populateSolutionCachePresent != 0) - { - // Design time builds are available when running with /populateSolutionCache. - return true; - } + // Design time builds are available when running with /populateSolutionCache. + return true; } + } - IVsAppId vsAppId = await _vsAppId.GetValueAsync(); + IVsAppId vsAppId = await _vsAppId.GetValueAsync(); - if (ErrorHandler.Succeeded(vsAppId.GetProperty((int)__VSAPROPID10.VSAPROPID_IsInCommandLineMode, out object value))) + if (ErrorHandler.Succeeded(vsAppId.GetProperty((int)__VSAPROPID10.VSAPROPID_IsInCommandLineMode, out object value))) + { + if (value is bool isInCommandLineMode) { - if (value is bool isInCommandLineMode) - { - // Design-time builds do not occur in command line mode, other than with /populateSolutionCache (checked earlier). - return !isInCommandLineMode; - } + // Design-time builds do not occur in command line mode, other than with /populateSolutionCache (checked earlier). + return !isInCommandLineMode; } + } - // We shouldn't reach this point. - System.Diagnostics.Debug.Fail($"{nameof(UpToDateCheckHost)}.{nameof(HasDesignTimeBuildsAsync)} was unable to determine result reliably."); + // We shouldn't reach this point. + System.Diagnostics.Debug.Fail($"{nameof(UpToDateCheckHost)}.{nameof(HasDesignTimeBuildsAsync)} was unable to determine result reliably."); - // Assume we don't have design-time builds, to prevent hangs from waiting for snapshot data that will never arrive. - return false; - } + // Assume we don't have design-time builds, to prevent hangs from waiting for snapshot data that will never arrive. + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInput.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInput.cs index 4518055054..82f5594206 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInput.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInput.cs @@ -3,798 +3,797 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Models all up-to-date check state for a single configuration. +/// +/// +/// Produced by . +/// +internal sealed class UpToDateCheckImplicitConfiguredInput { + public static UpToDateCheckImplicitConfiguredInput CreateEmpty(ProjectConfiguration projectConfiguration) + { + return new UpToDateCheckImplicitConfiguredInput(projectConfiguration); + } + + public static UpToDateCheckImplicitConfiguredInput CreateDisabled(ProjectConfiguration projectConfiguration) + { + return new UpToDateCheckImplicitConfiguredInput( + projectConfiguration: projectConfiguration, + msBuildProjectDirectory: null, + projectTargetPath: null, + copyUpToDateMarkerItem: null, + outputRelativeOrFullPath: null, + newestImportInput: null, + isDisabled: true, + isBuildAccelerationEnabled: false, + inputSourceItemTypes: [], + inputSourceItemsByItemType: ImmutableDictionary>.Empty, + upToDateCheckInputItemsByKindBySetName: ImmutableDictionary>>.Empty, + upToDateCheckOutputItemsByKindBySetName: ImmutableDictionary>>.Empty, + upToDateCheckBuiltItemsByKindBySetName: ImmutableDictionary>>.Empty, + buildFromInputFileItems: [], + resolvedAnalyzerReferencePaths: [], + resolvedCompilationReferencePaths: [], + copyReferenceInputs: [], + presentBuildAccelerationIncompatiblePackages: [], + lastItemsChangedAtUtc: null, + lastItemChanges: [], + itemHash: null, + projectCopyData: default); + } + /// - /// Models all up-to-date check state for a single configuration. + /// Gets the project configuration for this configured data snapshot. /// /// - /// Produced by . + /// Useful when a project multi-targets and we want to differentiate targets in log output. + /// when the up-to-date check is disabled. /// - internal sealed class UpToDateCheckImplicitConfiguredInput - { - public static UpToDateCheckImplicitConfiguredInput CreateEmpty(ProjectConfiguration projectConfiguration) - { - return new UpToDateCheckImplicitConfiguredInput(projectConfiguration); - } + public ProjectConfiguration ProjectConfiguration { get; } - public static UpToDateCheckImplicitConfiguredInput CreateDisabled(ProjectConfiguration projectConfiguration) - { - return new UpToDateCheckImplicitConfiguredInput( - projectConfiguration: projectConfiguration, - msBuildProjectDirectory: null, - projectTargetPath: null, - copyUpToDateMarkerItem: null, - outputRelativeOrFullPath: null, - newestImportInput: null, - isDisabled: true, - isBuildAccelerationEnabled: false, - inputSourceItemTypes: [], - inputSourceItemsByItemType: ImmutableDictionary>.Empty, - upToDateCheckInputItemsByKindBySetName: ImmutableDictionary>>.Empty, - upToDateCheckOutputItemsByKindBySetName: ImmutableDictionary>>.Empty, - upToDateCheckBuiltItemsByKindBySetName: ImmutableDictionary>>.Empty, - buildFromInputFileItems: [], - resolvedAnalyzerReferencePaths: [], - resolvedCompilationReferencePaths: [], - copyReferenceInputs: [], - presentBuildAccelerationIncompatiblePackages: [], - lastItemsChangedAtUtc: null, - lastItemChanges: [], - itemHash: null, - projectCopyData: default); - } + /// + /// Gets the full path to the project directory. + /// + /// + /// Comes from the MSBuildProjectDirectory MSBuild property. + /// For example, C:\repos\MySolution\MyProject. + /// + public string? MSBuildProjectDirectory { get; } - /// - /// Gets the project configuration for this configured data snapshot. - /// - /// - /// Useful when a project multi-targets and we want to differentiate targets in log output. - /// when the up-to-date check is disabled. - /// - public ProjectConfiguration ProjectConfiguration { get; } - - /// - /// Gets the full path to the project directory. - /// - /// - /// Comes from the MSBuildProjectDirectory MSBuild property. - /// For example, C:\repos\MySolution\MyProject. - /// - public string? MSBuildProjectDirectory { get; } - - /// - /// Gets the full path to the project's output file. - /// - /// - /// Comes from the TargetPath MSBuild property. - /// For example, C:\repos\MySolution\MyProject\bin\Debug\net6.0\MyProject.dll. - /// - public string? ProjectTargetPath { get; } - - /// - /// Gets the output path of the project, relative to the project directory. - /// - /// - /// Comes from the OutDir MSBuild property, if available, otherwise from the OutputPath MSBuild property. - /// For example, bin\Debug\net6.0\. - /// - public string? OutputRelativeOrFullPath { get; } - - /// - /// Contains the first path from the , - /// which MSBuild guarantees to be the newest import from all properties (since 16.0). As we - /// are only interested in the newest import, we need not retain the remaining paths. - /// - public string? NewestImportInput { get; } - - /// - /// Gets whether the fast up-to-date check has been disabled for this project. - /// - /// - /// - /// Customers disable the fast up-to-date check by setting the DisableFastUpToDateCheck - /// project property to true. - /// - /// - /// When , other properties on this snapshot will not be used, and so - /// are not populated. - /// - /// - public bool IsDisabled { get; } - - /// - /// Gets whether the fast up-to-date check is prohibited from copying files in the case - /// that the only reason a project is not up-to-date is due to the need to copy files. - /// - /// - /// Controlled by the AccelerateBuildsInVisualStudio project property. - /// when the property is undefined or cannot be parsed as a bool. - /// - public bool? IsBuildAccelerationEnabled { get; } - - /// - /// Gets the time at which the set of items changed. - /// - /// - /// - /// This is not the last timestamp of the items themselves. It is time at which items were - /// last added or removed from the project. - /// - /// - /// This property is not set until the set of items is observed changing. Until that time it will - /// be , representing the fact that the value is unknown and we cannot base - /// any decisions on this data property. - /// - /// - public DateTime? LastItemsChangedAtUtc { get; } - - public ImmutableArray<(bool IsAdd, string ItemType, string Item)> LastItemChanges { get; } - - public int? ItemHash { get; } - - /// - /// Gets the set of source item types known to be up-to-date check inputs. - /// - /// - /// Items of these types are available in . - /// - public ImmutableArray InputSourceItemTypes { get; } - - public ImmutableDictionary> InputSourceItemsByItemType { get; } - - /// - /// An alphabetically ordered list of the set names present in this project. - /// - /// - /// Does NOT contain . - /// - public ImmutableArray SetNames { get; } - - /// - /// An alphabetically ordered list of the kind names present in this project. - /// - /// - /// Contains . - /// - public ImmutableArray KindNames { get; } - - public ImmutableDictionary>> UpToDateCheckInputItemsByKindBySetName { get; } - - public ImmutableDictionary>> UpToDateCheckOutputItemsByKindBySetName { get; } - - public ImmutableDictionary>> UpToDateCheckBuiltItemsByKindBySetName { get; } - - /// - /// Holds items which are each built from a single input file. - /// - /// - /// - /// Projects add to this collection by specifying the - /// on items. - /// - /// - /// items without metadata - /// are treated as regular output items, and are modelled in - /// rather than here. - /// - /// - public ImmutableArray<(string DestinationRelative, string SourceRelative)> BuiltFromInputFileItems { get; } - - public ImmutableArray ResolvedAnalyzerReferencePaths { get; } - - /// - /// Absolute (rooted) paths to items in the project. - /// - public ImmutableArray ResolvedCompilationReferencePaths { get; } - - /// - /// Holds the set of non-empty and - /// metadata values from all - /// items in the project. - /// - public ImmutableArray CopyReferenceInputs { get; } - - /// - /// Gets the set of packages present in this project that are known to be incompatible with Build Acceleration. - /// - /// - /// When this collection is non-empty, Build Acceleration will be disabled. - /// We track the item specs so that we can include relevant details in log output. - /// Most projects will not have any entries here. - /// - public ImmutableArray PresentBuildAccelerationIncompatiblePackages { get; } - - /// - /// Gets the relative path to the output marker file. - /// - public string? CopyUpToDateMarkerItem { get; } - - /// - /// Gets the set of items this project contributes to the output directory when built. - /// These items are inherited by referencing projects too, transitively. - /// - /// - /// - /// Includes various kinds of files including assemblies content files from this project ONLY. - /// - /// - /// When building, we must obtain the full set of output directory items from this and all transitively referenced projects. - /// This collection does not provide that. Use instead. The intent is for the implementation - /// of that interface to consume this property. - /// - /// - public ProjectCopyData ProjectCopyData { get; } - - private UpToDateCheckImplicitConfiguredInput(ProjectConfiguration projectConfiguration) - { - var emptyItemBySetName = ImmutableDictionary.Create>>(BuildUpToDateCheck.SetNameComparer); - - ProjectConfiguration = projectConfiguration; - LastItemsChangedAtUtc = null; - InputSourceItemTypes = []; - InputSourceItemsByItemType = ImmutableDictionary.Create>(StringComparers.ItemTypes); - SetNames = []; - KindNames = []; - UpToDateCheckInputItemsByKindBySetName = emptyItemBySetName; - UpToDateCheckOutputItemsByKindBySetName = emptyItemBySetName; - UpToDateCheckBuiltItemsByKindBySetName = emptyItemBySetName; - BuiltFromInputFileItems = []; - ResolvedAnalyzerReferencePaths = []; - ResolvedCompilationReferencePaths = []; - CopyReferenceInputs = []; - PresentBuildAccelerationIncompatiblePackages = []; - LastItemChanges = []; - ProjectCopyData = new(null, "", false, [], []); - } + /// + /// Gets the full path to the project's output file. + /// + /// + /// Comes from the TargetPath MSBuild property. + /// For example, C:\repos\MySolution\MyProject\bin\Debug\net6.0\MyProject.dll. + /// + public string? ProjectTargetPath { get; } - private UpToDateCheckImplicitConfiguredInput( - ProjectConfiguration projectConfiguration, - string? msBuildProjectDirectory, - string? projectTargetPath, - string? copyUpToDateMarkerItem, - string? outputRelativeOrFullPath, - string? newestImportInput, - bool isDisabled, - bool? isBuildAccelerationEnabled, - ImmutableArray inputSourceItemTypes, - ImmutableDictionary> inputSourceItemsByItemType, - ImmutableDictionary>> upToDateCheckInputItemsByKindBySetName, - ImmutableDictionary>> upToDateCheckOutputItemsByKindBySetName, - ImmutableDictionary>> upToDateCheckBuiltItemsByKindBySetName, - ImmutableArray<(string DestinationRelative, string SourceRelative)> buildFromInputFileItems, - ImmutableArray resolvedAnalyzerReferencePaths, - ImmutableArray resolvedCompilationReferencePaths, - ImmutableArray copyReferenceInputs, - ImmutableArray presentBuildAccelerationIncompatiblePackages, - DateTime? lastItemsChangedAtUtc, - ImmutableArray<(bool IsAdd, string ItemType, string)> lastItemChanges, - int? itemHash, - ProjectCopyData projectCopyData) - { - ProjectConfiguration = projectConfiguration; - MSBuildProjectDirectory = msBuildProjectDirectory; - ProjectTargetPath = projectTargetPath; - CopyUpToDateMarkerItem = copyUpToDateMarkerItem; - OutputRelativeOrFullPath = outputRelativeOrFullPath; - NewestImportInput = newestImportInput; - IsDisabled = isDisabled; - IsBuildAccelerationEnabled = isBuildAccelerationEnabled; - InputSourceItemTypes = inputSourceItemTypes; - InputSourceItemsByItemType = inputSourceItemsByItemType; - UpToDateCheckInputItemsByKindBySetName = upToDateCheckInputItemsByKindBySetName; - UpToDateCheckOutputItemsByKindBySetName = upToDateCheckOutputItemsByKindBySetName; - UpToDateCheckBuiltItemsByKindBySetName = upToDateCheckBuiltItemsByKindBySetName; - BuiltFromInputFileItems = buildFromInputFileItems; - ResolvedAnalyzerReferencePaths = resolvedAnalyzerReferencePaths; - ResolvedCompilationReferencePaths = resolvedCompilationReferencePaths; - CopyReferenceInputs = copyReferenceInputs; - PresentBuildAccelerationIncompatiblePackages = presentBuildAccelerationIncompatiblePackages; - LastItemsChangedAtUtc = lastItemsChangedAtUtc; - LastItemChanges = lastItemChanges; - ItemHash = itemHash; - ProjectCopyData = projectCopyData; - - var setNames = new HashSet(BuildUpToDateCheck.SetNameComparer); - AddSetNames(upToDateCheckInputItemsByKindBySetName); - AddSetNames(upToDateCheckOutputItemsByKindBySetName); - AddSetNames(upToDateCheckBuiltItemsByKindBySetName); - setNames.Remove(BuildUpToDateCheck.DefaultSetName); - SetNames = setNames.OrderBy(n => n, BuildUpToDateCheck.SetNameComparer).ToImmutableArray(); - - var kindNames = new HashSet(BuildUpToDateCheck.KindNameComparer); - AddKindNames(upToDateCheckInputItemsByKindBySetName); - AddKindNames(upToDateCheckOutputItemsByKindBySetName); - AddKindNames(upToDateCheckBuiltItemsByKindBySetName); - KindNames = kindNames.OrderBy(n => n, BuildUpToDateCheck.KindNameComparer).ToImmutableArray(); - - void AddSetNames(ImmutableDictionary>> itemsByKindBySetName) - { - foreach ((string setName, _) in itemsByKindBySetName) - { - setNames.Add(setName); - } - } + /// + /// Gets the output path of the project, relative to the project directory. + /// + /// + /// Comes from the OutDir MSBuild property, if available, otherwise from the OutputPath MSBuild property. + /// For example, bin\Debug\net6.0\. + /// + public string? OutputRelativeOrFullPath { get; } - void AddKindNames(ImmutableDictionary>> itemsByKindBySetName) - { - foreach ((_, ImmutableDictionary> itemsByKind) in itemsByKindBySetName) - { - foreach ((string kind, _) in itemsByKind) - { - kindNames.Add(kind); - } - } - } - } + /// + /// Contains the first path from the , + /// which MSBuild guarantees to be the newest import from all properties (since 16.0). As we + /// are only interested in the newest import, we need not retain the remaining paths. + /// + public string? NewestImportInput { get; } - public UpToDateCheckImplicitConfiguredInput Update( - IProjectSubscriptionUpdate jointRuleUpdate, - IProjectSubscriptionUpdate sourceItemsUpdate, - IProjectItemSchema projectItemSchema, - IProjectCatalogSnapshot projectCatalogSnapshot) - { - bool isDisabled = jointRuleUpdate.CurrentState.IsPropertyTrue(ConfigurationGeneral.SchemaName, ConfigurationGeneral.DisableFastUpToDateCheckProperty, defaultValue: false); + /// + /// Gets whether the fast up-to-date check has been disabled for this project. + /// + /// + /// + /// Customers disable the fast up-to-date check by setting the DisableFastUpToDateCheck + /// project property to true. + /// + /// + /// When , other properties on this snapshot will not be used, and so + /// are not populated. + /// + /// + public bool IsDisabled { get; } - if (isDisabled) - { - return CreateDisabled(ProjectConfiguration); - } + /// + /// Gets whether the fast up-to-date check is prohibited from copying files in the case + /// that the only reason a project is not up-to-date is due to the need to copy files. + /// + /// + /// Controlled by the AccelerateBuildsInVisualStudio project property. + /// when the property is undefined or cannot be parsed as a bool. + /// + public bool? IsBuildAccelerationEnabled { get; } - string? msBuildProjectFullPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildProjectFullPathProperty, defaultValue: null); - string? msBuildProjectDirectory = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildProjectDirectoryProperty, defaultValue: MSBuildProjectDirectory); - string? projectTargetPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetPathProperty, defaultValue: ""); - string? projectOutputPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputPathProperty, defaultValue: OutputRelativeOrFullPath); - string? outputRelativeOrFullPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutDirProperty, defaultValue: projectOutputPath); - string msBuildAllProjects = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildAllProjectsProperty, defaultValue: ""); - bool? isBuildAccelerationEnabled = jointRuleUpdate.CurrentState.GetBooleanPropertyValue(ConfigurationGeneral.SchemaName, ConfigurationGeneral.AccelerateBuildsInVisualStudioProperty); + /// + /// Gets the time at which the set of items changed. + /// + /// + /// + /// This is not the last timestamp of the items themselves. It is time at which items were + /// last added or removed from the project. + /// + /// + /// This property is not set until the set of items is observed changing. Until that time it will + /// be , representing the fact that the value is unknown and we cannot base + /// any decisions on this data property. + /// + /// + public DateTime? LastItemsChangedAtUtc { get; } - // The first item in this semicolon-separated list of project files will always be the one - // with the newest timestamp. As we are only interested in timestamps on these files, we can - // save memory and time by only considering this first path (dotnet/project-system#4333). - // Note that we cannot exclude this path using the ProjectFileClassifier, as doing so may - // miss other imports of interest. - string? newestImportInput = new LazyStringSplit(msBuildAllProjects, ';').FirstOrDefault(); + public ImmutableArray<(bool IsAdd, string ItemType, string Item)> LastItemChanges { get; } - ProjectFileClassifier? projectFileClassifier = null; + public int? ItemHash { get; } - // Not all item types are up-to-date check inputs. Filter to the set that are. - var inputSourceItemTypes = projectItemSchema - .GetKnownItemTypes() - .Where(itemType => projectItemSchema.GetItemType(itemType).UpToDateCheckInput) - .ToHashSet(StringComparers.ItemTypes); + /// + /// Gets the set of source item types known to be up-to-date check inputs. + /// + /// + /// Items of these types are available in . + /// + public ImmutableArray InputSourceItemTypes { get; } - var itemTypeDiff = new SetDiff(InputSourceItemTypes, inputSourceItemTypes, StringComparers.ItemTypes); + public ImmutableDictionary> InputSourceItemsByItemType { get; } - var inputSourceItemsByItemTypeBuilder = InputSourceItemsByItemType.ToBuilder(); + /// + /// An alphabetically ordered list of the set names present in this project. + /// + /// + /// Does NOT contain . + /// + public ImmutableArray SetNames { get; } - bool itemTypesChanged = false; + /// + /// An alphabetically ordered list of the kind names present in this project. + /// + /// + /// Contains . + /// + public ImmutableArray KindNames { get; } - List<(bool IsAdd, string ItemType, string ItemSpec)> lastItemChanges = []; + public ImmutableDictionary>> UpToDateCheckInputItemsByKindBySetName { get; } - // If an item type was removed, remove all items of that type - foreach (string removedItemType in itemTypeDiff.Removed) - { - itemTypesChanged = true; + public ImmutableDictionary>> UpToDateCheckOutputItemsByKindBySetName { get; } - if (inputSourceItemsByItemTypeBuilder.TryGetValue(removedItemType, out ImmutableArray removedItems)) - { - foreach (string item in removedItems) - { - lastItemChanges.Add((IsAdd: false, removedItemType, item)); - } + public ImmutableDictionary>> UpToDateCheckBuiltItemsByKindBySetName { get; } - inputSourceItemsByItemTypeBuilder.Remove(removedItemType); - } - } + /// + /// Holds items which are each built from a single input file. + /// + /// + /// + /// Projects add to this collection by specifying the + /// on items. + /// + /// + /// items without metadata + /// are treated as regular output items, and are modelled in + /// rather than here. + /// + /// + public ImmutableArray<(string DestinationRelative, string SourceRelative)> BuiltFromInputFileItems { get; } + + public ImmutableArray ResolvedAnalyzerReferencePaths { get; } + + /// + /// Absolute (rooted) paths to items in the project. + /// + public ImmutableArray ResolvedCompilationReferencePaths { get; } + + /// + /// Holds the set of non-empty and + /// metadata values from all + /// items in the project. + /// + public ImmutableArray CopyReferenceInputs { get; } + + /// + /// Gets the set of packages present in this project that are known to be incompatible with Build Acceleration. + /// + /// + /// When this collection is non-empty, Build Acceleration will be disabled. + /// We track the item specs so that we can include relevant details in log output. + /// Most projects will not have any entries here. + /// + public ImmutableArray PresentBuildAccelerationIncompatiblePackages { get; } + + /// + /// Gets the relative path to the output marker file. + /// + public string? CopyUpToDateMarkerItem { get; } + + /// + /// Gets the set of items this project contributes to the output directory when built. + /// These items are inherited by referencing projects too, transitively. + /// + /// + /// + /// Includes various kinds of files including assemblies content files from this project ONLY. + /// + /// + /// When building, we must obtain the full set of output directory items from this and all transitively referenced projects. + /// This collection does not provide that. Use instead. The intent is for the implementation + /// of that interface to consume this property. + /// + /// + public ProjectCopyData ProjectCopyData { get; } - itemTypesChanged |= itemTypeDiff.Added.GetEnumerator().MoveNext(); + private UpToDateCheckImplicitConfiguredInput(ProjectConfiguration projectConfiguration) + { + var emptyItemBySetName = ImmutableDictionary.Create>>(BuildUpToDateCheck.SetNameComparer); + + ProjectConfiguration = projectConfiguration; + LastItemsChangedAtUtc = null; + InputSourceItemTypes = []; + InputSourceItemsByItemType = ImmutableDictionary.Create>(StringComparers.ItemTypes); + SetNames = []; + KindNames = []; + UpToDateCheckInputItemsByKindBySetName = emptyItemBySetName; + UpToDateCheckOutputItemsByKindBySetName = emptyItemBySetName; + UpToDateCheckBuiltItemsByKindBySetName = emptyItemBySetName; + BuiltFromInputFileItems = []; + ResolvedAnalyzerReferencePaths = []; + ResolvedCompilationReferencePaths = []; + CopyReferenceInputs = []; + PresentBuildAccelerationIncompatiblePackages = []; + LastItemChanges = []; + ProjectCopyData = new(null, "", false, [], []); + } - bool itemsChanged = false; + private UpToDateCheckImplicitConfiguredInput( + ProjectConfiguration projectConfiguration, + string? msBuildProjectDirectory, + string? projectTargetPath, + string? copyUpToDateMarkerItem, + string? outputRelativeOrFullPath, + string? newestImportInput, + bool isDisabled, + bool? isBuildAccelerationEnabled, + ImmutableArray inputSourceItemTypes, + ImmutableDictionary> inputSourceItemsByItemType, + ImmutableDictionary>> upToDateCheckInputItemsByKindBySetName, + ImmutableDictionary>> upToDateCheckOutputItemsByKindBySetName, + ImmutableDictionary>> upToDateCheckBuiltItemsByKindBySetName, + ImmutableArray<(string DestinationRelative, string SourceRelative)> buildFromInputFileItems, + ImmutableArray resolvedAnalyzerReferencePaths, + ImmutableArray resolvedCompilationReferencePaths, + ImmutableArray copyReferenceInputs, + ImmutableArray presentBuildAccelerationIncompatiblePackages, + DateTime? lastItemsChangedAtUtc, + ImmutableArray<(bool IsAdd, string ItemType, string)> lastItemChanges, + int? itemHash, + ProjectCopyData projectCopyData) + { + ProjectConfiguration = projectConfiguration; + MSBuildProjectDirectory = msBuildProjectDirectory; + ProjectTargetPath = projectTargetPath; + CopyUpToDateMarkerItem = copyUpToDateMarkerItem; + OutputRelativeOrFullPath = outputRelativeOrFullPath; + NewestImportInput = newestImportInput; + IsDisabled = isDisabled; + IsBuildAccelerationEnabled = isBuildAccelerationEnabled; + InputSourceItemTypes = inputSourceItemTypes; + InputSourceItemsByItemType = inputSourceItemsByItemType; + UpToDateCheckInputItemsByKindBySetName = upToDateCheckInputItemsByKindBySetName; + UpToDateCheckOutputItemsByKindBySetName = upToDateCheckOutputItemsByKindBySetName; + UpToDateCheckBuiltItemsByKindBySetName = upToDateCheckBuiltItemsByKindBySetName; + BuiltFromInputFileItems = buildFromInputFileItems; + ResolvedAnalyzerReferencePaths = resolvedAnalyzerReferencePaths; + ResolvedCompilationReferencePaths = resolvedCompilationReferencePaths; + CopyReferenceInputs = copyReferenceInputs; + PresentBuildAccelerationIncompatiblePackages = presentBuildAccelerationIncompatiblePackages; + LastItemsChangedAtUtc = lastItemsChangedAtUtc; + LastItemChanges = lastItemChanges; + ItemHash = itemHash; + ProjectCopyData = projectCopyData; + + var setNames = new HashSet(BuildUpToDateCheck.SetNameComparer); + AddSetNames(upToDateCheckInputItemsByKindBySetName); + AddSetNames(upToDateCheckOutputItemsByKindBySetName); + AddSetNames(upToDateCheckBuiltItemsByKindBySetName); + setNames.Remove(BuildUpToDateCheck.DefaultSetName); + SetNames = setNames.OrderBy(n => n, BuildUpToDateCheck.SetNameComparer).ToImmutableArray(); + + var kindNames = new HashSet(BuildUpToDateCheck.KindNameComparer); + AddKindNames(upToDateCheckInputItemsByKindBySetName); + AddKindNames(upToDateCheckOutputItemsByKindBySetName); + AddKindNames(upToDateCheckBuiltItemsByKindBySetName); + KindNames = kindNames.OrderBy(n => n, BuildUpToDateCheck.KindNameComparer).ToImmutableArray(); + + void AddSetNames(ImmutableDictionary>> itemsByKindBySetName) + { + foreach ((string setName, _) in itemsByKindBySetName) + { + setNames.Add(setName); + } + } - foreach ((string schemaName, IProjectChangeDescription projectChange) in sourceItemsUpdate.ProjectChanges) + void AddKindNames(ImmutableDictionary>> itemsByKindBySetName) + { + foreach ((_, ImmutableDictionary> itemsByKind) in itemsByKindBySetName) { - if ((!projectChange.Difference.AnyChanges && !itemTypesChanged) || - (projectChange.After.Items.Count == 0 && projectChange.Difference.RemovedItems.Count == 0)) + foreach ((string kind, _) in itemsByKind) { - continue; + kindNames.Add(kind); } + } + } + } - // Rule name (schema name) is usually the same as its item type, but not always (eg: auto-generated rules) - string? itemType = null; - if (projectCatalogSnapshot.NamedCatalogs.TryGetValue(PropertyPageContexts.File, out IPropertyPagesCatalog? fileCatalog)) - { - itemType = fileCatalog.GetSchema(schemaName)?.DataSource.ItemType; - } + public UpToDateCheckImplicitConfiguredInput Update( + IProjectSubscriptionUpdate jointRuleUpdate, + IProjectSubscriptionUpdate sourceItemsUpdate, + IProjectItemSchema projectItemSchema, + IProjectCatalogSnapshot projectCatalogSnapshot) + { + bool isDisabled = jointRuleUpdate.CurrentState.IsPropertyTrue(ConfigurationGeneral.SchemaName, ConfigurationGeneral.DisableFastUpToDateCheckProperty, defaultValue: false); - if (itemType is null || !inputSourceItemTypes.Contains(itemType)) - { - continue; - } + if (isDisabled) + { + return CreateDisabled(ProjectConfiguration); + } - if (!inputSourceItemsByItemTypeBuilder.TryGetValue(itemType, out ImmutableArray before)) - { - before = []; - } + string? msBuildProjectFullPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildProjectFullPathProperty, defaultValue: null); + string? msBuildProjectDirectory = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildProjectDirectoryProperty, defaultValue: MSBuildProjectDirectory); + string? projectTargetPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetPathProperty, defaultValue: ""); + string? projectOutputPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputPathProperty, defaultValue: OutputRelativeOrFullPath); + string? outputRelativeOrFullPath = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutDirProperty, defaultValue: projectOutputPath); + string msBuildAllProjects = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildAllProjectsProperty, defaultValue: ""); + bool? isBuildAccelerationEnabled = jointRuleUpdate.CurrentState.GetBooleanPropertyValue(ConfigurationGeneral.SchemaName, ConfigurationGeneral.AccelerateBuildsInVisualStudioProperty); - projectFileClassifier ??= BuildClassifier(); + // The first item in this semicolon-separated list of project files will always be the one + // with the newest timestamp. As we are only interested in timestamps on these files, we can + // save memory and time by only considering this first path (dotnet/project-system#4333). + // Note that we cannot exclude this path using the ProjectFileClassifier, as doing so may + // miss other imports of interest. + string? newestImportInput = new LazyStringSplit(msBuildAllProjects, ';').FirstOrDefault(); - var after = projectChange.After.Items - .Select(pair => pair.Key) - .Where(path => !projectFileClassifier.IsNonModifiable(path)) - .ToHashSet(StringComparers.Paths); + ProjectFileClassifier? projectFileClassifier = null; - var diff = new SetDiff(before, after, StringComparers.Paths); + // Not all item types are up-to-date check inputs. Filter to the set that are. + var inputSourceItemTypes = projectItemSchema + .GetKnownItemTypes() + .Where(itemType => projectItemSchema.GetItemType(itemType).UpToDateCheckInput) + .ToHashSet(StringComparers.ItemTypes); - foreach (string item in diff.Added) - { - lastItemChanges.Add((IsAdd: true, itemType, item)); - } + var itemTypeDiff = new SetDiff(InputSourceItemTypes, inputSourceItemTypes, StringComparers.ItemTypes); + + var inputSourceItemsByItemTypeBuilder = InputSourceItemsByItemType.ToBuilder(); + + bool itemTypesChanged = false; + + List<(bool IsAdd, string ItemType, string ItemSpec)> lastItemChanges = []; - foreach (string item in diff.Removed) + // If an item type was removed, remove all items of that type + foreach (string removedItemType in itemTypeDiff.Removed) + { + itemTypesChanged = true; + + if (inputSourceItemsByItemTypeBuilder.TryGetValue(removedItemType, out ImmutableArray removedItems)) + { + foreach (string item in removedItems) { - lastItemChanges.Add((IsAdd: false, itemType, item)); + lastItemChanges.Add((IsAdd: false, removedItemType, item)); } - inputSourceItemsByItemTypeBuilder[itemType] = after.ToImmutableArray(); - itemsChanged = true; + inputSourceItemsByItemTypeBuilder.Remove(removedItemType); } + } - ImmutableDictionary> inputSourceItemsByItemType = inputSourceItemsByItemTypeBuilder.ToImmutable(); - - int itemHash = BuildUpToDateCheck.ComputeItemHash(inputSourceItemsByItemType); + itemTypesChanged |= itemTypeDiff.Added.GetEnumerator().MoveNext(); - DateTime? lastItemsChangedAtUtc = LastItemsChangedAtUtc; + bool itemsChanged = false; - if (itemHash != ItemHash && ItemHash != null) + foreach ((string schemaName, IProjectChangeDescription projectChange) in sourceItemsUpdate.ProjectChanges) + { + if ((!projectChange.Difference.AnyChanges && !itemTypesChanged) || + (projectChange.After.Items.Count == 0 && projectChange.Difference.RemovedItems.Count == 0)) { - // The set of items has changed. - // For the case that the project loaded and no hash was available, do not touch lastItemsChangedAtUtc. - // Doing so when the project is actually up-to-date would cause builds to be scheduled until something - // actually changes. - lastItemsChangedAtUtc = DateTime.UtcNow; + continue; } - else if (itemsChanged && !InputSourceItemTypes.IsEmpty) + + // Rule name (schema name) is usually the same as its item type, but not always (eg: auto-generated rules) + string? itemType = null; + if (projectCatalogSnapshot.NamedCatalogs.TryGetValue(PropertyPageContexts.File, out IPropertyPagesCatalog? fileCatalog)) { - // When we previously had zero item types, we can surmise that the project has just been loaded. In such - // a case it is not correct to assume that the items changed, and so we do not update the timestamp. - // If we did, and the project was up-to-date when loaded, it would remain out-of-date until something changed, - // causing redundant builds until that time. See https://github.com/dotnet/project-system/issues/5386. - lastItemsChangedAtUtc = DateTime.UtcNow; + itemType = fileCatalog.GetSchema(schemaName)?.DataSource.ItemType; } - (ImmutableArray resolvedCompilationReferencePaths, ImmutableArray copyReferenceInputs) = UpdateResolvedCompilationReferences(); - - return new( - ProjectConfiguration, - msBuildProjectDirectory, - projectTargetPath, - copyUpToDateMarkerItem: UpdateCopyUpToDateMarkerItem(), - outputRelativeOrFullPath, - newestImportInput, - isDisabled: isDisabled, - isBuildAccelerationEnabled: isBuildAccelerationEnabled, - inputSourceItemTypes: inputSourceItemTypes.ToImmutableArray(), - inputSourceItemsByItemType: inputSourceItemsByItemType, - upToDateCheckInputItemsByKindBySetName: UpdateItemsByKindBySetName(UpToDateCheckInputItemsByKindBySetName, jointRuleUpdate, UpToDateCheckInput.SchemaName, UpToDateCheckInput.KindProperty, UpToDateCheckInput.SetProperty), - upToDateCheckOutputItemsByKindBySetName: UpdateItemsByKindBySetName(UpToDateCheckOutputItemsByKindBySetName, jointRuleUpdate, UpToDateCheckOutput.SchemaName, UpToDateCheckOutput.KindProperty, UpToDateCheckOutput.SetProperty), - upToDateCheckBuiltItemsByKindBySetName: UpdateItemsByKindBySetName(UpToDateCheckBuiltItemsByKindBySetName, jointRuleUpdate, UpToDateCheckBuilt.SchemaName, UpToDateCheckBuilt.KindProperty, UpToDateCheckBuilt.SetProperty, metadata => !metadata.TryGetValue(UpToDateCheckBuilt.OriginalProperty, out string? source) || string.IsNullOrEmpty(source)), - buildFromInputFileItems: UpdateBuildFromInputFileItems(), - resolvedAnalyzerReferencePaths: UpdateResolvedAnalyzerReferencePaths(), - resolvedCompilationReferencePaths: resolvedCompilationReferencePaths, - copyReferenceInputs: copyReferenceInputs, - presentBuildAccelerationIncompatiblePackages: UpdatePresentBuildAccelerationIncompatiblePackages(), - lastItemsChangedAtUtc: lastItemsChangedAtUtc, - lastItemChanges.ToImmutableArray(), - itemHash, - projectCopyData: UpdateCopyData()); - - string? UpdateCopyUpToDateMarkerItem() + if (itemType is null || !inputSourceItemTypes.Contains(itemType)) { - if (jointRuleUpdate.ProjectChanges.TryGetValue(CopyUpToDateMarker.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) - { - return change.After.Items.Count == 1 ? change.After.Items.Single().Key : null; - } - - return CopyUpToDateMarkerItem; + continue; } - static ImmutableDictionary>> UpdateItemsByKindBySetName( - ImmutableDictionary>> prior, - IProjectSubscriptionUpdate update, - string itemSchemaName, - string kindPropertyName, - string setPropertyName, - Predicate>? metadataPredicate = null) + if (!inputSourceItemsByItemTypeBuilder.TryGetValue(itemType, out ImmutableArray before)) { - if (!update.ProjectChanges.TryGetValue(itemSchemaName, out IProjectChangeDescription? projectChangeDescription) || !projectChangeDescription.Difference.AnyChanges) - { - // No change in state for this collection. Return the prior data unchanged. - return prior; - } + before = []; + } - var itemsByKindBySet = new Dictionary>>(BuildUpToDateCheck.SetNameComparer); + projectFileClassifier ??= BuildClassifier(); - foreach ((string item, IImmutableDictionary metadata) in projectChangeDescription.After.TryGetOrderedItems()) - { - if (metadataPredicate is not null && !metadataPredicate(metadata)) - { - continue; - } + var after = projectChange.After.Items + .Select(pair => pair.Key) + .Where(path => !projectFileClassifier.IsNonModifiable(path)) + .ToHashSet(StringComparers.Paths); - string? setNames = metadata.GetStringProperty(setPropertyName); - string kindName = metadata.GetStringProperty(kindPropertyName) ?? BuildUpToDateCheck.DefaultKindName; + var diff = new SetDiff(before, after, StringComparers.Paths); - if (setNames is not null) - { - foreach (string setName in new LazyStringSplit(setNames, ';')) - { - AddItem(setName, kindName, item); - } - } - else - { - AddItem(BuildUpToDateCheck.DefaultSetName, kindName, item); - } - } + foreach (string item in diff.Added) + { + lastItemChanges.Add((IsAdd: true, itemType, item)); + } - return itemsByKindBySet.ToImmutableDictionary( - pair => pair.Key, - pair => pair.Value.ToImmutableDictionary( - pair => pair.Key, - pair => pair.Value.ToImmutableArray()), - BuildUpToDateCheck.SetNameComparer); + foreach (string item in diff.Removed) + { + lastItemChanges.Add((IsAdd: false, itemType, item)); + } - void AddItem(string setName, string kindName, string item) - { - if (!itemsByKindBySet.TryGetValue(setName, out Dictionary>? itemsByKind)) - { - itemsByKindBySet[setName] = itemsByKind = new Dictionary>(BuildUpToDateCheck.KindNameComparer); - } + inputSourceItemsByItemTypeBuilder[itemType] = after.ToImmutableArray(); + itemsChanged = true; + } - if (!itemsByKind.TryGetValue(kindName, out HashSet? items)) - { - itemsByKind[kindName] = items = new HashSet(StringComparers.Paths); - } + ImmutableDictionary> inputSourceItemsByItemType = inputSourceItemsByItemTypeBuilder.ToImmutable(); - items.Add(item); - } - } + int itemHash = BuildUpToDateCheck.ComputeItemHash(inputSourceItemsByItemType); + + DateTime? lastItemsChangedAtUtc = LastItemsChangedAtUtc; - ImmutableArray<(string DestinationRelative, string SourceRelative)> UpdateBuildFromInputFileItems() + if (itemHash != ItemHash && ItemHash != null) + { + // The set of items has changed. + // For the case that the project loaded and no hash was available, do not touch lastItemsChangedAtUtc. + // Doing so when the project is actually up-to-date would cause builds to be scheduled until something + // actually changes. + lastItemsChangedAtUtc = DateTime.UtcNow; + } + else if (itemsChanged && !InputSourceItemTypes.IsEmpty) + { + // When we previously had zero item types, we can surmise that the project has just been loaded. In such + // a case it is not correct to assume that the items changed, and so we do not update the timestamp. + // If we did, and the project was up-to-date when loaded, it would remain out-of-date until something changed, + // causing redundant builds until that time. See https://github.com/dotnet/project-system/issues/5386. + lastItemsChangedAtUtc = DateTime.UtcNow; + } + + (ImmutableArray resolvedCompilationReferencePaths, ImmutableArray copyReferenceInputs) = UpdateResolvedCompilationReferences(); + + return new( + ProjectConfiguration, + msBuildProjectDirectory, + projectTargetPath, + copyUpToDateMarkerItem: UpdateCopyUpToDateMarkerItem(), + outputRelativeOrFullPath, + newestImportInput, + isDisabled: isDisabled, + isBuildAccelerationEnabled: isBuildAccelerationEnabled, + inputSourceItemTypes: inputSourceItemTypes.ToImmutableArray(), + inputSourceItemsByItemType: inputSourceItemsByItemType, + upToDateCheckInputItemsByKindBySetName: UpdateItemsByKindBySetName(UpToDateCheckInputItemsByKindBySetName, jointRuleUpdate, UpToDateCheckInput.SchemaName, UpToDateCheckInput.KindProperty, UpToDateCheckInput.SetProperty), + upToDateCheckOutputItemsByKindBySetName: UpdateItemsByKindBySetName(UpToDateCheckOutputItemsByKindBySetName, jointRuleUpdate, UpToDateCheckOutput.SchemaName, UpToDateCheckOutput.KindProperty, UpToDateCheckOutput.SetProperty), + upToDateCheckBuiltItemsByKindBySetName: UpdateItemsByKindBySetName(UpToDateCheckBuiltItemsByKindBySetName, jointRuleUpdate, UpToDateCheckBuilt.SchemaName, UpToDateCheckBuilt.KindProperty, UpToDateCheckBuilt.SetProperty, metadata => !metadata.TryGetValue(UpToDateCheckBuilt.OriginalProperty, out string? source) || string.IsNullOrEmpty(source)), + buildFromInputFileItems: UpdateBuildFromInputFileItems(), + resolvedAnalyzerReferencePaths: UpdateResolvedAnalyzerReferencePaths(), + resolvedCompilationReferencePaths: resolvedCompilationReferencePaths, + copyReferenceInputs: copyReferenceInputs, + presentBuildAccelerationIncompatiblePackages: UpdatePresentBuildAccelerationIncompatiblePackages(), + lastItemsChangedAtUtc: lastItemsChangedAtUtc, + lastItemChanges.ToImmutableArray(), + itemHash, + projectCopyData: UpdateCopyData()); + + string? UpdateCopyUpToDateMarkerItem() + { + if (jointRuleUpdate.ProjectChanges.TryGetValue(CopyUpToDateMarker.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) { - if (jointRuleUpdate.ProjectChanges.TryGetValue(UpToDateCheckBuilt.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) - { - var buildFromInputFileItemsBuilder = new Dictionary(StringComparers.Paths); + return change.After.Items.Count == 1 ? change.After.Items.Single().Key : null; + } - foreach ((string destination, IImmutableDictionary metadata) in change.After.Items) - { - if (metadata.TryGetValue(UpToDateCheckBuilt.OriginalProperty, out string? source) && !string.IsNullOrEmpty(source)) - { - // This file is copied, not built - // Remember the `Original` source for later - buildFromInputFileItemsBuilder[destination] = source; - } - } + return CopyUpToDateMarkerItem; + } - return buildFromInputFileItemsBuilder.Select(kvp => (kvp.Key, kvp.Value)).ToImmutableArray(); - } - else - { - return BuiltFromInputFileItems; - } + static ImmutableDictionary>> UpdateItemsByKindBySetName( + ImmutableDictionary>> prior, + IProjectSubscriptionUpdate update, + string itemSchemaName, + string kindPropertyName, + string setPropertyName, + Predicate>? metadataPredicate = null) + { + if (!update.ProjectChanges.TryGetValue(itemSchemaName, out IProjectChangeDescription? projectChangeDescription) || !projectChangeDescription.Difference.AnyChanges) + { + // No change in state for this collection. Return the prior data unchanged. + return prior; } - ImmutableArray UpdatePresentBuildAccelerationIncompatiblePackages() + var itemsByKindBySet = new Dictionary>>(BuildUpToDateCheck.SetNameComparer); + + foreach ((string item, IImmutableDictionary metadata) in projectChangeDescription.After.TryGetOrderedItems()) { - if (jointRuleUpdate.ProjectChanges.TryGetValue(PackageReference.SchemaName, out IProjectChangeDescription? packageReferenceChange) && - jointRuleUpdate.ProjectChanges.TryGetValue(BuildAccelerationIncompatiblePackage.SchemaName, out IProjectChangeDescription? incompatiblePackageChange) && - (packageReferenceChange.Difference.AnyChanges || incompatiblePackageChange.Difference.AnyChanges)) + if (metadataPredicate is not null && !metadataPredicate(metadata)) { - ImmutableArray.Builder? builder = null; + continue; + } - foreach ((string incompatiblePackage, _) in incompatiblePackageChange.After.Items) + string? setNames = metadata.GetStringProperty(setPropertyName); + string kindName = metadata.GetStringProperty(kindPropertyName) ?? BuildUpToDateCheck.DefaultKindName; + + if (setNames is not null) + { + foreach (string setName in new LazyStringSplit(setNames, ';')) { - if (packageReferenceChange.After.Items.ContainsKey(incompatiblePackage)) - { - builder ??= ImmutableArray.CreateBuilder(initialCapacity: 1); - builder.Add(incompatiblePackage); - } + AddItem(setName, kindName, item); } - - return builder is null - ? [] - : builder.Capacity == builder.Count - ? builder.MoveToImmutable() - : builder.ToImmutable(); } else { - return PresentBuildAccelerationIncompatiblePackages; + AddItem(BuildUpToDateCheck.DefaultSetName, kindName, item); } } - ImmutableArray UpdateResolvedAnalyzerReferencePaths() + return itemsByKindBySet.ToImmutableDictionary( + pair => pair.Key, + pair => pair.Value.ToImmutableDictionary( + pair => pair.Key, + pair => pair.Value.ToImmutableArray()), + BuildUpToDateCheck.SetNameComparer); + + void AddItem(string setName, string kindName, string item) { - if (jointRuleUpdate.ProjectChanges.TryGetValue(ResolvedAnalyzerReference.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) + if (!itemsByKindBySet.TryGetValue(setName, out Dictionary>? itemsByKind)) { - projectFileClassifier ??= BuildClassifier(); + itemsByKindBySet[setName] = itemsByKind = new Dictionary>(BuildUpToDateCheck.KindNameComparer); + } - return change.After.Items - .Select(item => item.Value[ResolvedAnalyzerReference.ResolvedPathProperty]) - .Where(path => !projectFileClassifier.IsNonModifiable(path)) - .Distinct(StringComparers.Paths) - .ToImmutableArray(); + if (!itemsByKind.TryGetValue(kindName, out HashSet? items)) + { + itemsByKind[kindName] = items = new HashSet(StringComparers.Paths); } - return ResolvedAnalyzerReferencePaths; + items.Add(item); } + } - (ImmutableArray ResolvedCompilationReferencePaths, ImmutableArray CopyReferenceInputs) UpdateResolvedCompilationReferences() + ImmutableArray<(string DestinationRelative, string SourceRelative)> UpdateBuildFromInputFileItems() + { + if (jointRuleUpdate.ProjectChanges.TryGetValue(UpToDateCheckBuilt.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) { - if (jointRuleUpdate.ProjectChanges.TryGetValue(ResolvedCompilationReference.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) - { - // We identify non-modifiable inputs (i.e. anything in Program Files, the VS install dir, or NuGet cache folders) - // and exclude them from the set of inputs we scan when an up-to-date query is made. - // - // For a .NET 5 xUnit project, this cuts the number of file timestamps checked from 187 to 17. Most of those are - // reference assemblies for the framework, which clearly aren't expected to change over time. - projectFileClassifier ??= BuildClassifier(); - - HashSet resolvedCompilationReferencePathsBuilder = new(StringComparers.Paths); - HashSet copyReferenceInputsBuilder = new(StringComparers.Paths); + var buildFromInputFileItemsBuilder = new Dictionary(StringComparers.Paths); - foreach (IImmutableDictionary itemMetadata in change.After.Items.Values) + foreach ((string destination, IImmutableDictionary metadata) in change.After.Items) + { + if (metadata.TryGetValue(UpToDateCheckBuilt.OriginalProperty, out string? source) && !string.IsNullOrEmpty(source)) { - string originalPath = itemMetadata[ResolvedCompilationReference.OriginalPathProperty]; - string resolvedPath = itemMetadata[ResolvedCompilationReference.ResolvedPathProperty]; - string copyReferenceInput = itemMetadata[ResolvedCompilationReference.CopyUpToDateMarkerProperty]; - - if (!projectFileClassifier.IsNonModifiable(resolvedPath)) - { - resolvedCompilationReferencePathsBuilder.Add(resolvedPath); - } - - if (!string.IsNullOrWhiteSpace(originalPath)) - { - copyReferenceInputsBuilder.Add(originalPath); - } - - if (!string.IsNullOrWhiteSpace(copyReferenceInput)) - { - copyReferenceInputsBuilder.Add(copyReferenceInput); - } + // This file is copied, not built + // Remember the `Original` source for later + buildFromInputFileItemsBuilder[destination] = source; } - - return (resolvedCompilationReferencePathsBuilder.ToImmutableArray(), copyReferenceInputsBuilder.ToImmutableArray()); - } - else - { - return (ResolvedCompilationReferencePaths, CopyReferenceInputs); } + + return buildFromInputFileItemsBuilder.Select(kvp => (kvp.Key, kvp.Value)).ToImmutableArray(); + } + else + { + return BuiltFromInputFileItems; } + } - ProjectCopyData UpdateCopyData() + ImmutableArray UpdatePresentBuildAccelerationIncompatiblePackages() + { + if (jointRuleUpdate.ProjectChanges.TryGetValue(PackageReference.SchemaName, out IProjectChangeDescription? packageReferenceChange) && + jointRuleUpdate.ProjectChanges.TryGetValue(BuildAccelerationIncompatiblePackage.SchemaName, out IProjectChangeDescription? incompatiblePackageChange) && + (packageReferenceChange.Difference.AnyChanges || incompatiblePackageChange.Difference.AnyChanges)) { - if (jointRuleUpdate.ProjectChanges.TryGetValue(CopyToOutputDirectoryItem.SchemaName, out IProjectChangeDescription? change1) && - jointRuleUpdate.ProjectChanges.TryGetValue(ResolvedProjectReference.SchemaName, out IProjectChangeDescription? change2) && - jointRuleUpdate.ProjectChanges.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectChangeDescription? change3)) + ImmutableArray.Builder? builder = null; + + foreach ((string incompatiblePackage, _) in incompatiblePackageChange.After.Items) { - if ((change1.Difference.AnyChanges || change2.Difference.AnyChanges || change3.Difference.AnyChanges) && - change3.After.Properties.TryGetStringProperty(ConfigurationGeneral.TargetPathProperty, out string? targetPath)) + if (packageReferenceChange.After.Items.ContainsKey(incompatiblePackage)) { - // Register this project's data with the CopyToOutputDirectoryItem tracking service. + builder ??= ImmutableArray.CreateBuilder(initialCapacity: 1); + builder.Add(incompatiblePackage); + } + } - projectFileClassifier ??= BuildClassifier(); + return builder is null + ? [] + : builder.Capacity == builder.Count + ? builder.MoveToImmutable() + : builder.ToImmutable(); + } + else + { + return PresentBuildAccelerationIncompatiblePackages; + } + } - ImmutableArray copyItems = change1.After.Items - .Where(pair => !projectFileClassifier.IsNonModifiable(pair.Key)) - .Select(pair => new CopyItem(path: pair.Key, metadata: pair.Value)) - .ToImmutableArray(); + ImmutableArray UpdateResolvedAnalyzerReferencePaths() + { + if (jointRuleUpdate.ProjectChanges.TryGetValue(ResolvedAnalyzerReference.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) + { + projectFileClassifier ??= BuildClassifier(); - ImmutableArray referenceItems = change2.After.Items.Where(pair => IncludeProjectReference(pair.Value)).Select(item => item.Key).ToImmutableArray(); + return change.After.Items + .Select(item => item.Value[ResolvedAnalyzerReference.ResolvedPathProperty]) + .Where(path => !projectFileClassifier.IsNonModifiable(path)) + .Distinct(StringComparers.Paths) + .ToImmutableArray(); + } - bool produceReferenceAssembly = change3.After.Properties.GetBoolProperty(ConfigurationGeneral.ProduceReferenceAssemblyProperty) ?? false; + return ResolvedAnalyzerReferencePaths; + } - return new ProjectCopyData(msBuildProjectFullPath, targetPath, produceReferenceAssembly, copyItems, referenceItems); - } - } + (ImmutableArray ResolvedCompilationReferencePaths, ImmutableArray CopyReferenceInputs) UpdateResolvedCompilationReferences() + { + if (jointRuleUpdate.ProjectChanges.TryGetValue(ResolvedCompilationReference.SchemaName, out IProjectChangeDescription? change) && change.Difference.AnyChanges) + { + // We identify non-modifiable inputs (i.e. anything in Program Files, the VS install dir, or NuGet cache folders) + // and exclude them from the set of inputs we scan when an up-to-date query is made. + // + // For a .NET 5 xUnit project, this cuts the number of file timestamps checked from 187 to 17. Most of those are + // reference assemblies for the framework, which clearly aren't expected to change over time. + projectFileClassifier ??= BuildClassifier(); - return ProjectCopyData; + HashSet resolvedCompilationReferencePathsBuilder = new(StringComparers.Paths); + HashSet copyReferenceInputsBuilder = new(StringComparers.Paths); - static bool IncludeProjectReference(IImmutableDictionary metadata) + foreach (IImmutableDictionary itemMetadata in change.After.Items.Values) { - // TODO this filtering is overzealous. In each of these cases, there are subtleties to how - // builds handle the output assembly vs. CopyToOutputDirectory items both of the directly - // referenced project, and of transitively referenced projects. To improve this we need - // more information on the edges of our project reference graph. + string originalPath = itemMetadata[ResolvedCompilationReference.OriginalPathProperty]; + string resolvedPath = itemMetadata[ResolvedCompilationReference.ResolvedPathProperty]; + string copyReferenceInput = itemMetadata[ResolvedCompilationReference.CopyUpToDateMarkerProperty]; - // Exclude any project reference for which we do not reference the output assembly. - if (metadata.GetBoolProperty(ResolvedProjectReference.ReferenceOutputAssemblyProperty) == false) + if (!projectFileClassifier.IsNonModifiable(resolvedPath)) { - return false; + resolvedCompilationReferencePathsBuilder.Add(resolvedPath); } - // Exclude any project reference having Private="false" (aka CopyLocal="No"). - if (metadata.GetBoolProperty(ResolvedProjectReference.PrivateProperty) == false) + if (!string.IsNullOrWhiteSpace(originalPath)) { - return false; + copyReferenceInputsBuilder.Add(originalPath); } - // Exclude any project reference having EmbedInteropTypes="true". - if (metadata.GetBoolProperty(ResolvedProjectReference.EmbedInteropTypesProperty) == true) + if (!string.IsNullOrWhiteSpace(copyReferenceInput)) { - return false; + copyReferenceInputsBuilder.Add(copyReferenceInput); } + } + + return (resolvedCompilationReferencePathsBuilder.ToImmutableArray(), copyReferenceInputsBuilder.ToImmutableArray()); + } + else + { + return (ResolvedCompilationReferencePaths, CopyReferenceInputs); + } + } + + ProjectCopyData UpdateCopyData() + { + if (jointRuleUpdate.ProjectChanges.TryGetValue(CopyToOutputDirectoryItem.SchemaName, out IProjectChangeDescription? change1) && + jointRuleUpdate.ProjectChanges.TryGetValue(ResolvedProjectReference.SchemaName, out IProjectChangeDescription? change2) && + jointRuleUpdate.ProjectChanges.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectChangeDescription? change3)) + { + if ((change1.Difference.AnyChanges || change2.Difference.AnyChanges || change3.Difference.AnyChanges) && + change3.After.Properties.TryGetStringProperty(ConfigurationGeneral.TargetPathProperty, out string? targetPath)) + { + // Register this project's data with the CopyToOutputDirectoryItem tracking service. + + projectFileClassifier ??= BuildClassifier(); - return true; + ImmutableArray copyItems = change1.After.Items + .Where(pair => !projectFileClassifier.IsNonModifiable(pair.Key)) + .Select(pair => new CopyItem(path: pair.Key, metadata: pair.Value)) + .ToImmutableArray(); + + ImmutableArray referenceItems = change2.After.Items.Where(pair => IncludeProjectReference(pair.Value)).Select(item => item.Key).ToImmutableArray(); + + bool produceReferenceAssembly = change3.After.Properties.GetBoolProperty(ConfigurationGeneral.ProduceReferenceAssemblyProperty) ?? false; + + return new ProjectCopyData(msBuildProjectFullPath, targetPath, produceReferenceAssembly, copyItems, referenceItems); } } - ProjectFileClassifier BuildClassifier() + return ProjectCopyData; + + static bool IncludeProjectReference(IImmutableDictionary metadata) { - return new ProjectFileClassifier + // TODO this filtering is overzealous. In each of these cases, there are subtleties to how + // builds handle the output assembly vs. CopyToOutputDirectory items both of the directly + // referenced project, and of transitively referenced projects. To improve this we need + // more information on the edges of our project reference graph. + + // Exclude any project reference for which we do not reference the output assembly. + if (metadata.GetBoolProperty(ResolvedProjectReference.ReferenceOutputAssemblyProperty) == false) + { + return false; + } + + // Exclude any project reference having Private="false" (aka CopyLocal="No"). + if (metadata.GetBoolProperty(ResolvedProjectReference.PrivateProperty) == false) + { + return false; + } + + // Exclude any project reference having EmbedInteropTypes="true". + if (metadata.GetBoolProperty(ResolvedProjectReference.EmbedInteropTypesProperty) == true) { - NuGetPackageFolders = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.NuGetPackageFoldersProperty, "") - }; + return false; + } + + return true; } } - /// - /// For unit tests only. - /// - internal UpToDateCheckImplicitConfiguredInput WithLastItemsChangedAtUtc(DateTime? lastItemsChangedAtUtc) + ProjectFileClassifier BuildClassifier() { - return new( - ProjectConfiguration, - MSBuildProjectDirectory, - ProjectTargetPath, - CopyUpToDateMarkerItem, - OutputRelativeOrFullPath, - NewestImportInput, - IsDisabled, - IsBuildAccelerationEnabled, - InputSourceItemTypes, - InputSourceItemsByItemType, - UpToDateCheckInputItemsByKindBySetName, - UpToDateCheckOutputItemsByKindBySetName, - UpToDateCheckBuiltItemsByKindBySetName, - BuiltFromInputFileItems, - ResolvedAnalyzerReferencePaths, - ResolvedCompilationReferencePaths, - CopyReferenceInputs, - PresentBuildAccelerationIncompatiblePackages, - lastItemsChangedAtUtc, - LastItemChanges, - ItemHash, - ProjectCopyData); + return new ProjectFileClassifier + { + NuGetPackageFolders = jointRuleUpdate.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.NuGetPackageFoldersProperty, "") + }; } + } - public UpToDateCheckImplicitConfiguredInput WithRestoredState(int itemHash, DateTime? lastItemsChangedAtUtc) - { - return new( - ProjectConfiguration, - MSBuildProjectDirectory, - ProjectTargetPath, - CopyUpToDateMarkerItem, - OutputRelativeOrFullPath, - NewestImportInput, - IsDisabled, - IsBuildAccelerationEnabled, - InputSourceItemTypes, - InputSourceItemsByItemType, - UpToDateCheckInputItemsByKindBySetName, - UpToDateCheckOutputItemsByKindBySetName, - UpToDateCheckBuiltItemsByKindBySetName, - BuiltFromInputFileItems, - ResolvedAnalyzerReferencePaths, - ResolvedCompilationReferencePaths, - CopyReferenceInputs, - PresentBuildAccelerationIncompatiblePackages, - lastItemsChangedAtUtc, - LastItemChanges, - itemHash, - ProjectCopyData); - } + /// + /// For unit tests only. + /// + internal UpToDateCheckImplicitConfiguredInput WithLastItemsChangedAtUtc(DateTime? lastItemsChangedAtUtc) + { + return new( + ProjectConfiguration, + MSBuildProjectDirectory, + ProjectTargetPath, + CopyUpToDateMarkerItem, + OutputRelativeOrFullPath, + NewestImportInput, + IsDisabled, + IsBuildAccelerationEnabled, + InputSourceItemTypes, + InputSourceItemsByItemType, + UpToDateCheckInputItemsByKindBySetName, + UpToDateCheckOutputItemsByKindBySetName, + UpToDateCheckBuiltItemsByKindBySetName, + BuiltFromInputFileItems, + ResolvedAnalyzerReferencePaths, + ResolvedCompilationReferencePaths, + CopyReferenceInputs, + PresentBuildAccelerationIncompatiblePackages, + lastItemsChangedAtUtc, + LastItemChanges, + ItemHash, + ProjectCopyData); + } + + public UpToDateCheckImplicitConfiguredInput WithRestoredState(int itemHash, DateTime? lastItemsChangedAtUtc) + { + return new( + ProjectConfiguration, + MSBuildProjectDirectory, + ProjectTargetPath, + CopyUpToDateMarkerItem, + OutputRelativeOrFullPath, + NewestImportInput, + IsDisabled, + IsBuildAccelerationEnabled, + InputSourceItemTypes, + InputSourceItemsByItemType, + UpToDateCheckInputItemsByKindBySetName, + UpToDateCheckOutputItemsByKindBySetName, + UpToDateCheckBuiltItemsByKindBySetName, + BuiltFromInputFileItems, + ResolvedAnalyzerReferencePaths, + ResolvedCompilationReferencePaths, + CopyReferenceInputs, + PresentBuildAccelerationIncompatiblePackages, + lastItemsChangedAtUtc, + LastItemChanges, + itemHash, + ProjectCopyData); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputDataSource.cs index 005c0e1031..96d195b489 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputDataSource.cs @@ -11,144 +11,143 @@ Microsoft.VisualStudio.ProjectSystem.IProjectItemSchema, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectCatalogSnapshot>; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +[Export(typeof(IUpToDateCheckImplicitConfiguredInputDataSource))] +[AppliesTo(BuildUpToDateCheck.AppliesToExpression)] +[ExportInitialBuildRulesSubscriptions([ + ResolvedAnalyzerReference.SchemaName, + ResolvedCompilationReference.SchemaName, + ResolvedProjectReference.SchemaName, + UpToDateCheckInput.SchemaName, + UpToDateCheckOutput.SchemaName, + UpToDateCheckBuilt.SchemaName, + CopyToOutputDirectoryItem.SchemaName, + BuildAccelerationIncompatiblePackage.SchemaName])] +internal sealed class UpToDateCheckImplicitConfiguredInputDataSource : ChainedProjectValueDataSourceBase, IUpToDateCheckImplicitConfiguredInputDataSource { - /// - [Export(typeof(IUpToDateCheckImplicitConfiguredInputDataSource))] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - [ExportInitialBuildRulesSubscriptions([ - ResolvedAnalyzerReference.SchemaName, - ResolvedCompilationReference.SchemaName, - ResolvedProjectReference.SchemaName, - UpToDateCheckInput.SchemaName, - UpToDateCheckOutput.SchemaName, - UpToDateCheckBuilt.SchemaName, - CopyToOutputDirectoryItem.SchemaName, - BuildAccelerationIncompatiblePackage.SchemaName])] - internal sealed class UpToDateCheckImplicitConfiguredInputDataSource : ChainedProjectValueDataSourceBase, IUpToDateCheckImplicitConfiguredInputDataSource + private readonly ConfiguredProject _configuredProject; + private readonly IProjectItemSchemaService _projectItemSchemaService; + private readonly IUpToDateCheckStatePersistence _persistentState; + private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; + private readonly ICopyItemAggregator _copyItemAggregator; + + /// + /// The set of rules we consume that come from evaluation. + /// + private static ImmutableHashSet EvaluationRuleNames { get; } = ImmutableStringHashSet.EmptyOrdinal + .Add(ConfigurationGeneral.SchemaName) + .Add(CopyUpToDateMarker.SchemaName) + .Add(PackageReference.SchemaName); + + /// + /// The set of rules we consume that come from design-time build. + /// + private static ImmutableHashSet BuildRuleNames { get; } = ImmutableStringHashSet.EmptyOrdinal + .Add(ResolvedAnalyzerReference.SchemaName) + .Add(ResolvedCompilationReference.SchemaName) + .Add(ResolvedProjectReference.SchemaName) + .Add(UpToDateCheckInput.SchemaName) + .Add(UpToDateCheckOutput.SchemaName) + .Add(UpToDateCheckBuilt.SchemaName) + .Add(CopyToOutputDirectoryItem.SchemaName) + .Add(BuildAccelerationIncompatiblePackage.SchemaName); + + [ImportingConstructor] + public UpToDateCheckImplicitConfiguredInputDataSource( + ConfiguredProject containingProject, + IProjectItemSchemaService projectItemSchemaService, + [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, + ICopyItemAggregator copyItemAggregator, + IUpToDateCheckStatePersistence persistentState) + : base(containingProject, synchronousDisposal: false, registerDataSource: false) { - private readonly ConfiguredProject _configuredProject; - private readonly IProjectItemSchemaService _projectItemSchemaService; - private readonly IUpToDateCheckStatePersistence _persistentState; - private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; - private readonly ICopyItemAggregator _copyItemAggregator; - - /// - /// The set of rules we consume that come from evaluation. - /// - private static ImmutableHashSet EvaluationRuleNames { get; } = ImmutableStringHashSet.EmptyOrdinal - .Add(ConfigurationGeneral.SchemaName) - .Add(CopyUpToDateMarker.SchemaName) - .Add(PackageReference.SchemaName); - - /// - /// The set of rules we consume that come from design-time build. - /// - private static ImmutableHashSet BuildRuleNames { get; } = ImmutableStringHashSet.EmptyOrdinal - .Add(ResolvedAnalyzerReference.SchemaName) - .Add(ResolvedCompilationReference.SchemaName) - .Add(ResolvedProjectReference.SchemaName) - .Add(UpToDateCheckInput.SchemaName) - .Add(UpToDateCheckOutput.SchemaName) - .Add(UpToDateCheckBuilt.SchemaName) - .Add(CopyToOutputDirectoryItem.SchemaName) - .Add(BuildAccelerationIncompatiblePackage.SchemaName); - - [ImportingConstructor] - public UpToDateCheckImplicitConfiguredInputDataSource( - ConfiguredProject containingProject, - IProjectItemSchemaService projectItemSchemaService, - [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, - ICopyItemAggregator copyItemAggregator, - IUpToDateCheckStatePersistence persistentState) - : base(containingProject, synchronousDisposal: false, registerDataSource: false) - { - _configuredProject = containingProject; - _projectItemSchemaService = projectItemSchemaService; - _projectAsynchronousTasksService = projectAsynchronousTasksService; - _copyItemAggregator = copyItemAggregator; - _persistentState = persistentState; - } + _configuredProject = containingProject; + _projectItemSchemaService = projectItemSchemaService; + _projectAsynchronousTasksService = projectAsynchronousTasksService; + _copyItemAggregator = copyItemAggregator; + _persistentState = persistentState; + } - protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) - { - Assumes.Present(_configuredProject.Services.ProjectSubscription); + protected override IDisposable LinkExternalInput(ITargetBlock> targetBlock) + { + Assumes.Present(_configuredProject.Services.ProjectSubscription); - bool attemptedStateRestore = false; + bool attemptedStateRestore = false; - // Initial state is empty. We will evolve this reference over time, updating it iteratively - // on each new data update. - UpToDateCheckImplicitConfiguredInput state = UpToDateCheckImplicitConfiguredInput.CreateEmpty(_configuredProject.ProjectConfiguration); + // Initial state is empty. We will evolve this reference over time, updating it iteratively + // on each new data update. + UpToDateCheckImplicitConfiguredInput state = UpToDateCheckImplicitConfiguredInput.CreateEmpty(_configuredProject.ProjectConfiguration); - IPropagatorBlock, IProjectVersionedValue> transformBlock - = DataflowBlockSlim.CreateTransformBlock, IProjectVersionedValue>(TransformAsync); + IPropagatorBlock, IProjectVersionedValue> transformBlock + = DataflowBlockSlim.CreateTransformBlock, IProjectVersionedValue>(TransformAsync); - IProjectValueDataSource source1 = _configuredProject.Services.ProjectSubscription.JointRuleSource; - IProjectValueDataSource source2 = _configuredProject.Services.ProjectSubscription.SourceItemsRuleSource; - IProjectItemSchemaService source3 = _projectItemSchemaService; - IProjectValueDataSource source4 = _configuredProject.Services.ProjectSubscription.ProjectCatalogSource; + IProjectValueDataSource source1 = _configuredProject.Services.ProjectSubscription.JointRuleSource; + IProjectValueDataSource source2 = _configuredProject.Services.ProjectSubscription.SourceItemsRuleSource; + IProjectItemSchemaService source3 = _projectItemSchemaService; + IProjectValueDataSource source4 = _configuredProject.Services.ProjectSubscription.ProjectCatalogSource; - return new DisposableBag - { - // Sync-link various sources to our transform block - ProjectDataSources.SyncLinkTo( - source1.SourceBlock.SyncLinkOptions(DataflowOption.WithJointRuleNames(EvaluationRuleNames, BuildRuleNames)), - source2.SourceBlock.SyncLinkOptions(), - source3.SourceBlock.SyncLinkOptions(), - source4.SourceBlock.SyncLinkOptions(), - target: transformBlock, - linkOptions: DataflowOption.PropagateCompletion, - CancellationToken.None), - - // Link the transform block to our target block - transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion), - - JoinUpstreamDataSources(source1, source2, source3, source4) - }; - - async Task> TransformAsync(IProjectVersionedValue e) + return new DisposableBag + { + // Sync-link various sources to our transform block + ProjectDataSources.SyncLinkTo( + source1.SourceBlock.SyncLinkOptions(DataflowOption.WithJointRuleNames(EvaluationRuleNames, BuildRuleNames)), + source2.SourceBlock.SyncLinkOptions(), + source3.SourceBlock.SyncLinkOptions(), + source4.SourceBlock.SyncLinkOptions(), + target: transformBlock, + linkOptions: DataflowOption.PropagateCompletion, + CancellationToken.None), + + // Link the transform block to our target block + transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion), + + JoinUpstreamDataSources(source1, source2, source3, source4) + }; + + async Task> TransformAsync(IProjectVersionedValue e) + { + if (!attemptedStateRestore) { - if (!attemptedStateRestore) - { - attemptedStateRestore = true; + attemptedStateRestore = true; - // Restoring state requires the UI thread. We must use JTF.RunAsync here to ensure the UI - // thread is shared between related work and prevent deadlocks. - (int ItemHash, DateTime? InputsChangedAtUtc)? restoredState = - await JoinableFactory.RunAsync(() => _persistentState.RestoreItemStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, _projectAsynchronousTasksService.UnloadCancellationToken)); + // Restoring state requires the UI thread. We must use JTF.RunAsync here to ensure the UI + // thread is shared between related work and prevent deadlocks. + (int ItemHash, DateTime? InputsChangedAtUtc)? restoredState = + await JoinableFactory.RunAsync(() => _persistentState.RestoreItemStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, _projectAsynchronousTasksService.UnloadCancellationToken)); - if (restoredState is not null) - { - state = state.WithRestoredState(restoredState.Value.ItemHash, restoredState.Value.InputsChangedAtUtc); - } + if (restoredState is not null) + { + state = state.WithRestoredState(restoredState.Value.ItemHash, restoredState.Value.InputsChangedAtUtc); } + } - int? priorItemHash = state.ItemHash; - DateTime? priorLastItemsChangedAtUtc = state.LastItemsChangedAtUtc; - ProjectCopyData priorCopyData = state.ProjectCopyData; - - state = state.Update( - jointRuleUpdate: e.Value.Item1, - sourceItemsUpdate: e.Value.Item2, - projectItemSchema: e.Value.Item3, - projectCatalogSnapshot: e.Value.Item4); + int? priorItemHash = state.ItemHash; + DateTime? priorLastItemsChangedAtUtc = state.LastItemsChangedAtUtc; + ProjectCopyData priorCopyData = state.ProjectCopyData; - if (priorCopyData != state.ProjectCopyData) - { - // If the FUTDC is disabled, we won't have valid copy items in the snapshot. - if (!state.IsDisabled) - { - _copyItemAggregator.SetProjectData(state.ProjectCopyData); - } - } + state = state.Update( + jointRuleUpdate: e.Value.Item1, + sourceItemsUpdate: e.Value.Item2, + projectItemSchema: e.Value.Item3, + projectCatalogSnapshot: e.Value.Item4); - if (state.ItemHash is not null && (priorItemHash != state.ItemHash || priorLastItemsChangedAtUtc != state.LastItemsChangedAtUtc)) + if (priorCopyData != state.ProjectCopyData) + { + // If the FUTDC is disabled, we won't have valid copy items in the snapshot. + if (!state.IsDisabled) { - await _persistentState.StoreItemStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, state.ItemHash.Value, state.LastItemsChangedAtUtc, _projectAsynchronousTasksService.UnloadCancellationToken); + _copyItemAggregator.SetProjectData(state.ProjectCopyData); } + } - return new ProjectVersionedValue(state, e.DataSourceVersions); + if (state.ItemHash is not null && (priorItemHash != state.ItemHash || priorLastItemsChangedAtUtc != state.LastItemsChangedAtUtc)) + { + await _persistentState.StoreItemStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, state.ItemHash.Value, state.LastItemsChangedAtUtc, _projectAsynchronousTasksService.UnloadCancellationToken); } + + return new ProjectVersionedValue(state, e.DataSourceVersions); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.ConfiguredProjectComparer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.ConfiguredProjectComparer.cs index 4b7472cf6a..1a45b93680 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.ConfiguredProjectComparer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.ConfiguredProjectComparer.cs @@ -1,50 +1,49 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +internal sealed partial class UpToDateCheckStatePersistence { - internal sealed partial class UpToDateCheckStatePersistence + /// + /// Compares projects and dimensions. + /// + private sealed class ConfiguredProjectComparer : IEqualityComparer<(string ProjectPath, IImmutableDictionary ConfigurationDimensions)> { - /// - /// Compares projects and dimensions. - /// - private sealed class ConfiguredProjectComparer : IEqualityComparer<(string ProjectPath, IImmutableDictionary ConfigurationDimensions)> + public static ConfiguredProjectComparer Instance { get; } = new(); + + public bool Equals( + (string ProjectPath, IImmutableDictionary ConfigurationDimensions) x, + (string ProjectPath, IImmutableDictionary ConfigurationDimensions) y) { - public static ConfiguredProjectComparer Instance { get; } = new(); + if (!StringComparers.Paths.Equals(x.ProjectPath, y.ProjectPath)) + return false; - public bool Equals( - (string ProjectPath, IImmutableDictionary ConfigurationDimensions) x, - (string ProjectPath, IImmutableDictionary ConfigurationDimensions) y) - { - if (!StringComparers.Paths.Equals(x.ProjectPath, y.ProjectPath)) - return false; + if (x.ConfigurationDimensions.Count != y.ConfigurationDimensions.Count) + return false; - if (x.ConfigurationDimensions.Count != y.ConfigurationDimensions.Count) + foreach ((string name, string xValue) in x.ConfigurationDimensions) + { + if (!y.ConfigurationDimensions.TryGetValue(name, out string? yValue) || + !StringComparers.ConfigurationDimensionValues.Equals(xValue, yValue)) return false; - - foreach ((string name, string xValue) in x.ConfigurationDimensions) - { - if (!y.ConfigurationDimensions.TryGetValue(name, out string? yValue) || - !StringComparers.ConfigurationDimensionValues.Equals(xValue, yValue)) - return false; - } - - return true; } - public int GetHashCode((string ProjectPath, IImmutableDictionary ConfigurationDimensions) obj) - { - unchecked - { - int hash = obj.ProjectPath.GetHashCode(); + return true; + } - foreach ((string name, string value) in obj.ConfigurationDimensions) - { - // XOR values so that order doesn't matter - hash ^= (name.GetHashCode() * 397) ^ value.GetHashCode(); - } + public int GetHashCode((string ProjectPath, IImmutableDictionary ConfigurationDimensions) obj) + { + unchecked + { + int hash = obj.ProjectPath.GetHashCode(); - return hash; + foreach ((string name, string value) in obj.ConfigurationDimensions) + { + // XOR values so that order doesn't matter + hash ^= (name.GetHashCode() * 397) ^ value.GetHashCode(); } + + return hash; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.cs index cb37237feb..7174767501 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UpToDate/UpToDateCheckStatePersistence.cs @@ -5,308 +5,307 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +/// +/// Implementation of for use in Visual Studio. +/// +/// +/// Stores the required data to disk in the .vs folder. +/// +[Export(typeof(IUpToDateCheckStatePersistence))] +[AppliesTo(BuildUpToDateCheck.AppliesToExpression)] +internal sealed partial class UpToDateCheckStatePersistence : OnceInitializedOnceDisposedUnderLockAsync, IUpToDateCheckStatePersistence, IVsSolutionEvents { - /// - /// Implementation of for use in Visual Studio. - /// - /// - /// Stores the required data to disk in the .vs folder. - /// - [Export(typeof(IUpToDateCheckStatePersistence))] - [AppliesTo(BuildUpToDateCheck.AppliesToExpression)] - internal sealed partial class UpToDateCheckStatePersistence : OnceInitializedOnceDisposedUnderLockAsync, IUpToDateCheckStatePersistence, IVsSolutionEvents - { - // The name of the file within the .vs folder that we use to store data for the up-to-date check. - // Note the suffix that indicates the file format's version. If a change is made to the format, - // this number must be bumped. - private const string ProjectItemCacheFileName = ".futdcache.v2"; + // The name of the file within the .vs folder that we use to store data for the up-to-date check. + // Note the suffix that indicates the file format's version. If a change is made to the format, + // this number must be bumped. + private const string ProjectItemCacheFileName = ".futdcache.v2"; - private Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)>? _dataByConfiguredProject; + private Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)>? _dataByConfiguredProject; - private readonly ISolutionService _solutionService; + private readonly ISolutionService _solutionService; - private bool _hasUnsavedChange; - private IAsyncDisposable? _solutionEventsSubscription; - private string? _cacheFilePath; - private JoinableTask? _cleanupTask; + private bool _hasUnsavedChange; + private IAsyncDisposable? _solutionEventsSubscription; + private string? _cacheFilePath; + private JoinableTask? _cleanupTask; - [ImportingConstructor] - public UpToDateCheckStatePersistence( - ISolutionService solutionService, - JoinableTaskContext joinableTaskContext) - : base(new JoinableTaskContextNode(joinableTaskContext)) - { - _solutionService = solutionService; - } + [ImportingConstructor] + public UpToDateCheckStatePersistence( + ISolutionService solutionService, + JoinableTaskContext joinableTaskContext) + : base(new JoinableTaskContextNode(joinableTaskContext)) + { + _solutionService = solutionService; + } - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _solutionEventsSubscription = await _solutionService.SubscribeAsync(this, cancellationToken); - } + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _solutionEventsSubscription = await _solutionService.SubscribeAsync(this, cancellationToken); + } - protected override async Task DisposeCoreUnderLockAsync(bool initialized) + protected override async Task DisposeCoreUnderLockAsync(bool initialized) + { + if (initialized) { - if (initialized) - { - Assumes.NotNull(_solutionEventsSubscription); + Assumes.NotNull(_solutionEventsSubscription); - await _solutionEventsSubscription.DisposeAsync(); - } + await _solutionEventsSubscription.DisposeAsync(); } + } - public async Task<(int ItemHash, DateTime? ItemsChangedAtUtc)?> RestoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken) - { - await InitializeAsync(cancellationToken); + public async Task<(int ItemHash, DateTime? ItemsChangedAtUtc)?> RestoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken) + { + await InitializeAsync(cancellationToken); - return await ExecuteUnderLockAsync<(int ItemHash, DateTime? ItemsChangedAtUtc)?>( - async token => - { - await EnsureDataInitializedAsync(token); + return await ExecuteUnderLockAsync<(int ItemHash, DateTime? ItemsChangedAtUtc)?>( + async token => + { + await EnsureDataInitializedAsync(token); - Assumes.NotNull(_dataByConfiguredProject); + Assumes.NotNull(_dataByConfiguredProject); - if (_dataByConfiguredProject.TryGetValue((projectPath, configurationDimensions), out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData)) - { - return (storedData.ItemHash, storedData.ItemsChangedAtUtc); - } + if (_dataByConfiguredProject.TryGetValue((projectPath, configurationDimensions), out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData)) + { + return (storedData.ItemHash, storedData.ItemsChangedAtUtc); + } - return null; - }, - cancellationToken); - } + return null; + }, + cancellationToken); + } - public Task StoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, int itemHash, DateTime? itemsChangedAtUtc, CancellationToken cancellationToken) - { - Requires.Argument(itemsChangedAtUtc != DateTime.MinValue, nameof(itemsChangedAtUtc), "Must not be DateTime.MinValue."); + public Task StoreItemStateAsync(string projectPath, IImmutableDictionary configurationDimensions, int itemHash, DateTime? itemsChangedAtUtc, CancellationToken cancellationToken) + { + Requires.Argument(itemsChangedAtUtc != DateTime.MinValue, nameof(itemsChangedAtUtc), "Must not be DateTime.MinValue."); - return ExecuteUnderLockAsync( - token => + return ExecuteUnderLockAsync( + token => + { + if (_dataByConfiguredProject is not null) { - if (_dataByConfiguredProject is not null) + (string ProjectPath, IImmutableDictionary ConfigurationDimensions) key = (ProjectPath: projectPath, ConfigurationDimensions: configurationDimensions); + + if (!_dataByConfiguredProject.TryGetValue(key, out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData) + || storedData.ItemHash != itemHash + || storedData.ItemsChangedAtUtc != itemsChangedAtUtc) { - (string ProjectPath, IImmutableDictionary ConfigurationDimensions) key = (ProjectPath: projectPath, ConfigurationDimensions: configurationDimensions); - - if (!_dataByConfiguredProject.TryGetValue(key, out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData) - || storedData.ItemHash != itemHash - || storedData.ItemsChangedAtUtc != itemsChangedAtUtc) - { - _dataByConfiguredProject[key] = (itemHash, itemsChangedAtUtc, storedData.LastSuccessfulBuildStartedAtUtc); - _hasUnsavedChange = true; - } + _dataByConfiguredProject[key] = (itemHash, itemsChangedAtUtc, storedData.LastSuccessfulBuildStartedAtUtc); + _hasUnsavedChange = true; } + } - return Task.CompletedTask; - }, - cancellationToken); - } + return Task.CompletedTask; + }, + cancellationToken); + } - public async Task RestoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken) - { - await InitializeAsync(cancellationToken); + public async Task RestoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, CancellationToken cancellationToken) + { + await InitializeAsync(cancellationToken); - return await ExecuteUnderLockAsync( - async token => - { - await EnsureDataInitializedAsync(token); + return await ExecuteUnderLockAsync( + async token => + { + await EnsureDataInitializedAsync(token); - Assumes.NotNull(_dataByConfiguredProject); + Assumes.NotNull(_dataByConfiguredProject); - if (_dataByConfiguredProject.TryGetValue((projectPath, configurationDimensions), out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData) - && storedData.LastSuccessfulBuildStartedAtUtc is not null and { Ticks: > 0 }) - { - return storedData.LastSuccessfulBuildStartedAtUtc; - } + if (_dataByConfiguredProject.TryGetValue((projectPath, configurationDimensions), out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData) + && storedData.LastSuccessfulBuildStartedAtUtc is not null and { Ticks: > 0 }) + { + return storedData.LastSuccessfulBuildStartedAtUtc; + } - return null; - }, - cancellationToken); - } + return null; + }, + cancellationToken); + } - public Task StoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, DateTime lastSuccessfulBuildStartedAtUtc, CancellationToken cancellationToken) - { - Requires.Argument(lastSuccessfulBuildStartedAtUtc != DateTime.MinValue, nameof(lastSuccessfulBuildStartedAtUtc), "Must not be DateTime.MinValue."); + public Task StoreLastSuccessfulBuildStateAsync(string projectPath, IImmutableDictionary configurationDimensions, DateTime lastSuccessfulBuildStartedAtUtc, CancellationToken cancellationToken) + { + Requires.Argument(lastSuccessfulBuildStartedAtUtc != DateTime.MinValue, nameof(lastSuccessfulBuildStartedAtUtc), "Must not be DateTime.MinValue."); - return ExecuteUnderLockAsync( - token => + return ExecuteUnderLockAsync( + token => + { + if (_dataByConfiguredProject is not null) { - if (_dataByConfiguredProject is not null) + (string ProjectPath, IImmutableDictionary ConfigurationDimensions) key = (ProjectPath: projectPath, ConfigurationDimensions: configurationDimensions); + + if (!_dataByConfiguredProject.TryGetValue(key, out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData) + || storedData.LastSuccessfulBuildStartedAtUtc != lastSuccessfulBuildStartedAtUtc) { - (string ProjectPath, IImmutableDictionary ConfigurationDimensions) key = (ProjectPath: projectPath, ConfigurationDimensions: configurationDimensions); - - if (!_dataByConfiguredProject.TryGetValue(key, out (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) storedData) - || storedData.LastSuccessfulBuildStartedAtUtc != lastSuccessfulBuildStartedAtUtc) - { - _dataByConfiguredProject[key] = (storedData.ItemHash, storedData.ItemsChangedAtUtc, lastSuccessfulBuildStartedAtUtc); - _hasUnsavedChange = true; - } + _dataByConfiguredProject[key] = (storedData.ItemHash, storedData.ItemsChangedAtUtc, lastSuccessfulBuildStartedAtUtc); + _hasUnsavedChange = true; } + } - return Task.CompletedTask; - }, - cancellationToken); - } + return Task.CompletedTask; + }, + cancellationToken); + } - private async Task EnsureDataInitializedAsync(CancellationToken cancellationToken) + private async Task EnsureDataInitializedAsync(CancellationToken cancellationToken) + { + if (_cacheFilePath is null || _dataByConfiguredProject is null) { - if (_cacheFilePath is null || _dataByConfiguredProject is null) - { - string filePath = await GetCacheFilePathAsync(cancellationToken); + string filePath = await GetCacheFilePathAsync(cancellationToken); - // Switch to a background thread before doing file I/O - await TaskScheduler.Default; + // Switch to a background thread before doing file I/O + await TaskScheduler.Default; - if (_cacheFilePath is null || _dataByConfiguredProject is null) - { - _cacheFilePath = filePath; - _dataByConfiguredProject = Deserialize(_cacheFilePath); - } + if (_cacheFilePath is null || _dataByConfiguredProject is null) + { + _cacheFilePath = filePath; + _dataByConfiguredProject = Deserialize(_cacheFilePath); } + } - return; + return; - async Task GetCacheFilePathAsync(CancellationToken cancellationToken) - { - await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); + async Task GetCacheFilePathAsync(CancellationToken cancellationToken) + { + await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); - var solutionWorkingFolder = _solutionService.Solution as IVsSolutionWorkingFolders; + var solutionWorkingFolder = _solutionService.Solution as IVsSolutionWorkingFolders; - Assumes.Present(solutionWorkingFolder); + Assumes.Present(solutionWorkingFolder); - solutionWorkingFolder.GetFolder( - (uint)__SolutionWorkingFolder.SlnWF_StatePersistence, - guidProject: Guid.Empty, - fVersionSpecific: true, - fEnsureCreated: true, - out _, // isTemporary - out string workingFolderPath); + solutionWorkingFolder.GetFolder( + (uint)__SolutionWorkingFolder.SlnWF_StatePersistence, + guidProject: Guid.Empty, + fVersionSpecific: true, + fEnsureCreated: true, + out _, // isTemporary + out string workingFolderPath); - return Path.Combine(workingFolderPath, ProjectItemCacheFileName); - } + return Path.Combine(workingFolderPath, ProjectItemCacheFileName); } + } + + #region Serialization + + private static void Serialize(string cacheFilePath, Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)> dataByConfiguredProject) + { + using var stream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.None); + using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); - #region Serialization + writer.Write(dataByConfiguredProject.Count); - private static void Serialize(string cacheFilePath, Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)> dataByConfiguredProject) + foreach (((string path, IImmutableDictionary dimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) data) in dataByConfiguredProject) { - using var stream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.None); - using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); + writer.Write(path); - writer.Write(dataByConfiguredProject.Count); + writer.Write(dimensions.Count); - foreach (((string path, IImmutableDictionary dimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc) data) in dataByConfiguredProject) + foreach ((string name, string value) in dimensions) { - writer.Write(path); + writer.Write(name); + writer.Write(value); + } - writer.Write(dimensions.Count); + writer.Write(data.ItemHash); + writer.Write(data.ItemsChangedAtUtc?.Ticks ?? 0L); + writer.Write(data.LastSuccessfulBuildStartedAtUtc?.Ticks ?? 0L); + } + } - foreach ((string name, string value) in dimensions) - { - writer.Write(name); - writer.Write(value); - } + private static Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)> Deserialize(string cacheFilePath) + { + var data = new Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)>(ConfiguredProjectComparer.Instance); - writer.Write(data.ItemHash); - writer.Write(data.ItemsChangedAtUtc?.Ticks ?? 0L); - writer.Write(data.LastSuccessfulBuildStartedAtUtc?.Ticks ?? 0L); - } + if (!File.Exists(cacheFilePath)) + { + return data; } - private static Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)> Deserialize(string cacheFilePath) + try { - var data = new Dictionary<(string ProjectPath, IImmutableDictionary ConfigurationDimensions), (int ItemHash, DateTime? ItemsChangedAtUtc, DateTime? LastSuccessfulBuildStartedAtUtc)>(ConfiguredProjectComparer.Instance); + using var stream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.None); + using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); - if (!File.Exists(cacheFilePath)) - { - return data; - } + int configuredProjectCount = reader.ReadInt32(); - try + while (configuredProjectCount-- != 0) { - using var stream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.None); - using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); + string path = reader.ReadString(); - int configuredProjectCount = reader.ReadInt32(); + int dimensionCount = reader.ReadInt32(); + var dimensions = ImmutableStringDictionary.EmptyOrdinal.ToBuilder(); - while (configuredProjectCount-- != 0) + while (dimensionCount-- != 0) { - string path = reader.ReadString(); - - int dimensionCount = reader.ReadInt32(); - var dimensions = ImmutableStringDictionary.EmptyOrdinal.ToBuilder(); - - while (dimensionCount-- != 0) - { - string name = reader.ReadString(); - string value = reader.ReadString(); - dimensions[name] = value; - } + string name = reader.ReadString(); + string value = reader.ReadString(); + dimensions[name] = value; + } - int hash = reader.ReadInt32(); - var itemsChangedAtUtc = new DateTime(reader.ReadInt64(), DateTimeKind.Utc); - var lastSuccessfulBuildStartedAtUtc = new DateTime(reader.ReadInt64(), DateTimeKind.Utc); + int hash = reader.ReadInt32(); + var itemsChangedAtUtc = new DateTime(reader.ReadInt64(), DateTimeKind.Utc); + var lastSuccessfulBuildStartedAtUtc = new DateTime(reader.ReadInt64(), DateTimeKind.Utc); - data[(path, dimensions.ToImmutable())] = (hash, itemsChangedAtUtc, lastSuccessfulBuildStartedAtUtc); - } - } - catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or NotSupportedException or ArgumentException) - { - // Return empty data in case of failure. Assume the whole file is corrupted. - return new(); + data[(path, dimensions.ToImmutable())] = (hash, itemsChangedAtUtc, lastSuccessfulBuildStartedAtUtc); } - - return data; } + catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or NotSupportedException or ArgumentException) + { + // Return empty data in case of failure. Assume the whole file is corrupted. + return new(); + } + + return data; + } - #endregion + #endregion - #region IVsSolutionEvents + #region IVsSolutionEvents - public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) => HResult.NotImplemented; - public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) => HResult.NotImplemented; - public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) => HResult.NotImplemented; - public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) => HResult.NotImplemented; - public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) => HResult.NotImplemented; - public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) => HResult.NotImplemented; - public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) => HResult.NotImplemented; - public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) => HResult.NotImplemented; - public int OnBeforeCloseSolution(object pUnkReserved) + public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) => HResult.NotImplemented; + public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) => HResult.NotImplemented; + public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) => HResult.NotImplemented; + public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) => HResult.NotImplemented; + public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) => HResult.NotImplemented; + public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) => HResult.NotImplemented; + public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) => HResult.NotImplemented; + public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) => HResult.NotImplemented; + public int OnBeforeCloseSolution(object pUnkReserved) + { + // Kick off clean up work now. We will join on it after solution close. + _cleanupTask = JoinableFactory.RunAsync(async () => { - // Kick off clean up work now. We will join on it after solution close. - _cleanupTask = JoinableFactory.RunAsync(async () => - { - await TaskScheduler.Default; + await TaskScheduler.Default; - await ExecuteUnderLockAsync( - _ => + await ExecuteUnderLockAsync( + _ => + { + if (_hasUnsavedChange && _cacheFilePath is not null && _dataByConfiguredProject is not null) { - if (_hasUnsavedChange && _cacheFilePath is not null && _dataByConfiguredProject is not null) - { - Serialize(_cacheFilePath, _dataByConfiguredProject); - } - - _cacheFilePath = null; - _dataByConfiguredProject = null; - _hasUnsavedChange = false; + Serialize(_cacheFilePath, _dataByConfiguredProject); + } - return Task.CompletedTask; - }); - }); + _cacheFilePath = null; + _dataByConfiguredProject = null; + _hasUnsavedChange = false; - return HResult.OK; - } + return Task.CompletedTask; + }); + }); - public int OnAfterCloseSolution(object pUnkReserved) - { - // Wait for any async clean up to complete. We need to ensure this occurs before we close - // the solution so that if we are immediately re-opening the solution (e.g. during branch - // switching where the .sln file changed) we will restore the persisted state correctly. - _cleanupTask?.Join(); - _cleanupTask = null; + return HResult.OK; + } - return HResult.OK; - } + public int OnAfterCloseSolution(object pUnkReserved) + { + // Wait for any async clean up to complete. We need to ensure this occurs before we close + // the solution so that if we are immediately re-opening the solution (e.g. during branch + // switching where the .sln file changed) we will restore the persisted state correctly. + _cleanupTask?.Join(); + _cleanupTask = null; - #endregion + return HResult.OK; } + + #endregion } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs index be14c47a09..0808ff9006 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UserNotificationServices.cs @@ -6,56 +6,55 @@ using Microsoft.VisualStudio.Shell.Interop; using static Microsoft.VisualStudio.VSConstants; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[Export(typeof(IUserNotificationServices))] +internal class UserNotificationServices : IUserNotificationServices { - [Export(typeof(IUserNotificationServices))] - internal class UserNotificationServices : IUserNotificationServices - { - private readonly SVsServiceProvider _serviceProvider; - private readonly IProjectThreadingService _threadingService; + private readonly SVsServiceProvider _serviceProvider; + private readonly IProjectThreadingService _threadingService; - [ImportingConstructor] - public UserNotificationServices(SVsServiceProvider serviceProvider, IProjectThreadingService threadingService) - { - _serviceProvider = serviceProvider; - _threadingService = threadingService; - } + [ImportingConstructor] + public UserNotificationServices(SVsServiceProvider serviceProvider, IProjectThreadingService threadingService) + { + _serviceProvider = serviceProvider; + _threadingService = threadingService; + } - public bool Confirm(string message) - { - MessageBoxResult result = ShowMessageBox(message, OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_YESNO, MessageBoxResult.IDYES); + public bool Confirm(string message) + { + MessageBoxResult result = ShowMessageBox(message, OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_YESNO, MessageBoxResult.IDYES); - return result == MessageBoxResult.IDYES; - } + return result == MessageBoxResult.IDYES; + } - public void ShowWarning(string warning) - { - ShowMessageBox(warning, OLEMSGICON.OLEMSGICON_WARNING, OLEMSGBUTTON.OLEMSGBUTTON_OK); - } + public void ShowWarning(string warning) + { + ShowMessageBox(warning, OLEMSGICON.OLEMSGICON_WARNING, OLEMSGBUTTON.OLEMSGBUTTON_OK); + } - public void ShowError(string error) - { - ShowMessageBox(error, OLEMSGICON.OLEMSGICON_CRITICAL, OLEMSGBUTTON.OLEMSGBUTTON_OK); - } + public void ShowError(string error) + { + ShowMessageBox(error, OLEMSGICON.OLEMSGICON_CRITICAL, OLEMSGBUTTON.OLEMSGBUTTON_OK); + } - private MessageBoxResult ShowMessageBox(string message, OLEMSGICON icon, OLEMSGBUTTON button, MessageBoxResult defaultResult = MessageBoxResult.IDOK) - { - _threadingService.VerifyOnUIThread(); + private MessageBoxResult ShowMessageBox(string message, OLEMSGICON icon, OLEMSGBUTTON button, MessageBoxResult defaultResult = MessageBoxResult.IDOK) + { + _threadingService.VerifyOnUIThread(); - if (VsShellUtilities.IsInAutomationFunction(_serviceProvider)) - return defaultResult; + if (VsShellUtilities.IsInAutomationFunction(_serviceProvider)) + return defaultResult; - return (MessageBoxResult)VsShellUtilities.ShowMessageBox(_serviceProvider, message, null, icon, button, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); - } + return (MessageBoxResult)VsShellUtilities.ShowMessageBox(_serviceProvider, message, null, icon, button, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + } - public bool Confirm(string message, out bool disablePromptMessage) - { - string dontShowAgainMessage = string.Format(CultureInfo.CurrentCulture, VSResources.DontShowAgain); + public bool Confirm(string message, out bool disablePromptMessage) + { + string dontShowAgainMessage = string.Format(CultureInfo.CurrentCulture, VSResources.DontShowAgain); - var userSelection = MessageDialog.Show("Microsoft Visual Studio", message, MessageDialogCommandSet.YesNo, dontShowAgainMessage, - out disablePromptMessage); + var userSelection = MessageDialog.Show("Microsoft Visual Studio", message, MessageDialogCommandSet.YesNo, dontShowAgainMessage, + out disablePromptMessage); - return userSelection == MessageDialogCommand.Yes; - } + return userSelection == MessageDialogCommand.Yes; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/EnumerableExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/EnumerableExtensions.cs index 2ff4dfc474..7a08eec39c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/EnumerableExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/EnumerableExtensions.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +internal static class EnumerableExtensions { - internal static class EnumerableExtensions + /// + /// Given an , returns a new that yields tuples with + /// the items of the original plus their index. + /// + /// + /// + /// + public static IEnumerable<(int index, T item)> WithIndices(this IEnumerable enumerable) { - /// - /// Given an , returns a new that yields tuples with - /// the items of the original plus their index. - /// - /// - /// - /// - public static IEnumerable<(int index, T item)> WithIndices(this IEnumerable enumerable) + int index = 0; + foreach (var item in enumerable) { - int index = 0; - foreach (var item in enumerable) - { - yield return (index++, item); - } + yield return (index++, item); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/FocusAttacher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/FocusAttacher.cs index 8799544780..10f8fcbe08 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/FocusAttacher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/FocusAttacher.cs @@ -3,30 +3,29 @@ using System.Windows; using System.Windows.Controls; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +public class FocusAttacher { - public class FocusAttacher + public static readonly DependencyProperty FocusProperty = DependencyProperty.RegisterAttached("Focus", typeof(bool), typeof(FocusAttacher), new PropertyMetadata(false, FocusChanged)); + public static bool GetFocus(DependencyObject d) { - public static readonly DependencyProperty FocusProperty = DependencyProperty.RegisterAttached("Focus", typeof(bool), typeof(FocusAttacher), new PropertyMetadata(false, FocusChanged)); - public static bool GetFocus(DependencyObject d) - { - return (bool)d.GetValue(FocusProperty); - } + return (bool)d.GetValue(FocusProperty); + } - public static void SetFocus(DependencyObject d, bool value) - { - d.SetValue(FocusProperty, value); - } + public static void SetFocus(DependencyObject d, bool value) + { + d.SetValue(FocusProperty, value); + } - private static void FocusChanged(object sender, DependencyPropertyChangedEventArgs e) + private static void FocusChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if ((bool)e.NewValue) { - if ((bool)e.NewValue) + ((UIElement)sender).Focus(); + if (sender is TextBox tb) { - ((UIElement)sender).Focus(); - if (sender is TextBox tb) - { - tb.SelectAll(); - } + tb.SelectAll(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/KnownEventArgs.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/KnownEventArgs.cs index e03779d860..7a38797673 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/KnownEventArgs.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/KnownEventArgs.cs @@ -4,14 +4,13 @@ using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +internal static class KnownEventArgs { - internal static class KnownEventArgs - { - public static PropertyChangedEventArgs TextPropertyChanged { get; } = new PropertyChangedEventArgs(nameof(ITreeDisplayItem.Text)); + public static PropertyChangedEventArgs TextPropertyChanged { get; } = new PropertyChangedEventArgs(nameof(ITreeDisplayItem.Text)); - public static PropertyChangedEventArgs IsUpdatingItemsPropertyChanged { get; } = new PropertyChangedEventArgs(nameof(IAsyncAttachedCollectionSource.IsUpdatingHasItems)); + public static PropertyChangedEventArgs IsUpdatingItemsPropertyChanged { get; } = new PropertyChangedEventArgs(nameof(IAsyncAttachedCollectionSource.IsUpdatingHasItems)); - public static PropertyChangedEventArgs HasItemsPropertyChanged { get; } = new PropertyChangedEventArgs(nameof(IAttachedCollectionSource.HasItems)); - } + public static PropertyChangedEventArgs HasItemsPropertyChanged { get; } = new PropertyChangedEventArgs(nameof(IAttachedCollectionSource.HasItems)); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/UIThreadHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/UIThreadHelper.cs index 9824d5b548..0aa3c39ddd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/UIThreadHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/UIThreadHelper.cs @@ -3,37 +3,36 @@ using System.Runtime.CompilerServices; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +/// +/// Static class containing helper utilities for working with UI thread. +/// +internal static class UIThreadHelper { /// - /// Static class containing helper utilities for working with UI thread. + /// Helper utility to ensure that we are on UI thread. Needs to be called + /// in every method/property with UI thread affinity (to avoid hangs + /// which are hard to repro and investigate). /// - internal static class UIThreadHelper + public static void VerifyOnUIThread([CallerMemberName] string memberName = "") { - /// - /// Helper utility to ensure that we are on UI thread. Needs to be called - /// in every method/property with UI thread affinity (to avoid hangs - /// which are hard to repro and investigate). - /// - public static void VerifyOnUIThread([CallerMemberName] string memberName = "") - { #if DEBUG - try - { + try + { #pragma warning disable RS0030 // Do not used banned APIs - ThreadHelper.ThrowIfNotOnUIThread(memberName); + ThreadHelper.ThrowIfNotOnUIThread(memberName); #pragma warning restore RS0030 // Do not used banned APIs - } - catch - { - System.Diagnostics.Debug.Fail("Call made on the Non-UI thread by " + memberName); - throw; - } + } + catch + { + System.Diagnostics.Debug.Fail("Call made on the Non-UI thread by " + memberName); + throw; + } #else #pragma warning disable RS0030 // Do not used banned APIs - ThreadHelper.ThrowIfNotOnUIThread(memberName); + ThreadHelper.ThrowIfNotOnUIThread(memberName); #pragma warning restore RS0030 // Do not used banned APIs #endif - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/WpfHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/WpfHelper.cs index 5c4b3d6334..1cc5306204 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/WpfHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Utilities/WpfHelper.cs @@ -4,66 +4,65 @@ using System.Windows.Controls.Primitives; using System.Windows.Media; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +internal static class WpfHelper { - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - internal static class WpfHelper + public static DataGridCell? GetCell(DataGrid dataGrid, int row, int column) { - public static DataGridCell? GetCell(DataGrid dataGrid, int row, int column) + DataGridRow rowContainer = GetRow(dataGrid, row); + + if (rowContainer is not null) { - DataGridRow rowContainer = GetRow(dataGrid, row); + DataGridCellsPresenter? presenter = GetVisualChild(rowContainer); - if (rowContainer is not null) + if (presenter is not null) { - DataGridCellsPresenter? presenter = GetVisualChild(rowContainer); - - if (presenter is not null) + // try to get the cell but it may possibly be virtualized + var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); + if (cell is null) { - // try to get the cell but it may possibly be virtualized - var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); - if (cell is null) - { - // now try to bring into view and retrieve the cell - dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]); - cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); - } - - return cell; + // now try to bring into view and retrieve the cell + dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]); + cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); } + + return cell; } - return null; } + return null; + } - public static DataGridRow GetRow(DataGrid dataGrid, int index) + public static DataGridRow GetRow(DataGrid dataGrid, int index) + { + var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index); + if (row is null) { - var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index); - if (row is null) - { - // may be virtualized, bring into view and try again - dataGrid.ScrollIntoView(dataGrid.Items[index]); - row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index); - } - return row; + // may be virtualized, bring into view and try again + dataGrid.ScrollIntoView(dataGrid.Items[index]); + row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index); } + return row; + } - public static T? GetVisualChild(Visual parent) where T : Visual + public static T? GetVisualChild(Visual parent) where T : Visual + { + T? child = null; + int numVisuals = VisualTreeHelper.GetChildrenCount(parent); + for (int i = 0; i < numVisuals; i++) { - T? child = null; - int numVisuals = VisualTreeHelper.GetChildrenCount(parent); - for (int i = 0; i < numVisuals; i++) + var v = (Visual)VisualTreeHelper.GetChild(parent, i); + child = v as T; + if (child is null) { - var v = (Visual)VisualTreeHelper.GetChild(parent, i); - child = v as T; - if (child is null) - { - child = GetVisualChild(v); - } - if (child is not null) - { - break; - } + child = GetVisualChild(v); + } + if (child is not null) + { + break; } - return child; } + return child; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProvider.cs index cb64544396..0b9123ba9d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProvider.cs @@ -3,32 +3,31 @@ using Microsoft.Build.Construction; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic; + +/// +/// Checks a legacy Visual Basic project for compatibility with the new project system. +/// +[SupportedProjectTypeGuid(ProjectType.LegacyVisualBasic)] +[Export(ExportContractNames.Extensions.SupportedProjectTypeGuid)] +[Export(typeof(IFlavoredProjectCompatibilityProvider))] +[ProjectTypeGuidFilter(ProjectType.LegacyVisualBasic)] +[AppliesTo(ProjectCapabilities.AlwaysApplicable)] +internal class VisualBasicProjectCompatibilityProvider : IFlavoredProjectCompatibilityProvider { - /// - /// Checks a legacy Visual Basic project for compatibility with the new project system. - /// - [SupportedProjectTypeGuid(ProjectType.LegacyVisualBasic)] - [Export(ExportContractNames.Extensions.SupportedProjectTypeGuid)] - [Export(typeof(IFlavoredProjectCompatibilityProvider))] - [ProjectTypeGuidFilter(ProjectType.LegacyVisualBasic)] - [AppliesTo(ProjectCapabilities.AlwaysApplicable)] - internal class VisualBasicProjectCompatibilityProvider : IFlavoredProjectCompatibilityProvider + [ImportingConstructor] + public VisualBasicProjectCompatibilityProvider() { - [ImportingConstructor] - public VisualBasicProjectCompatibilityProvider() - { - } + } - public Task IsProjectCompatibleAsync(ProjectRootElement project) - { - return TaskResult.True; - } + public Task IsProjectCompatibleAsync(ProjectRootElement project) + { + return TaskResult.True; + } - public Task IsProjectNeedBeUpgradedAsync(ProjectRootElement project) - { - // We need to fill this out: https://github.com/dotnet/roslyn/issues/11285 - return TaskResult.False; - } + public Task IsProjectNeedBeUpgradedAsync(ProjectRootElement project) + { + // We need to fill this out: https://github.com/dotnet/roslyn/issues/11285 + return TaskResult.False; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProvider.cs index 366f7ebcfd..46edb7ba25 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProvider.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic; + +/// +/// Provides the Visual Basic implementation of . +/// +[Export(typeof(IItemTypeGuidProvider))] +[AppliesTo(ProjectCapabilities.VB)] +internal class VisualBasicProjectTypeGuidProvider : IItemTypeGuidProvider { - /// - /// Provides the Visual Basic implementation of . - /// - [Export(typeof(IItemTypeGuidProvider))] - [AppliesTo(ProjectCapabilities.VB)] - internal class VisualBasicProjectTypeGuidProvider : IItemTypeGuidProvider + [ImportingConstructor] + public VisualBasicProjectTypeGuidProvider() { - [ImportingConstructor] - public VisualBasicProjectTypeGuidProvider() - { - } + } - public Guid ProjectTypeGuid - { - get { return ProjectType.LegacyVisualBasicGuid; } - } + public Guid ProjectTypeGuid + { + get { return ProjectType.LegacyVisualBasicGuid; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsOnlineServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsOnlineServices.cs index 6fd8c0185f..aa03cc19c7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsOnlineServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsOnlineServices.cs @@ -2,11 +2,10 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[Export(typeof(IVsOnlineServices))] +internal class VsOnlineServices : IVsOnlineServices { - [Export(typeof(IVsOnlineServices))] - internal class VsOnlineServices : IVsOnlineServices - { - public bool ConnectedToVSOnline => KnownUIContexts.CloudEnvironmentConnectedContext.IsActive; - } + public bool ConnectedToVSOnline => KnownUIContexts.CloudEnvironmentConnectedContext.IsActive; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSafeProjectGuidService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSafeProjectGuidService.cs index f187d5b426..9715b28505 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSafeProjectGuidService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSafeProjectGuidService.cs @@ -2,30 +2,29 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// An implementation of that waits until the project +/// has been loaded into the host environment before returning the project GUID. +/// +[Export(typeof(ISafeProjectGuidService))] +internal class VsSafeProjectGuidService : ISafeProjectGuidService { - /// - /// An implementation of that waits until the project - /// has been loaded into the host environment before returning the project GUID. - /// - [Export(typeof(ISafeProjectGuidService))] - internal class VsSafeProjectGuidService : ISafeProjectGuidService - { - private readonly UnconfiguredProject _project; - private readonly IUnconfiguredProjectTasksService _tasksService; + private readonly UnconfiguredProject _project; + private readonly IUnconfiguredProjectTasksService _tasksService; - [ImportingConstructor] - public VsSafeProjectGuidService(UnconfiguredProject project, IUnconfiguredProjectTasksService tasksService) - { - _project = project; - _tasksService = tasksService; - } + [ImportingConstructor] + public VsSafeProjectGuidService(UnconfiguredProject project, IUnconfiguredProjectTasksService tasksService) + { + _project = project; + _tasksService = tasksService; + } - public async Task GetProjectGuidAsync(CancellationToken cancellationToken = default) - { - await _tasksService.PrioritizedProjectLoadedInHost.WithCancellation(cancellationToken); + public async Task GetProjectGuidAsync(CancellationToken cancellationToken = default) + { + await _tasksService.PrioritizedProjectLoadedInHost.WithCancellation(cancellationToken); - return await _project.GetProjectGuidAsync(); - } + return await _project.GetProjectGuidAsync(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSolutionEventListener.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSolutionEventListener.cs index adc8fd6eaa..450efbddfe 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSolutionEventListener.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsSolutionEventListener.cs @@ -4,297 +4,296 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Responsible for listening to project and solution loaded events and firing +/// , +/// and +/// . +/// +[Export(typeof(ILoadedInHostListener))] +[Export(typeof(ISolutionService))] +internal class VsSolutionEventListener : OnceInitializedOnceDisposedAsync, IVsSolutionEvents, IVsSolutionLoadEvents, IVsPrioritizedSolutionEvents, ILoadedInHostListener, ISolutionService { - /// - /// Responsible for listening to project and solution loaded events and firing - /// , - /// and - /// . - /// - [Export(typeof(ILoadedInHostListener))] - [Export(typeof(ISolutionService))] - internal class VsSolutionEventListener : OnceInitializedOnceDisposedAsync, IVsSolutionEvents, IVsSolutionLoadEvents, IVsPrioritizedSolutionEvents, ILoadedInHostListener, ISolutionService - { - private readonly IVsUIService _solution; - - private TaskCompletionSource _loadedInHost = new(TaskCreationOptions.RunContinuationsAsynchronously); - private IAsyncDisposable? _solutionEventsSubscription; - - [ImportingConstructor] - public VsSolutionEventListener(IVsUIService solution, JoinableTaskContext joinableTaskContext) - : base(new JoinableTaskContextNode(joinableTaskContext)) - { - _solution = solution; - } + private readonly IVsUIService _solution; - public Task LoadedInHost - { - get { return _loadedInHost.Task; } - } + private TaskCompletionSource _loadedInHost = new(TaskCreationOptions.RunContinuationsAsynchronously); + private IAsyncDisposable? _solutionEventsSubscription; + + [ImportingConstructor] + public VsSolutionEventListener(IVsUIService solution, JoinableTaskContext joinableTaskContext) + : base(new JoinableTaskContextNode(joinableTaskContext)) + { + _solution = solution; + } + + public Task LoadedInHost + { + get { return _loadedInHost.Task; } + } - public IVsSolution Solution + public IVsSolution Solution + { + get { - get - { - JoinableFactory.Context.VerifyIsOnMainThread(); - return _solution.Value; - } + JoinableFactory.Context.VerifyIsOnMainThread(); + return _solution.Value; } + } - public async Task SubscribeAsync(IVsSolutionEvents solutionEvents, CancellationToken cancellationToken) - { - await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); + public async Task SubscribeAsync(IVsSolutionEvents solutionEvents, CancellationToken cancellationToken) + { + await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); - IVsSolution solution = _solution.Value; + IVsSolution solution = _solution.Value; - Verify.HResult(solution.AdviseSolutionEvents(solutionEvents, out uint cookie)); + Verify.HResult(solution.AdviseSolutionEvents(solutionEvents, out uint cookie)); - return new Subscription(solution, JoinableFactory, cookie); - } + return new Subscription(solution, JoinableFactory, cookie); + } - private sealed class Subscription : IAsyncDisposable + private sealed class Subscription : IAsyncDisposable + { + private readonly IVsSolution _solution; + private readonly JoinableTaskFactory _joinableTaskFactory; + private int _cookie; + + public Subscription(IVsSolution solution, JoinableTaskFactory joinableTaskFactory, uint cookie) { - private readonly IVsSolution _solution; - private readonly JoinableTaskFactory _joinableTaskFactory; - private int _cookie; + _solution = solution; + _joinableTaskFactory = joinableTaskFactory; + _cookie = unchecked((int)cookie); + } - public Subscription(IVsSolution solution, JoinableTaskFactory joinableTaskFactory, uint cookie) - { - _solution = solution; - _joinableTaskFactory = joinableTaskFactory; - _cookie = unchecked((int)cookie); - } + public async ValueTask DisposeAsync() + { + uint cookie = unchecked((uint)Interlocked.Exchange(ref _cookie, (int)VSConstants.VSCOOKIE_NIL)); - public async ValueTask DisposeAsync() + if (cookie != VSConstants.VSCOOKIE_NIL) { - uint cookie = unchecked((uint)Interlocked.Exchange(ref _cookie, (int)VSConstants.VSCOOKIE_NIL)); - - if (cookie != VSConstants.VSCOOKIE_NIL) - { - await _joinableTaskFactory.SwitchToMainThreadAsync(); + await _joinableTaskFactory.SwitchToMainThreadAsync(); - Verify.HResult(_solution.UnadviseSolutionEvents(cookie)); - } + Verify.HResult(_solution.UnadviseSolutionEvents(cookie)); } } + } - public Task StartListeningAsync() - { - return InitializeAsync(CancellationToken.None); - } + public Task StartListeningAsync() + { + return InitializeAsync(CancellationToken.None); + } - protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) - { - await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); + protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) + { + await JoinableFactory.SwitchToMainThreadAsync(cancellationToken); - _solutionEventsSubscription = await SubscribeAsync(this, cancellationToken); + _solutionEventsSubscription = await SubscribeAsync(this, cancellationToken); - // In the situation where the solution has already been loaded by the time we're - // initialized, we need to make sure we set LoadedInHost as we will have missed the - // event. This can occur when the first CPS project is loaded due to reload of - // an unloaded project or the first CPS project is loaded in the Add New/Existing Project - // case. - Verify.HResult(_solution.Value.GetProperty((int)__VSPROPID4.VSPROPID_IsSolutionFullyLoaded, out object isFullyLoaded)); + // In the situation where the solution has already been loaded by the time we're + // initialized, we need to make sure we set LoadedInHost as we will have missed the + // event. This can occur when the first CPS project is loaded due to reload of + // an unloaded project or the first CPS project is loaded in the Add New/Existing Project + // case. + Verify.HResult(_solution.Value.GetProperty((int)__VSPROPID4.VSPROPID_IsSolutionFullyLoaded, out object isFullyLoaded)); - if ((bool)isFullyLoaded) - { - _loadedInHost.TrySetResult(); - } + if ((bool)isFullyLoaded) + { + _loadedInHost.TrySetResult(); } + } - protected override async Task DisposeCoreAsync(bool initialized) + protected override async Task DisposeCoreAsync(bool initialized) + { + if (initialized) { - if (initialized) - { - Assumes.NotNull(_solutionEventsSubscription); + Assumes.NotNull(_solutionEventsSubscription); - await JoinableFactory.SwitchToMainThreadAsync(); + await JoinableFactory.SwitchToMainThreadAsync(); - await _solutionEventsSubscription.DisposeAsync(); + await _solutionEventsSubscription.DisposeAsync(); - _loadedInHost.TrySetCanceled(); - } + _loadedInHost.TrySetCanceled(); } + } - public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) - { - UnconfiguredProjectTasksService? tasksService = GetUnconfiguredProjectTasksServiceIfApplicable(pHierarchy); - tasksService?.OnProjectLoadedInHost(); + public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) + { + UnconfiguredProjectTasksService? tasksService = GetUnconfiguredProjectTasksServiceIfApplicable(pHierarchy); + tasksService?.OnProjectLoadedInHost(); - return HResult.OK; - } + return HResult.OK; + } - public int PrioritizedOnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) - { - UnconfiguredProjectTasksService? tasksService = GetUnconfiguredProjectTasksServiceIfApplicable(pHierarchy); - tasksService?.OnPrioritizedProjectLoadedInHost(); + public int PrioritizedOnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) + { + UnconfiguredProjectTasksService? tasksService = GetUnconfiguredProjectTasksServiceIfApplicable(pHierarchy); + tasksService?.OnPrioritizedProjectLoadedInHost(); - return HResult.OK; - } + return HResult.OK; + } - public int OnAfterBackgroundSolutionLoadComplete() - { - _loadedInHost.TrySetResult(); - return HResult.OK; - } + public int OnAfterBackgroundSolutionLoadComplete() + { + _loadedInHost.TrySetResult(); + return HResult.OK; + } - public int OnAfterCloseSolution(object pUnkReserved) - { - _loadedInHost = new TaskCompletionSource(); + public int OnAfterCloseSolution(object pUnkReserved) + { + _loadedInHost = new TaskCompletionSource(); - return HResult.OK; - } + return HResult.OK; + } - private static UnconfiguredProjectTasksService? GetUnconfiguredProjectTasksServiceIfApplicable(IVsHierarchy hierarchy) + private static UnconfiguredProjectTasksService? GetUnconfiguredProjectTasksServiceIfApplicable(IVsHierarchy hierarchy) + { + if (hierarchy is IVsBrowseObjectContext context) { - if (hierarchy is IVsBrowseObjectContext context) + // Only want to run in projects where this is applicable + Lazy export = context.UnconfiguredProject.Services.ExportProvider.GetExport(); + if (export.AppliesTo(context.UnconfiguredProject.Capabilities)) { - // Only want to run in projects where this is applicable - Lazy export = context.UnconfiguredProject.Services.ExportProvider.GetExport(); - if (export.AppliesTo(context.UnconfiguredProject.Capabilities)) - { - return export.Value; - } + return export.Value; } - - return null; } - public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) - { - return HResult.NotImplemented; - } + return null; + } - public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) - { - return HResult.NotImplemented; - } + public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) + { + return HResult.NotImplemented; + } - public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) - { - return HResult.NotImplemented; - } + public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) + { + return HResult.NotImplemented; + } - public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) - { - return HResult.NotImplemented; - } + public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) + { + return HResult.NotImplemented; + } - public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) - { - return HResult.NotImplemented; - } + public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) + { + return HResult.NotImplemented; + } - public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) - { - return HResult.NotImplemented; - } + public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) + { + return HResult.NotImplemented; + } - public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) - { - return HResult.NotImplemented; - } + public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) + { + return HResult.NotImplemented; + } - public int OnBeforeCloseSolution(object pUnkReserved) - { - return HResult.NotImplemented; - } + public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) + { + return HResult.NotImplemented; + } - public int PrioritizedOnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) - { - return HResult.NotImplemented; - } + public int OnBeforeCloseSolution(object pUnkReserved) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) + { + return HResult.NotImplemented; + } - public int PrioritizedOnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterOpenSolution(object pUnkReserved, int fNewSolution) - { - return HResult.NotImplemented; - } + public int PrioritizedOnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnBeforeCloseSolution(object pUnkReserved) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterOpenSolution(object pUnkReserved, int fNewSolution) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterCloseSolution(object pUnkReserved) - { - return HResult.NotImplemented; - } + public int PrioritizedOnBeforeCloseSolution(object pUnkReserved) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterMergeSolution(object pUnkReserved) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterCloseSolution(object pUnkReserved) + { + return HResult.NotImplemented; + } - public int PrioritizedOnBeforeOpeningChildren(IVsHierarchy pHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterMergeSolution(object pUnkReserved) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterOpeningChildren(IVsHierarchy pHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnBeforeOpeningChildren(IVsHierarchy pHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnBeforeClosingChildren(IVsHierarchy pHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterOpeningChildren(IVsHierarchy pHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterClosingChildren(IVsHierarchy pHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnBeforeClosingChildren(IVsHierarchy pHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterRenameProject(IVsHierarchy pHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterClosingChildren(IVsHierarchy pHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterChangeProjectParent(IVsHierarchy pHierarchy) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterRenameProject(IVsHierarchy pHierarchy) + { + return HResult.NotImplemented; + } - public int PrioritizedOnAfterAsynchOpenProject(IVsHierarchy pHierarchy, int fAdded) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterChangeProjectParent(IVsHierarchy pHierarchy) + { + return HResult.NotImplemented; + } - public int OnBeforeOpenSolution(string pszSolutionFilename) - { - return HResult.NotImplemented; - } + public int PrioritizedOnAfterAsynchOpenProject(IVsHierarchy pHierarchy, int fAdded) + { + return HResult.NotImplemented; + } - public int OnBeforeBackgroundSolutionLoadBegins() - { - return HResult.NotImplemented; - } + public int OnBeforeOpenSolution(string pszSolutionFilename) + { + return HResult.NotImplemented; + } - public int OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle) - { - pfShouldDelayLoadToNextIdle = false; - return HResult.NotImplemented; - } + public int OnBeforeBackgroundSolutionLoadBegins() + { + return HResult.NotImplemented; + } - public int OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) - { - return HResult.NotImplemented; - } + public int OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle) + { + pfShouldDelayLoadToNextIdle = false; + return HResult.NotImplemented; + } - public int OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) - { - return HResult.NotImplemented; - } + public int OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) + { + return HResult.NotImplemented; + } + + public int OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) + { + return HResult.NotImplemented; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`1.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`1.cs index c4e310c1ae..a0200e2f59 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`1.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`1.cs @@ -5,45 +5,44 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides an implementation of that calls into Visual Studio's . +/// +[Export(typeof(IVsUIService<>))] +internal class VsUIService : IVsUIService + where T : class? { - /// - /// Provides an implementation of that calls into Visual Studio's . - /// - [Export(typeof(IVsUIService<>))] - internal class VsUIService : IVsUIService - where T : class? - { - private readonly Lazy _value; - private readonly JoinableTaskContext _joinableTaskContext; + private readonly Lazy _value; + private readonly JoinableTaskContext _joinableTaskContext; - [ImportingConstructor] - public VsUIService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, JoinableTaskContext joinableTaskContext) - { - Requires.NotNull(serviceProvider); - Requires.NotNull(joinableTaskContext); + [ImportingConstructor] + public VsUIService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, JoinableTaskContext joinableTaskContext) + { + Requires.NotNull(serviceProvider); + Requires.NotNull(joinableTaskContext); - _value = new Lazy(() => (T)serviceProvider.GetService(ServiceType)); - _joinableTaskContext = joinableTaskContext; - } + _value = new Lazy(() => (T)serviceProvider.GetService(ServiceType)); + _joinableTaskContext = joinableTaskContext; + } - public T Value + public T Value + { + get { - get - { - // We always verify that we're on the UI thread regardless - // of whether we've already retrieved the service to always - // enforce this. - _joinableTaskContext.VerifyIsOnMainThread(); + // We always verify that we're on the UI thread regardless + // of whether we've already retrieved the service to always + // enforce this. + _joinableTaskContext.VerifyIsOnMainThread(); - return _value.Value; - } + return _value.Value; } + } - protected virtual Type ServiceType - { - get { return typeof(T); } - } + protected virtual Type ServiceType + { + get { return typeof(T); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs index c3ec248633..838f976a2f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/VsUIService`2.cs @@ -5,25 +5,24 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides an implementation of that calls into Visual Studio's . +/// +[Export(typeof(IVsUIService<,>))] +internal class VsUIService : VsUIService, IVsUIService + where TService : class + where TInterface : class? { - /// - /// Provides an implementation of that calls into Visual Studio's . - /// - [Export(typeof(IVsUIService<,>))] - internal class VsUIService : VsUIService, IVsUIService - where TService : class - where TInterface : class? + [ImportingConstructor] + public VsUIService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, JoinableTaskContext joinableTaskContext) + : base(serviceProvider, joinableTaskContext) { - [ImportingConstructor] - public VsUIService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, JoinableTaskContext joinableTaskContext) - : base(serviceProvider, joinableTaskContext) - { - } + } - protected override Type ServiceType - { - get { return typeof(TService); } - } + protected override Type ServiceType + { + get { return typeof(TService); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs index 2752f1dc48..30e1c369e9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitContext.cs @@ -4,124 +4,123 @@ using Microsoft.VisualStudio.ProjectSystem.Waiting; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting +namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting; + +internal sealed class VisualStudioWaitContext : IWaitContext { - internal sealed class VisualStudioWaitContext : IWaitContext - { - private const int DelayToShowDialogSecs = 2; + private const int DelayToShowDialogSecs = 2; - private readonly string _title; - private readonly CancellationTokenSource? _cancellationTokenSource; - private readonly IVsThreadedWaitDialog3 _dialog; + private readonly string _title; + private readonly CancellationTokenSource? _cancellationTokenSource; + private readonly IVsThreadedWaitDialog3 _dialog; + + private string _message; + private string? _progressText; + private int _currentStep; + private int _totalSteps; - private string _message; - private string? _progressText; - private int _currentStep; - private int _totalSteps; + public VisualStudioWaitContext(IVsThreadedWaitDialogFactory waitDialogFactory, string title, string message, bool allowCancel, int totalSteps = 0) + { + _title = title; + _message = message; + _totalSteps = totalSteps; - public VisualStudioWaitContext(IVsThreadedWaitDialogFactory waitDialogFactory, string title, string message, bool allowCancel, int totalSteps = 0) + if (allowCancel) { - _title = title; - _message = message; - _totalSteps = totalSteps; + _cancellationTokenSource = new CancellationTokenSource(); + } + + _dialog = CreateDialog(waitDialogFactory); + } - if (allowCancel) - { - _cancellationTokenSource = new CancellationTokenSource(); - } + private IVsThreadedWaitDialog3 CreateDialog(IVsThreadedWaitDialogFactory dialogFactory) + { + Marshal.ThrowExceptionForHR(dialogFactory.CreateInstance(out IVsThreadedWaitDialog2 dialog2)); + + Assumes.NotNull(dialog2); + + var dialog3 = (IVsThreadedWaitDialog3)dialog2; + var callback = new Callback(_cancellationTokenSource); + + dialog3.StartWaitDialogWithCallback( + szWaitCaption: _title, + szWaitMessage: _message, + szProgressText: null, + varStatusBmpAnim: null, + szStatusBarText: null, + fIsCancelable: _cancellationTokenSource is not null, + iDelayToShowDialog: DelayToShowDialogSecs, + fShowProgress: _totalSteps != 0, + iTotalSteps: _totalSteps, + iCurrentStep: 0, + pCallback: callback); + + return dialog3; + } - _dialog = CreateDialog(waitDialogFactory); - } + private class Callback : IVsThreadedWaitDialogCallback + { + private readonly CancellationTokenSource? _cancellationTokenSource; - private IVsThreadedWaitDialog3 CreateDialog(IVsThreadedWaitDialogFactory dialogFactory) + public Callback(CancellationTokenSource? cancellationTokenSource) { - Marshal.ThrowExceptionForHR(dialogFactory.CreateInstance(out IVsThreadedWaitDialog2 dialog2)); + _cancellationTokenSource = cancellationTokenSource; + } - Assumes.NotNull(dialog2); + public void OnCanceled() + { + _cancellationTokenSource?.Cancel(); + } + } - var dialog3 = (IVsThreadedWaitDialog3)dialog2; - var callback = new Callback(_cancellationTokenSource); + public CancellationToken CancellationToken => _cancellationTokenSource?.Token ?? CancellationToken.None; - dialog3.StartWaitDialogWithCallback( - szWaitCaption: _title, - szWaitMessage: _message, - szProgressText: null, - varStatusBmpAnim: null, - szStatusBarText: null, - fIsCancelable: _cancellationTokenSource is not null, - iDelayToShowDialog: DelayToShowDialogSecs, - fShowProgress: _totalSteps != 0, - iTotalSteps: _totalSteps, - iCurrentStep: 0, - pCallback: callback); + public void Update(string? message = null, int? currentStep = null, int? totalSteps = null, string? progressText = null) + { + bool hasChange = false; - return dialog3; + if (message is not null && !Equals(_message, message)) + { + _message = message; + hasChange = true; } - private class Callback : IVsThreadedWaitDialogCallback + if (totalSteps is not null && totalSteps != _totalSteps) { - private readonly CancellationTokenSource? _cancellationTokenSource; + _totalSteps = totalSteps.Value; + hasChange = true; + } - public Callback(CancellationTokenSource? cancellationTokenSource) - { - _cancellationTokenSource = cancellationTokenSource; - } + if (currentStep is not null && currentStep != _currentStep) + { + Requires.Argument(currentStep <= _totalSteps, nameof(currentStep), $"Must be less than or equal to the total number of steps."); - public void OnCanceled() - { - _cancellationTokenSource?.Cancel(); - } + _currentStep = currentStep.Value; + hasChange = true; } - public CancellationToken CancellationToken => _cancellationTokenSource?.Token ?? CancellationToken.None; - - public void Update(string? message = null, int? currentStep = null, int? totalSteps = null, string? progressText = null) + if (!Equals(progressText, _progressText)) { - bool hasChange = false; - - if (message is not null && !Equals(_message, message)) - { - _message = message; - hasChange = true; - } - - if (totalSteps is not null && totalSteps != _totalSteps) - { - _totalSteps = totalSteps.Value; - hasChange = true; - } - - if (currentStep is not null && currentStep != _currentStep) - { - Requires.Argument(currentStep <= _totalSteps, nameof(currentStep), $"Must be less than or equal to the total number of steps."); - - _currentStep = currentStep.Value; - hasChange = true; - } - - if (!Equals(progressText, _progressText)) - { - _progressText = progressText; - hasChange = true; - } - - if (hasChange) - { - _dialog.UpdateProgress( - _message, - szProgressText: _progressText, - szStatusBarText: null, - iCurrentStep: _currentStep, - iTotalSteps: _totalSteps, - fDisableCancel: _cancellationTokenSource is null, - pfCanceled: out _); - } + _progressText = progressText; + hasChange = true; } - public void Dispose() + if (hasChange) { - _dialog.EndWaitDialog(out _); - _cancellationTokenSource?.Dispose(); + _dialog.UpdateProgress( + _message, + szProgressText: _progressText, + szStatusBarText: null, + iCurrentStep: _currentStep, + iTotalSteps: _totalSteps, + fDisableCancel: _cancellationTokenSource is null, + pfCanceled: out _); } } + + public void Dispose() + { + _dialog.EndWaitDialog(out _); + _cancellationTokenSource?.Dispose(); + } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitIndicator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitIndicator.cs index f802870cb7..55e53de76f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitIndicator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Waiting/VisualStudioWaitIndicator.cs @@ -4,64 +4,63 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting +namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting; + +[Export(typeof(IWaitIndicator))] +internal partial class VisualStudioWaitIndicator : IWaitIndicator { - [Export(typeof(IWaitIndicator))] - internal partial class VisualStudioWaitIndicator : IWaitIndicator - { - private readonly JoinableTaskContext _joinableTaskContext; - private readonly IVsUIService _waitDialogFactoryService; + private readonly JoinableTaskContext _joinableTaskContext; + private readonly IVsUIService _waitDialogFactoryService; - [ImportingConstructor] - public VisualStudioWaitIndicator(JoinableTaskContext joinableTaskContext, - IVsUIService waitDialogFactoryService) - { - _joinableTaskContext = joinableTaskContext; - _waitDialogFactoryService = waitDialogFactoryService; - } + [ImportingConstructor] + public VisualStudioWaitIndicator(JoinableTaskContext joinableTaskContext, + IVsUIService waitDialogFactoryService) + { + _joinableTaskContext = joinableTaskContext; + _waitDialogFactoryService = waitDialogFactoryService; + } - public async Task RunAsync(string title, string message, bool allowCancel, Func asyncMethod, int totalSteps = 0) - { - await _joinableTaskContext.Factory.SwitchToMainThreadAsync(); + public async Task RunAsync(string title, string message, bool allowCancel, Func asyncMethod, int totalSteps = 0) + { + await _joinableTaskContext.Factory.SwitchToMainThreadAsync(); - using IWaitContext waitContext = new VisualStudioWaitContext(_waitDialogFactoryService.Value, title, message, allowCancel, totalSteps); + using IWaitContext waitContext = new VisualStudioWaitContext(_waitDialogFactoryService.Value, title, message, allowCancel, totalSteps); - try - { - await asyncMethod(waitContext); + try + { + await asyncMethod(waitContext); - return WaitIndicatorResult.Completed; - } - catch (OperationCanceledException) - { - return WaitIndicatorResult.Cancelled; - } - catch (AggregateException aggregate) when (aggregate.InnerExceptions.All(e => e is OperationCanceledException)) - { - return WaitIndicatorResult.Cancelled; - } + return WaitIndicatorResult.Completed; } - - public async Task> RunAsync(string title, string message, bool allowCancel, Func> asyncMethod, int totalSteps = 0) + catch (OperationCanceledException) { - await _joinableTaskContext.Factory.SwitchToMainThreadAsync(); + return WaitIndicatorResult.Cancelled; + } + catch (AggregateException aggregate) when (aggregate.InnerExceptions.All(e => e is OperationCanceledException)) + { + return WaitIndicatorResult.Cancelled; + } + } + + public async Task> RunAsync(string title, string message, bool allowCancel, Func> asyncMethod, int totalSteps = 0) + { + await _joinableTaskContext.Factory.SwitchToMainThreadAsync(); - using IWaitContext waitContext = new VisualStudioWaitContext(_waitDialogFactoryService.Value, title, message, allowCancel, totalSteps); + using IWaitContext waitContext = new VisualStudioWaitContext(_waitDialogFactoryService.Value, title, message, allowCancel, totalSteps); - try - { - T result = await asyncMethod(waitContext); + try + { + T result = await asyncMethod(waitContext); - return WaitIndicatorResult.FromResult(result); - } - catch (OperationCanceledException) - { - return WaitIndicatorResult.Cancelled; - } - catch (AggregateException aggregate) when (aggregate.InnerExceptions.All(e => e is OperationCanceledException)) - { - return WaitIndicatorResult.Cancelled; - } + return WaitIndicatorResult.FromResult(result); + } + catch (OperationCanceledException) + { + return WaitIndicatorResult.Cancelled; + } + catch (AggregateException aggregate) when (aggregate.InnerExceptions.All(e => e is OperationCanceledException)) + { + return WaitIndicatorResult.Cancelled; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilter.cs index f75f995811..f6f26de4a6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilter.cs @@ -2,74 +2,73 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms +namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; + +/// +/// A filter for the Add New Item dialog that filters out Windows Forms items from non-Windows Forms projects. +/// +[ExportProjectNodeComService(typeof(IVsFilterAddProjectItemDlg))] +[AppliesTo(ProjectCapability.DotNet)] +internal class WindowsFormsAddItemFilter : IVsFilterAddProjectItemDlg, IDisposable { - /// - /// A filter for the Add New Item dialog that filters out Windows Forms items from non-Windows Forms projects. - /// - [ExportProjectNodeComService(typeof(IVsFilterAddProjectItemDlg))] - [AppliesTo(ProjectCapability.DotNet)] - internal class WindowsFormsAddItemFilter : IVsFilterAddProjectItemDlg, IDisposable + private UnconfiguredProject? _project; + + [ImportingConstructor] + public WindowsFormsAddItemFilter(UnconfiguredProject project) { - private UnconfiguredProject? _project; + _project = project; + } - [ImportingConstructor] - public WindowsFormsAddItemFilter(UnconfiguredProject project) - { - _project = project; - } + public int FilterTreeItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) + { + pfFilter = 0; + return HResult.NotImplemented; + } - public int FilterTreeItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) - { - pfFilter = 0; - return HResult.NotImplemented; - } + public int FilterTreeItemByTemplateDir(ref Guid rguidProjectItemTemplates, string pszTemplateDir, out int pfFilter) + { + pfFilter = 0; - public int FilterTreeItemByTemplateDir(ref Guid rguidProjectItemTemplates, string pszTemplateDir, out int pfFilter) + var project = _project; + if (project is null) { - pfFilter = 0; - - var project = _project; - if (project is null) - { - return HResult.Unexpected; - } - - // Most of the templates for Windows Forms items are filtered by capabilities in the .vstemplate but there are a couple - // that use an older .vsz tmeplate format that doesn't support capabilities, so we filter them all out here. - if (pszTemplateDir.EndsWith("\\Windows Forms", StringComparisons.Paths) && !project.Capabilities.AppliesTo(ProjectCapability.WindowsForms)) - { - pfFilter = 1; - } - return HResult.OK; + return HResult.Unexpected; } - public int FilterListItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) + // Most of the templates for Windows Forms items are filtered by capabilities in the .vstemplate but there are a couple + // that use an older .vsz tmeplate format that doesn't support capabilities, so we filter them all out here. + if (pszTemplateDir.EndsWith("\\Windows Forms", StringComparisons.Paths) && !project.Capabilities.AppliesTo(ProjectCapability.WindowsForms)) { - pfFilter = 0; - return HResult.NotImplemented; + pfFilter = 1; } + return HResult.OK; + } - public int FilterListItemByTemplateFile(ref Guid rguidProjectItemTemplates, string pszTemplateFile, out int pfFilter) - { - pfFilter = 0; + public int FilterListItemByLocalizedName(ref Guid rguidProjectItemTemplates, string pszLocalizedName, out int pfFilter) + { + pfFilter = 0; + return HResult.NotImplemented; + } - // These item templates are an older style that can't be filtered by capabilities, and use a Wizard that is broken for .NET Core - if (pszTemplateFile.EndsWith("\\CSControlInheritanceWizard.vsz", StringComparisons.Paths) || - pszTemplateFile.EndsWith("\\CSFormInheritanceWizard.vsz", StringComparisons.Paths) || - pszTemplateFile.EndsWith("\\InheritedControl.vsz", StringComparisons.Paths) || - pszTemplateFile.EndsWith("\\InheritedForm.vsz", StringComparisons.Paths)) - { - pfFilter = 1; - } - return HResult.OK; - } + public int FilterListItemByTemplateFile(ref Guid rguidProjectItemTemplates, string pszTemplateFile, out int pfFilter) + { + pfFilter = 0; - public void Dispose() + // These item templates are an older style that can't be filtered by capabilities, and use a Wizard that is broken for .NET Core + if (pszTemplateFile.EndsWith("\\CSControlInheritanceWizard.vsz", StringComparisons.Paths) || + pszTemplateFile.EndsWith("\\CSFormInheritanceWizard.vsz", StringComparisons.Paths) || + pszTemplateFile.EndsWith("\\InheritedControl.vsz", StringComparisons.Paths) || + pszTemplateFile.EndsWith("\\InheritedForm.vsz", StringComparisons.Paths)) { - // Important for ProjectNodeComServices to null out fields to reduce the amount - // of data we leak when extensions incorrectly holds onto the IVsHierarchy. - _project = null; + pfFilter = 1; } + return HResult.OK; + } + + public void Dispose() + { + // Important for ProjectNodeComServices to null out fields to reduce the amount + // of data we leak when extensions incorrectly holds onto the IVsHierarchy. + _project = null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.EditorInfo.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.EditorInfo.cs index 32a5ff8019..f16db3c65c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.EditorInfo.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.EditorInfo.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms +namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; + +internal partial class WindowsFormsEditorProvider { - internal partial class WindowsFormsEditorProvider + private class EditorInfo : IProjectSpecificEditorInfo { - private class EditorInfo : IProjectSpecificEditorInfo + public EditorInfo(Guid editor, string displayName, bool isDefaultEditor) { - public EditorInfo(Guid editor, string displayName, bool isDefaultEditor) - { - Requires.NotEmpty(editor); - Requires.NotNullOrEmpty(displayName); + Requires.NotEmpty(editor); + Requires.NotNullOrEmpty(displayName); - EditorFactory = editor; - DisplayName = displayName; - IsDefaultEditor = isDefaultEditor; - } + EditorFactory = editor; + DisplayName = displayName; + IsDefaultEditor = isDefaultEditor; + } - public Guid EditorFactory { get; } + public Guid EditorFactory { get; } - public bool IsDefaultEditor { get; } + public bool IsDefaultEditor { get; } - public string DisplayName { get; } + public string DisplayName { get; } - public Guid DefaultView => VSConstants.LOGVIEWID.Designer_guid; - } + public Guid DefaultView => VSConstants.LOGVIEWID.Designer_guid; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.SubTypeDescriptor.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.SubTypeDescriptor.cs index bbdfd3678d..fc498d09fd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.SubTypeDescriptor.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.SubTypeDescriptor.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms +namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; + +internal partial class WindowsFormsEditorProvider { - internal partial class WindowsFormsEditorProvider + private class SubTypeDescriptor { - private class SubTypeDescriptor - { - public readonly string SubType; + public readonly string SubType; - public readonly string DisplayName; + public readonly string DisplayName; - public readonly bool UseDesignerByDefault; + public readonly bool UseDesignerByDefault; - public SubTypeDescriptor(string subType, string displayName, bool useDesignerByDefault) - { - SubType = subType; - DisplayName = displayName; - UseDesignerByDefault = useDesignerByDefault; - } + public SubTypeDescriptor(string subType, string displayName, bool useDesignerByDefault) + { + SubType = subType; + DisplayName = displayName; + UseDesignerByDefault = useDesignerByDefault; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs index 53316fa9cf..7ba4d97c47 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProvider.cs @@ -2,149 +2,148 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms +namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; + +/// +/// A project-specific editor provider that is responsible for handling two things; +/// +/// 1) Add the Windows Forms designer to the list of editor factories for a "designable" source file, and +/// determines whether it opens by default. +/// +/// 2) Persists whether the designer opens by default when the user uses Open With -> Set As Default. +/// +[Export(typeof(IProjectSpecificEditorProvider))] +[AppliesTo(ProjectCapability.DotNet)] +[Order(Order.BeforeDefault)] // Need to run before CPS's version before its deleted +internal partial class WindowsFormsEditorProvider : IProjectSpecificEditorProvider { - /// - /// A project-specific editor provider that is responsible for handling two things; - /// - /// 1) Add the Windows Forms designer to the list of editor factories for a "designable" source file, and - /// determines whether it opens by default. - /// - /// 2) Persists whether the designer opens by default when the user uses Open With -> Set As Default. - /// - [Export(typeof(IProjectSpecificEditorProvider))] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.BeforeDefault)] // Need to run before CPS's version before its deleted - internal partial class WindowsFormsEditorProvider : IProjectSpecificEditorProvider + private static readonly SubTypeDescriptor[] s_subTypeDescriptors = new[] { - private static readonly SubTypeDescriptor[] s_subTypeDescriptors = new[] - { - new SubTypeDescriptor("Form", VSResources.WindowsFormEditor_DisplayName, useDesignerByDefault: true), - new SubTypeDescriptor("Designer", VSResources.WindowsFormEditor_DisplayName, useDesignerByDefault: true), - new SubTypeDescriptor("UserControl", VSResources.UserControlEditor_DisplayName, useDesignerByDefault: true), - new SubTypeDescriptor("Component", VSResources.ComponentEditor_DisplayName, useDesignerByDefault: false) - }; - - private readonly UnconfiguredProject _project; - private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; - private readonly Lazy _projectTree; - private readonly Lazy _options; - - [ImportingConstructor] - public WindowsFormsEditorProvider( - UnconfiguredProject project, - [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, - Lazy projectTree, - Lazy options) - { - _project = project; - _projectAsynchronousTasksService = projectAsynchronousTasksService; - _projectTree = projectTree; - _options = options; + new SubTypeDescriptor("Form", VSResources.WindowsFormEditor_DisplayName, useDesignerByDefault: true), + new SubTypeDescriptor("Designer", VSResources.WindowsFormEditor_DisplayName, useDesignerByDefault: true), + new SubTypeDescriptor("UserControl", VSResources.UserControlEditor_DisplayName, useDesignerByDefault: true), + new SubTypeDescriptor("Component", VSResources.ComponentEditor_DisplayName, useDesignerByDefault: false) + }; + + private readonly UnconfiguredProject _project; + private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; + private readonly Lazy _projectTree; + private readonly Lazy _options; + + [ImportingConstructor] + public WindowsFormsEditorProvider( + UnconfiguredProject project, + [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, + Lazy projectTree, + Lazy options) + { + _project = project; + _projectAsynchronousTasksService = projectAsynchronousTasksService; + _projectTree = projectTree; + _options = options; - ProjectSpecificEditorProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + ProjectSpecificEditorProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - public OrderPrecedenceImportCollection ProjectSpecificEditorProviders { get; } + [ImportMany] + public OrderPrecedenceImportCollection ProjectSpecificEditorProviders { get; } - public async Task GetSpecificEditorAsync(string documentMoniker) - { - Requires.NotNullOrEmpty(documentMoniker); + public async Task GetSpecificEditorAsync(string documentMoniker) + { + Requires.NotNullOrEmpty(documentMoniker); - CancellationToken cancellationToken = _projectAsynchronousTasksService.UnloadCancellationToken; + CancellationToken cancellationToken = _projectAsynchronousTasksService.UnloadCancellationToken; - (IProjectSpecificEditorInfo? editor, SubTypeDescriptor? descriptor) - = await (GetDefaultEditorAsync(documentMoniker), GetSubTypeDescriptorAsync(documentMoniker, cancellationToken)); + (IProjectSpecificEditorInfo? editor, SubTypeDescriptor? descriptor) + = await (GetDefaultEditorAsync(documentMoniker), GetSubTypeDescriptorAsync(documentMoniker, cancellationToken)); - if (editor is null || descriptor is null) - return null; + if (editor is null || descriptor is null) + return null; - bool isDefaultEditor = await _options.Value.GetUseDesignerByDefaultAsync(descriptor.SubType, descriptor.UseDesignerByDefault, cancellationToken); + bool isDefaultEditor = await _options.Value.GetUseDesignerByDefaultAsync(descriptor.SubType, descriptor.UseDesignerByDefault, cancellationToken); - return new EditorInfo(editor.EditorFactory, descriptor.DisplayName, isDefaultEditor); - } + return new EditorInfo(editor.EditorFactory, descriptor.DisplayName, isDefaultEditor); + } - public async Task SetUseGlobalEditorAsync(string documentMoniker, bool useGlobalEditor) - { - Requires.NotNullOrEmpty(documentMoniker); + public async Task SetUseGlobalEditorAsync(string documentMoniker, bool useGlobalEditor) + { + Requires.NotNullOrEmpty(documentMoniker); - CancellationToken cancellationToken = _projectAsynchronousTasksService.UnloadCancellationToken; + CancellationToken cancellationToken = _projectAsynchronousTasksService.UnloadCancellationToken; - SubTypeDescriptor? editorInfo = await GetSubTypeDescriptorAsync(documentMoniker, cancellationToken); - if (editorInfo is null) - return false; + SubTypeDescriptor? editorInfo = await GetSubTypeDescriptorAsync(documentMoniker, cancellationToken); + if (editorInfo is null) + return false; - // 'useGlobalEditor' means use the default editor that is registered for source files - await _options.Value.SetUseDesignerByDefaultAsync(editorInfo.SubType, !useGlobalEditor, cancellationToken); - return true; - } + // 'useGlobalEditor' means use the default editor that is registered for source files + await _options.Value.SetUseDesignerByDefaultAsync(editorInfo.SubType, !useGlobalEditor, cancellationToken); + return true; + } - private async Task GetDefaultEditorAsync(string documentMoniker) - { - IProjectSpecificEditorProvider? defaultProvider = GetDefaultEditorProvider(); - if (defaultProvider is null) - return null; + private async Task GetDefaultEditorAsync(string documentMoniker) + { + IProjectSpecificEditorProvider? defaultProvider = GetDefaultEditorProvider(); + if (defaultProvider is null) + return null; - return await defaultProvider.GetSpecificEditorAsync(documentMoniker); - } + return await defaultProvider.GetSpecificEditorAsync(documentMoniker); + } - private async Task GetSubTypeDescriptorAsync(string documentMoniker, CancellationToken cancellationToken) - { - string? subType = await GetSubTypeAsync(documentMoniker, cancellationToken); + private async Task GetSubTypeDescriptorAsync(string documentMoniker, CancellationToken cancellationToken) + { + string? subType = await GetSubTypeAsync(documentMoniker, cancellationToken); - if (subType is not null) + if (subType is not null) + { + foreach (SubTypeDescriptor descriptor in s_subTypeDescriptors) { - foreach (SubTypeDescriptor descriptor in s_subTypeDescriptors) - { - if (StringComparers.PropertyLiteralValues.Equals(subType, descriptor.SubType)) - return descriptor; - } + if (StringComparers.PropertyLiteralValues.Equals(subType, descriptor.SubType)) + return descriptor; } - - return null; } - private async Task GetSubTypeAsync(string documentMoniker, CancellationToken cancellationToken) - { - IProjectItemTree? item = await FindCompileItemByMonikerAsync(documentMoniker, cancellationToken); - if (item is null) - return null; + return null; + } - ConfiguredProject? project = await _project.GetSuggestedConfiguredProjectAsync(); + private async Task GetSubTypeAsync(string documentMoniker, CancellationToken cancellationToken) + { + IProjectItemTree? item = await FindCompileItemByMonikerAsync(documentMoniker, cancellationToken); + if (item is null) + return null; - IRule? browseObject = GetBrowseObjectProperties(project!, item); - if (browseObject is null) - return null; + ConfiguredProject? project = await _project.GetSuggestedConfiguredProjectAsync(); - return await browseObject.GetPropertyValueAsync(Compile.SubTypeProperty); - } + IRule? browseObject = GetBrowseObjectProperties(project!, item); + if (browseObject is null) + return null; - protected virtual IRule? GetBrowseObjectProperties(ConfiguredProject project, IProjectItemTree item) - { - // For unit testing purposes - return item.GetBrowseObjectPropertiesViaSnapshotIfAvailable(project); - } + return await browseObject.GetPropertyValueAsync(Compile.SubTypeProperty); + } - private async Task FindCompileItemByMonikerAsync(string documentMoniker, CancellationToken cancellationToken) - { - IProjectTreeServiceState result = await _projectTree.Value.TreeService.PublishAnyNonLoadingTreeAsync(cancellationToken); + protected virtual IRule? GetBrowseObjectProperties(ConfiguredProject project, IProjectItemTree item) + { + // For unit testing purposes + return item.GetBrowseObjectPropertiesViaSnapshotIfAvailable(project); + } - if (result.TreeProvider.FindByPath(result.Tree, documentMoniker) is IProjectItemTree treeItem && - treeItem.Parent?.Flags.Contains(ProjectTreeFlags.SourceFile) == false && - StringComparers.ItemTypes.Equals(treeItem.Item?.ItemType, Compile.SchemaName)) - { - return treeItem; - } + private async Task FindCompileItemByMonikerAsync(string documentMoniker, CancellationToken cancellationToken) + { + IProjectTreeServiceState result = await _projectTree.Value.TreeService.PublishAnyNonLoadingTreeAsync(cancellationToken); - return null; + if (result.TreeProvider.FindByPath(result.Tree, documentMoniker) is IProjectItemTree treeItem && + treeItem.Parent?.Flags.Contains(ProjectTreeFlags.SourceFile) == false && + StringComparers.ItemTypes.Equals(treeItem.Item?.ItemType, Compile.SchemaName)) + { + return treeItem; } - private IProjectSpecificEditorProvider? GetDefaultEditorProvider() - { - Lazy? editorProvider = ProjectSpecificEditorProviders.FirstOrDefault(p => string.Equals(p.Metadata.Name, "Default", StringComparisons.NamedExports)); + return null; + } - return editorProvider?.Value; - } + private IProjectSpecificEditorProvider? GetDefaultEditorProvider() + { + Lazy? editorProvider = ProjectSpecificEditorProviders.FirstOrDefault(p => string.Equals(p.Metadata.Name, "Default", StringComparisons.NamedExports)); + + return editorProvider?.Value; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Xproj/XprojProjectFactory.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Xproj/XprojProjectFactory.cs index 8840724a0d..52e7da6db6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Xproj/XprojProjectFactory.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Xproj/XprojProjectFactory.cs @@ -7,73 +7,72 @@ using Microsoft.VisualStudio.Threading; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Xproj +namespace Microsoft.VisualStudio.ProjectSystem.VS.Xproj; + +[Export(typeof(IPackageService))] +[Guid(ProjectType.LegacyXProj)] +internal sealed class XprojProjectFactory : FlavoredProjectFactoryBase, IVsProjectUpgradeViaFactory4, IPackageService, IDisposable { - [Export(typeof(IPackageService))] - [Guid(ProjectType.LegacyXProj)] - internal sealed class XprojProjectFactory : FlavoredProjectFactoryBase, IVsProjectUpgradeViaFactory4, IPackageService, IDisposable + private readonly JoinableTaskContext _context; + private IVsRegisterProjectTypes? _registerProjectTypes; + private uint _cookie = VSConstants.VSCOOKIE_NIL; + + [ImportingConstructor] + public XprojProjectFactory(JoinableTaskContext context) { - private readonly JoinableTaskContext _context; - private IVsRegisterProjectTypes? _registerProjectTypes; - private uint _cookie = VSConstants.VSCOOKIE_NIL; + _context = context; + } - [ImportingConstructor] - public XprojProjectFactory(JoinableTaskContext context) - { - _context = context; - } + public async Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) + { + Assumes.Null(_registerProjectTypes); + _context.VerifyIsOnMainThread(); - public async Task InitializeAsync(IAsyncServiceProvider asyncServiceProvider) - { - Assumes.Null(_registerProjectTypes); - _context.VerifyIsOnMainThread(); + _registerProjectTypes = await asyncServiceProvider.GetServiceAsync(); - _registerProjectTypes = await asyncServiceProvider.GetServiceAsync(); + ((IVsProjectFactory)this).SetSite(new ServiceProviderToOleServiceProviderAdapter(ServiceProvider.GlobalProvider)); - ((IVsProjectFactory)this).SetSite(new ServiceProviderToOleServiceProviderAdapter(ServiceProvider.GlobalProvider)); + Guid guid = GetType().GUID; + Verify.HResult(_registerProjectTypes.RegisterProjectType(ref guid, this, out _cookie)); + } - Guid guid = GetType().GUID; - Verify.HResult(_registerProjectTypes.RegisterProjectType(ref guid, this, out _cookie)); - } + public void UpgradeProject_CheckOnly( + string fileName, + IVsUpgradeLogger? logger, + out uint upgradeRequired, + out Guid migratedProjectFactory, + out uint upgradeProjectCapabilityFlags) + { + // Xproj is deprecated. It cannot be upgraded, and cannot be loaded. + upgradeRequired = (uint)__VSPPROJECTUPGRADEVIAFACTORYREPAIRFLAGS.VSPUVF_PROJECT_DEPRECATED; + migratedProjectFactory = GetType().GUID; + upgradeProjectCapabilityFlags = 0; - public void UpgradeProject_CheckOnly( - string fileName, - IVsUpgradeLogger? logger, - out uint upgradeRequired, - out Guid migratedProjectFactory, - out uint upgradeProjectCapabilityFlags) - { - // Xproj is deprecated. It cannot be upgraded, and cannot be loaded. - upgradeRequired = (uint)__VSPPROJECTUPGRADEVIAFACTORYREPAIRFLAGS.VSPUVF_PROJECT_DEPRECATED; - migratedProjectFactory = GetType().GUID; - upgradeProjectCapabilityFlags = 0; + // Log a message explaining that the project cannot be automatically upgraded + // and how to perform the upgrade manually. This message will be presented in + // the upgrade report. + logger?.LogMessage( + (uint)__VSUL_ERRORLEVEL.VSUL_ERROR, + Path.GetFileNameWithoutExtension(fileName), + fileName, + VSResources.XprojNotSupported); + } - // Log a message explaining that the project cannot be automatically upgraded - // and how to perform the upgrade manually. This message will be presented in - // the upgrade report. - logger?.LogMessage( - (uint)__VSUL_ERRORLEVEL.VSUL_ERROR, - Path.GetFileNameWithoutExtension(fileName), - fileName, - VSResources.XprojNotSupported); - } + protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) + { + // Should not be called + throw new NotImplementedException(); + } - protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) - { - // Should not be called - throw new NotImplementedException(); - } + public void Dispose() + { + _context.VerifyIsOnMainThread(); - public void Dispose() + if (_cookie != VSConstants.VSCOOKIE_NIL && _registerProjectTypes is not null) { - _context.VerifyIsOnMainThread(); - - if (_cookie != VSConstants.VSCOOKIE_NIL && _registerProjectTypes is not null) - { - Verify.HResult(_registerProjectTypes.UnregisterProjectType(_cookie)); - } - - Dispose(disposing: true); + Verify.HResult(_registerProjectTypes.UnregisterProjectType(_cookie)); } + + Dispose(disposing: true); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/HierarchyId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/HierarchyId.cs index 67331fc83e..9ae0ea5657 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/HierarchyId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/HierarchyId.cs @@ -1,100 +1,99 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell +namespace Microsoft.VisualStudio.Shell; + +/// +/// Represents an ITEMID in an IVsHierarchy. +/// +internal readonly struct HierarchyId { + private readonly uint _id; + /// - /// Represents an ITEMID in an IVsHierarchy. + /// Represents the root of a project hierarchy and is used to identify the entire hierarchy, as opposed + /// to a single item. /// - internal readonly struct HierarchyId - { - private readonly uint _id; + public static readonly HierarchyId Root = new(VSConstants.VSITEMID_ROOT); - /// - /// Represents the root of a project hierarchy and is used to identify the entire hierarchy, as opposed - /// to a single item. - /// - public static readonly HierarchyId Root = new(VSConstants.VSITEMID_ROOT); - - /// - /// Represents the currently selected items, which can include the root of the hierarchy. - /// - public static readonly HierarchyId Selection = new(VSConstants.VSITEMID_SELECTION); + /// + /// Represents the currently selected items, which can include the root of the hierarchy. + /// + public static readonly HierarchyId Selection = new(VSConstants.VSITEMID_SELECTION); - /// - /// Represents the absence of a project item. This value is used when there is no current selection. - /// - public static readonly HierarchyId Nil = new(VSConstants.VSITEMID_NIL); + /// + /// Represents the absence of a project item. This value is used when there is no current selection. + /// + public static readonly HierarchyId Nil = new(VSConstants.VSITEMID_NIL); - /// - /// Represent an empty item. - /// - public static readonly HierarchyId Empty = new(0); + /// + /// Represent an empty item. + /// + public static readonly HierarchyId Empty = new(0); - public HierarchyId(uint id) - { - _id = id; - } + public HierarchyId(uint id) + { + _id = id; + } - /// - /// Returns the underlying ITEMID. - /// - public uint Id - { - get { return _id; } - } + /// + /// Returns the underlying ITEMID. + /// + public uint Id + { + get { return _id; } + } - /// - /// Returns a value indicating if the represents the root of a project hierarchy - /// and is used to identify the entire hierarchy, as opposed to a single item. - /// - public bool IsRoot - { - get { return _id == Root.Id; } - } + /// + /// Returns a value indicating if the represents the root of a project hierarchy + /// and is used to identify the entire hierarchy, as opposed to a single item. + /// + public bool IsRoot + { + get { return _id == Root.Id; } + } - /// - /// Returns a value indicating if the represents the currently selected items, - /// which can include the root of the hierarchy. - /// - public bool IsSelection - { - get { return _id == Selection.Id; } - } + /// + /// Returns a value indicating if the represents the currently selected items, + /// which can include the root of the hierarchy. + /// + public bool IsSelection + { + get { return _id == Selection.Id; } + } - /// - /// Returns a value indicating if the is empty. - /// - public bool IsEmpty - { - get { return _id == Empty.Id; } - } + /// + /// Returns a value indicating if the is empty. + /// + public bool IsEmpty + { + get { return _id == Empty.Id; } + } - /// - /// Returns a value indicating if the is or - /// . - /// - public bool IsNilOrEmpty - { - get { return IsNil || IsEmpty; } - } + /// + /// Returns a value indicating if the is or + /// . + /// + public bool IsNilOrEmpty + { + get { return IsNil || IsEmpty; } + } - /// - /// Returns a value indicating if the represents the absence of a project item. - /// This value is used when there is no current selection. - /// - public bool IsNil - { - get { return _id == Nil.Id; } - } + /// + /// Returns a value indicating if the represents the absence of a project item. + /// This value is used when there is no current selection. + /// + public bool IsNil + { + get { return _id == Nil.Id; } + } - public static implicit operator uint(HierarchyId id) - { - return id.Id; - } + public static implicit operator uint(HierarchyId id) + { + return id.Id; + } - public static implicit operator HierarchyId(uint id) - { - return new HierarchyId(id); - } + public static implicit operator HierarchyId(uint id) + { + return new HierarchyId(id); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/IVsShellServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/IVsShellServices.cs index a4fad1908e..ea9f80173d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/IVsShellServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/IVsShellServices.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.Shell +namespace Microsoft.VisualStudio.Shell; + +/// +/// Provides common shell services in an agnostic manner +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Host)] +internal interface IVsShellServices { /// - /// Provides common shell services in an agnostic manner + /// Gets a value indicating whether VS is running in command line mode. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Host)] - internal interface IVsShellServices - { - /// - /// Gets a value indicating whether VS is running in command line mode. - /// - /// - /// This has a free-threaded implementation. - /// - Task IsCommandLineModeAsync(CancellationToken cancellationToken = default); + /// + /// This has a free-threaded implementation. + /// + Task IsCommandLineModeAsync(CancellationToken cancellationToken = default); - /// - /// Gets a value indicating whether VS is populating the solution cache during a command line operation. - /// - /// - /// Implies is also . - /// - /// - /// This has a free-threaded implementation. - /// - Task IsPopulateSolutionCacheModeAsync(CancellationToken cancellationToken = default); - } + /// + /// Gets a value indicating whether VS is populating the solution cache during a command line operation. + /// + /// + /// Implies is also . + /// + /// + /// This has a free-threaded implementation. + /// + Task IsPopulateSolutionCacheModeAsync(CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowExtensions.cs index 7109b624ce..6f916a28aa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowExtensions.cs @@ -2,53 +2,52 @@ using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +/// +/// Provides extension methods for . +/// +internal static class IVsOutputWindowExtensions { /// - /// Provides extension methods for . + /// Actives the output window pane associated with the specified GUID. Does nothing if the pane cannot be found. /// - internal static class IVsOutputWindowExtensions + /// + /// is . + /// + /// + /// is empty. + /// + public static void ActivatePane(this IVsOutputWindow outputWindow, Guid paneGuid) { - /// - /// Actives the output window pane associated with the specified GUID. Does nothing if the pane cannot be found. - /// - /// - /// is . - /// - /// - /// is empty. - /// - public static void ActivatePane(this IVsOutputWindow outputWindow, Guid paneGuid) - { - Requires.NotNull(outputWindow); - Requires.NotEmpty(paneGuid); - - HResult hr = outputWindow.GetPane(ref paneGuid, out IVsOutputWindowPane pane); - if (hr.IsOK) // Pane found - { - Verify.HResult(pane.Activate()); - } - } + Requires.NotNull(outputWindow); + Requires.NotEmpty(paneGuid); - /// - /// Returns the GUID associated with the active window pane, or if no - /// active pane or the active pane is unknown. - /// - /// - /// is . - /// - public static Guid GetActivePane(this IVsOutputWindow outputWindow) + HResult hr = outputWindow.GetPane(ref paneGuid, out IVsOutputWindowPane pane); + if (hr.IsOK) // Pane found { - Requires.NotNull(outputWindow); + Verify.HResult(pane.Activate()); + } + } - if (outputWindow is IVsOutputWindow2 outputWindow2) - { - Verify.HResult(outputWindow2.GetActivePaneGUID(out Guid activePaneGuid)); + /// + /// Returns the GUID associated with the active window pane, or if no + /// active pane or the active pane is unknown. + /// + /// + /// is . + /// + public static Guid GetActivePane(this IVsOutputWindow outputWindow) + { + Requires.NotNull(outputWindow); - return activePaneGuid; - } + if (outputWindow is IVsOutputWindow2 outputWindow2) + { + Verify.HResult(outputWindow2.GetActivePaneGUID(out Guid activePaneGuid)); - return Guid.Empty; + return activePaneGuid; } + + return Guid.Empty; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowPaneExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowPaneExtensions.cs index 258d0f078c..dd7e704683 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowPaneExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/IVsOutputWindowPaneExtensions.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +/// +/// Provides extension methods for . +/// +internal static class IVsOutputWindowPaneExtensions { /// - /// Provides extension methods for . + /// Prints text to the output window avoiding pushing a message pump, if possible. /// - internal static class IVsOutputWindowPaneExtensions + /// + /// is . + /// + public static void OutputStringNoPump(this IVsOutputWindowPane pane, string pszOutputString) { - /// - /// Prints text to the output window avoiding pushing a message pump, if possible. - /// - /// - /// is . - /// - public static void OutputStringNoPump(this IVsOutputWindowPane pane, string pszOutputString) - { - Requires.NotNull(pane); + Requires.NotNull(pane); - if (pane is IVsOutputWindowPaneNoPump noPumpPane) - { - noPumpPane.OutputStringNoPump(pszOutputString + Environment.NewLine); - } - else - { - Verify.HResult(pane.OutputStringThreadSafe(pszOutputString + Environment.NewLine)); - } + if (pane is IVsOutputWindowPaneNoPump noPumpPane) + { + noPumpPane.OutputStringNoPump(pszOutputString + Environment.NewLine); + } + else + { + Verify.HResult(pane.OutputStringThreadSafe(pszOutputString + Environment.NewLine)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsHierarchyExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsHierarchyExtensions.cs index 8e668d67bc..7ce90713d6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsHierarchyExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsHierarchyExtensions.cs @@ -3,103 +3,102 @@ using Microsoft.VisualStudio.ProjectSystem.VS; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +/// +/// Provides extension methods for instances. +/// +internal static class VsHierarchyExtensions { /// - /// Provides extension methods for instances. + /// Returns the GUID of the specified property. /// - internal static class VsHierarchyExtensions + public static Guid GetGuidProperty(this IVsHierarchy hierarchy, VsHierarchyPropID property) { - /// - /// Returns the GUID of the specified property. - /// - public static Guid GetGuidProperty(this IVsHierarchy hierarchy, VsHierarchyPropID property) - { - Requires.NotNull(hierarchy); - Verify.HResult(hierarchy.GetGuidProperty(HierarchyId.Root, (int)property, out Guid result)); + Requires.NotNull(hierarchy); + Verify.HResult(hierarchy.GetGuidProperty(HierarchyId.Root, (int)property, out Guid result)); - return result; - } + return result; + } - /// - /// Gets the value of the specified property if the hierarchy supports it, or throws an exception if there was an error. - /// - public static T? GetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID property, T? defaultValue = default) - { - return GetProperty(hierarchy, HierarchyId.Root, property, defaultValue); - } + /// + /// Gets the value of the specified property if the hierarchy supports it, or throws an exception if there was an error. + /// + public static T? GetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID property, T? defaultValue = default) + { + return GetProperty(hierarchy, HierarchyId.Root, property, defaultValue); + } - /// - /// Gets the value of the specified property of the specified item if the hierarchy supports it, or throws an exception if there was an error. - /// - public static T? GetProperty(this IVsHierarchy hierarchy, HierarchyId item, VsHierarchyPropID property, T? defaultValue = default) - { - Verify.HResult(GetProperty(hierarchy, item, property, defaultValue, out T? result)); + /// + /// Gets the value of the specified property of the specified item if the hierarchy supports it, or throws an exception if there was an error. + /// + public static T? GetProperty(this IVsHierarchy hierarchy, HierarchyId item, VsHierarchyPropID property, T? defaultValue = default) + { + Verify.HResult(GetProperty(hierarchy, item, property, defaultValue, out T? result)); - return result; - } + return result; + } - /// - /// Gets the value of the specified property if the hierarchy supports it, or returns a HRESULT if there was an error. - /// - public static int GetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID property, T? defaultValue, out T? result) + /// + /// Gets the value of the specified property if the hierarchy supports it, or returns a HRESULT if there was an error. + /// + public static int GetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID property, T? defaultValue, out T? result) + { + return GetProperty(hierarchy, HierarchyId.Root, property, defaultValue, out result); + } + + /// + /// Gets the value of the specified property of the specified item if the hierarchy supports it, or returns a HRESULT if there was an error. + /// + public static int GetProperty(this IVsHierarchy hierarchy, HierarchyId item, VsHierarchyPropID property, T defaultValue, out T? result) + { + Requires.NotNull(hierarchy); + + if (item.IsNilOrEmpty || item.IsSelection) + throw new ArgumentException(null, nameof(item)); + + HResult hr = hierarchy.GetProperty(item, (int)property, out object resultObject); + if (hr.IsOK) { - return GetProperty(hierarchy, HierarchyId.Root, property, defaultValue, out result); + // NOTE: We consider it a bug in the underlying project system or the caller if this cast fails + result = (T)resultObject; + return HResult.OK; } - /// - /// Gets the value of the specified property of the specified item if the hierarchy supports it, or returns a HRESULT if there was an error. - /// - public static int GetProperty(this IVsHierarchy hierarchy, HierarchyId item, VsHierarchyPropID property, T defaultValue, out T? result) + if (hr == HResult.MemberNotFound) { - Requires.NotNull(hierarchy); - - if (item.IsNilOrEmpty || item.IsSelection) - throw new ArgumentException(null, nameof(item)); - - HResult hr = hierarchy.GetProperty(item, (int)property, out object resultObject); - if (hr.IsOK) - { - // NOTE: We consider it a bug in the underlying project system or the caller if this cast fails - result = (T)resultObject; - return HResult.OK; - } - - if (hr == HResult.MemberNotFound) - { - result = defaultValue; - return HResult.OK; - } - - result = default!; - return hr; + result = defaultValue; + return HResult.OK; } - /// - /// Returns EnvDTE.Project object for the hierarchy - /// - public static EnvDTE.Project? GetDTEProject(this IVsHierarchy hierarchy) - { - UIThreadHelper.VerifyOnUIThread(); - if (ErrorHandler.Succeeded(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out object extObject))) - { - return extObject as EnvDTE.Project; - } + result = default!; + return hr; + } - return null; + /// + /// Returns EnvDTE.Project object for the hierarchy + /// + public static EnvDTE.Project? GetDTEProject(this IVsHierarchy hierarchy) + { + UIThreadHelper.VerifyOnUIThread(); + if (ErrorHandler.Succeeded(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out object extObject))) + { + return extObject as EnvDTE.Project; } - /// - /// Returns the path to the project file. Assumes the hierarchy implements IVsProject. Returns null on failure - /// - public static string? GetProjectFilePath(this IVsHierarchy hierarchy) - { - if (ErrorHandler.Succeeded(((IVsProject)hierarchy).GetMkDocument(VSConstants.VSITEMID_ROOT, out string projectPath))) - { - return projectPath; - } + return null; + } - return null; + /// + /// Returns the path to the project file. Assumes the hierarchy implements IVsProject. Returns null on failure + /// + public static string? GetProjectFilePath(this IVsHierarchy hierarchy) + { + if (ErrorHandler.Succeeded(((IVsProject)hierarchy).GetMkDocument(VSConstants.VSITEMID_ROOT, out string projectPath))) + { + return projectPath; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsProjectExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsProjectExtensions.cs index 27a0432064..865816eaa0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsProjectExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsProjectExtensions.cs @@ -1,50 +1,49 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +/// +/// Provides extension methods for instances. +/// +internal static class VsProjectExtensions { /// - /// Provides extension methods for instances. + /// Returns the of the given document moniker, or + /// if the document moniker is not part of the project. /// - internal static class VsProjectExtensions + public static HierarchyId GetHierarchyId(this IVsProject project, string documentMoniker) { - /// - /// Returns the of the given document moniker, or - /// if the document moniker is not part of the project. - /// - public static HierarchyId GetHierarchyId(this IVsProject project, string documentMoniker) - { - Requires.NotNull(project); - Requires.NotNullOrEmpty(documentMoniker); - - var priority = new VSDOCUMENTPRIORITY[1]; - Verify.HResult(project.IsDocumentInProject(documentMoniker, out int isFound, priority, out uint itemId)); - - // We only return items that are actually part of the project. CPS returns non-member from this API. - if (isFound == 0 || (priority[0] != VSDOCUMENTPRIORITY.DP_Standard && priority[0] != VSDOCUMENTPRIORITY.DP_Intrinsic)) - return HierarchyId.Nil; - - HierarchyId id = itemId; - - Assumes.False(id.IsNilOrEmpty); - - return id; - } - - /// - /// Opens the specified item with the specified editor using the primary logical view. - /// - /// - /// The that contains the editor; otherwise, if it was opened - /// with an editor external of Visual Studio. - /// - public static IVsWindowFrame? OpenItemWithSpecific(this IVsProject4 project, HierarchyId id, Guid editorType) - { - Requires.NotNull(project); - - Verify.HResult(project.OpenItemWithSpecific(id, 0, ref editorType, "", VSConstants.LOGVIEWID_Primary, (IntPtr)(-1), out IVsWindowFrame frame)); - - // NOTE: frame is 'null' when opened in an external editor - return frame; - } + Requires.NotNull(project); + Requires.NotNullOrEmpty(documentMoniker); + + var priority = new VSDOCUMENTPRIORITY[1]; + Verify.HResult(project.IsDocumentInProject(documentMoniker, out int isFound, priority, out uint itemId)); + + // We only return items that are actually part of the project. CPS returns non-member from this API. + if (isFound == 0 || (priority[0] != VSDOCUMENTPRIORITY.DP_Standard && priority[0] != VSDOCUMENTPRIORITY.DP_Intrinsic)) + return HierarchyId.Nil; + + HierarchyId id = itemId; + + Assumes.False(id.IsNilOrEmpty); + + return id; + } + + /// + /// Opens the specified item with the specified editor using the primary logical view. + /// + /// + /// The that contains the editor; otherwise, if it was opened + /// with an editor external of Visual Studio. + /// + public static IVsWindowFrame? OpenItemWithSpecific(this IVsProject4 project, HierarchyId id, Guid editorType) + { + Requires.NotNull(project); + + Verify.HResult(project.OpenItemWithSpecific(id, 0, ref editorType, "", VSConstants.LOGVIEWID_Primary, (IntPtr)(-1), out IVsWindowFrame frame)); + + // NOTE: frame is 'null' when opened in an external editor + return frame; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsShellExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsShellExtensions.cs index ecd3a8df02..59165203f7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsShellExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/Interop/VsShellExtensions.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class VsShellExtensions { - internal static class VsShellExtensions + /// + /// Directly loads a localized string from a VSPackage satellite DLL. + /// + /// The IVsShell implementation + /// Unique identifier of the VSPackage whose UI DLL contains the string specified to load. + /// Identifier of the string table resource. + /// The requested string + public static string LoadPackageString(this IVsShell vsShell, Guid packageGuid, uint resourceId) { - /// - /// Directly loads a localized string from a VSPackage satellite DLL. - /// - /// The IVsShell implementation - /// Unique identifier of the VSPackage whose UI DLL contains the string specified to load. - /// Identifier of the string table resource. - /// The requested string - public static string LoadPackageString(this IVsShell vsShell, Guid packageGuid, uint resourceId) - { - ErrorHandler.ThrowOnFailure(vsShell.LoadPackageString(ref packageGuid, resourceId, out string result)); + ErrorHandler.ThrowOnFailure(vsShell.LoadPackageString(ref packageGuid, resourceId, out string result)); - return result; - } + return result; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/ServiceProviderToOleServiceProviderAdapter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/ServiceProviderToOleServiceProviderAdapter.cs index c01ff54549..8949f48084 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/ServiceProviderToOleServiceProviderAdapter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/ServiceProviderToOleServiceProviderAdapter.cs @@ -4,59 +4,58 @@ using Microsoft.VisualStudio.ProjectSystem.VS; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; -namespace Microsoft.VisualStudio.Shell +namespace Microsoft.VisualStudio.Shell; + +// Adapts an IServiceProvider to an OLE IServiceProvider +internal class ServiceProviderToOleServiceProviderAdapter : IOleServiceProvider { - // Adapts an IServiceProvider to an OLE IServiceProvider - internal class ServiceProviderToOleServiceProviderAdapter : IOleServiceProvider + private readonly IServiceProvider _serviceProvider; + + public ServiceProviderToOleServiceProviderAdapter(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + Requires.NotNull(serviceProvider); - public ServiceProviderToOleServiceProviderAdapter(IServiceProvider serviceProvider) - { - Requires.NotNull(serviceProvider); + _serviceProvider = serviceProvider; + } - _serviceProvider = serviceProvider; - } + public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) + { + ppvObject = IntPtr.Zero; - public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) + if (!TryGetService(guidService, out object service)) { - ppvObject = IntPtr.Zero; - - if (!TryGetService(guidService, out object service)) - { - return HResult.NoInterface; - } - - return GetComInterfaceForObject(service, riid, out ppvObject); + return HResult.NoInterface; } - private bool TryGetService(Guid riid, out object service) - { - var serviceType = Type.GetTypeFromCLSID(riid, throwOnError: true); // Should only throw on OOM according to MSDN + return GetComInterfaceForObject(service, riid, out ppvObject); + } + + private bool TryGetService(Guid riid, out object service) + { + var serviceType = Type.GetTypeFromCLSID(riid, throwOnError: true); // Should only throw on OOM according to MSDN #pragma warning disable RS0030 // Do not used banned APIs (deliberately adapting) - service = _serviceProvider.GetService(serviceType); + service = _serviceProvider.GetService(serviceType); #pragma warning restore RS0030 // Do not used banned APIs - return service is not null; - } + return service is not null; + } - private static HResult GetComInterfaceForObject(object instance, Guid iid, out IntPtr ppvObject) - { - Requires.NotNull(instance); + private static HResult GetComInterfaceForObject(object instance, Guid iid, out IntPtr ppvObject) + { + Requires.NotNull(instance); - IntPtr unknown = Marshal.GetIUnknownForObject(instance); - if (iid.Equals(VSConstants.IID_IUnknown)) - { - ppvObject = unknown; - return HResult.OK; - } + IntPtr unknown = Marshal.GetIUnknownForObject(instance); + if (iid.Equals(VSConstants.IID_IUnknown)) + { + ppvObject = unknown; + return HResult.OK; + } - HResult result = Marshal.QueryInterface(unknown, ref iid, out ppvObject); + HResult result = Marshal.QueryInterface(unknown, ref iid, out ppvObject); - // Don't leak the IUnknown - Marshal.Release(unknown); + // Don't leak the IUnknown + Marshal.Release(unknown); - return result; - } + return result; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/SolutionExplorerWindow.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/SolutionExplorerWindow.cs index f674a255a2..4965b48772 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/SolutionExplorerWindow.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Shell/SolutionExplorerWindow.cs @@ -5,88 +5,87 @@ using Microsoft.VisualStudio.ProjectSystem.VS; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.Shell +namespace Microsoft.VisualStudio.Shell; + +/// +/// Provides operations for manipulating nodes in Solution Explorer. +/// +[Export] +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal class SolutionExplorerWindow { + private static readonly Guid s_solutionExplorer = new(EnvDTE.Constants.vsWindowKindSolutionExplorer); + + private readonly Lazy _solutionExplorer; + private readonly IProjectThreadingService _threadingService; + + [ImportingConstructor] + public SolutionExplorerWindow( + IProjectThreadingService threadingService, + IVsUIService shell) + { + _solutionExplorer = new Lazy(() => GetUIHierarchyWindow(shell, s_solutionExplorer)); + _threadingService = threadingService; + } + /// - /// Provides operations for manipulating nodes in Solution Explorer. + /// Gets a value indicating if the Solution Explorer window is available. /// - [Export] - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal class SolutionExplorerWindow + /// + /// This property was not accessed from the UI thread. + /// + public bool IsAvailable { - private static readonly Guid s_solutionExplorer = new(EnvDTE.Constants.vsWindowKindSolutionExplorer); - - private readonly Lazy _solutionExplorer; - private readonly IProjectThreadingService _threadingService; - - [ImportingConstructor] - public SolutionExplorerWindow( - IProjectThreadingService threadingService, - IVsUIService shell) + get { - _solutionExplorer = new Lazy(() => GetUIHierarchyWindow(shell, s_solutionExplorer)); - _threadingService = threadingService; + _threadingService.VerifyOnUIThread(); + return _solutionExplorer.Value is not null; } + } - /// - /// Gets a value indicating if the Solution Explorer window is available. - /// - /// - /// This property was not accessed from the UI thread. - /// - public bool IsAvailable - { - get - { - _threadingService.VerifyOnUIThread(); - return _solutionExplorer.Value is not null; - } - } + /// + /// Selects the specified node. + /// + /// is . + /// is nil, empty or represents a selection. + /// This method was not accessed from the UI thread. + /// is . + public HResult Select(IVsUIHierarchy uiHierarchy, HierarchyId id) + { + return ExpandItem(uiHierarchy, id, EXPANDFLAGS.EXPF_SelectItem); + } - /// - /// Selects the specified node. - /// - /// is . - /// is nil, empty or represents a selection. - /// This method was not accessed from the UI thread. - /// is . - public HResult Select(IVsUIHierarchy uiHierarchy, HierarchyId id) - { - return ExpandItem(uiHierarchy, id, EXPANDFLAGS.EXPF_SelectItem); - } + private HResult ExpandItem(IVsUIHierarchy uiHierarchy, HierarchyId id, EXPANDFLAGS flags) + { + Requires.NotNull(uiHierarchy); + Requires.Argument(!(id.IsNilOrEmpty || id.IsSelection), nameof(id), "id must not be nil, empty or represent a selection."); - private HResult ExpandItem(IVsUIHierarchy uiHierarchy, HierarchyId id, EXPANDFLAGS flags) - { - Requires.NotNull(uiHierarchy); - Requires.Argument(!(id.IsNilOrEmpty || id.IsSelection), nameof(id), "id must not be nil, empty or represent a selection."); + return Invoke(window => window.ExpandItem(uiHierarchy, id, flags)); + } - return Invoke(window => window.ExpandItem(uiHierarchy, id, flags)); - } + private HResult Invoke(Func action) + { + _threadingService.VerifyOnUIThread(); - private HResult Invoke(Func action) + IVsUIHierarchyWindow2? window = _solutionExplorer.Value; + if (window is null) { - _threadingService.VerifyOnUIThread(); - - IVsUIHierarchyWindow2? window = _solutionExplorer.Value; - if (window is null) - { - throw new InvalidOperationException("Solution Explorer is not available in command-line mode."); - } - - return action(window); + throw new InvalidOperationException("Solution Explorer is not available in command-line mode."); } - private static IVsUIHierarchyWindow2? GetUIHierarchyWindow(IVsUIService shell, Guid persistenceSlot) - { - if (ErrorHandler.Succeeded(shell.Value.FindToolWindow(0, ref persistenceSlot, out IVsWindowFrame? frame)) && frame is not null) - { - ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out object? view)); + return action(window); + } - return (IVsUIHierarchyWindow2)view; - } + private static IVsUIHierarchyWindow2? GetUIHierarchyWindow(IVsUIService shell, Guid persistenceSlot) + { + if (ErrorHandler.Succeeded(shell.Value.FindToolWindow(0, ref persistenceSlot, out IVsWindowFrame? frame)) && frame is not null) + { + ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out object? view)); - // Command-line/non-UI mode - return null; + return (IVsUIHierarchyWindow2)view; } + + // Command-line/non-UI mode + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Threading/JoinableTaskContextExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Threading/JoinableTaskContextExtensions.cs index 050999f5f0..edd2792c39 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Threading/JoinableTaskContextExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/Threading/JoinableTaskContextExtensions.cs @@ -3,22 +3,21 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.Threading +namespace Microsoft.VisualStudio.Threading; + +internal static class JoinableTaskContextExtensions { - internal static class JoinableTaskContextExtensions + /// + /// Verifies that this method is called on the main ("UI") thread, + /// and throws an exception if not. + /// + public static void VerifyIsOnMainThread(this JoinableTaskContext joinableTaskContext) { - /// - /// Verifies that this method is called on the main ("UI") thread, - /// and throws an exception if not. - /// - public static void VerifyIsOnMainThread(this JoinableTaskContext joinableTaskContext) - { - Requires.NotNull(joinableTaskContext); + Requires.NotNull(joinableTaskContext); - if (!joinableTaskContext.IsOnMainThread) - { - throw new COMException("This method must be called on the UI thread.", HResult.WrongThread); - } + if (!joinableTaskContext.IsOnMainThread) + { + throw new COMException("This method must be called on the UI thread.", HResult.WrongThread); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaConverter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaConverter.cs index 33421d7723..a3d577a40e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaConverter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaConverter.cs @@ -3,66 +3,65 @@ using System.Globalization; using System.Windows.Data; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +// Copied from CPS Converters.cs +internal static partial class Converters { - // Copied from CPS Converters.cs - internal static partial class Converters + /// + /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. + /// + /// The source type, to convert from. + /// The destination type, to convert to. + private sealed class LambdaConverter : IValueConverter { - /// - /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. - /// - /// The source type, to convert from. - /// The destination type, to convert to. - private sealed class LambdaConverter : IValueConverter - { - private readonly Func _convert; - private readonly Func? _convertBack; - private readonly bool _convertFromNull; + private readonly Func _convert; + private readonly Func? _convertBack; + private readonly bool _convertFromNull; - public LambdaConverter(Func convert, Func? convertBack = null, bool convertFromNull = true) - { - _convert = convert; - _convertBack = convertBack; - _convertFromNull = convertFromNull; - } + public LambdaConverter(Func convert, Func? convertBack = null, bool convertFromNull = true) + { + _convert = convert; + _convertBack = convertBack; + _convertFromNull = convertFromNull; + } - public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture) + { + try { - try + if (value is TFrom from) { - if (value is TFrom from) - { - return _convert(from); - } - - if (value is null && _convertFromNull) - { - return _convert(default!); - } - - return value; + return _convert(from); } - catch (Exception ex) + + if (value is null && _convertFromNull) { - throw new InvalidOperationException($"Callback threw an exception when converting from {typeof(TFrom)} to {typeof(TTo)}.", ex); + return _convert(default!); } - } - public object? ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture) + return value; + } + catch (Exception ex) { - try - { - if (_convertBack is not null && value is TTo to) - { - return _convertBack(to); - } + throw new InvalidOperationException($"Callback threw an exception when converting from {typeof(TFrom)} to {typeof(TTo)}.", ex); + } + } - return value; - } - catch (Exception ex) + public object? ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture) + { + try + { + if (_convertBack is not null && value is TTo to) { - throw new InvalidOperationException($"Callback threw an exception when converting back to {typeof(TTo)} from {typeof(TFrom)}.", ex); + return _convertBack(to); } + + return value; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Callback threw an exception when converting back to {typeof(TTo)} from {typeof(TFrom)}.", ex); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaMultiConverter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaMultiConverter.cs index 08dad1d5b0..bf1efebcc0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaMultiConverter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.LambdaMultiConverter.cs @@ -6,166 +6,165 @@ using System.Windows; using System.Windows.Data; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities -{ - // Copied from CPS Converters.cs +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +// Copied from CPS Converters.cs - internal static partial class Converters +internal static partial class Converters +{ + /// + /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. + /// + /// The first source type, to convert from. + /// The second source type, to convert from. + /// The destination type, to convert to. + private sealed class LambdaMultiConverter : IMultiValueConverter { - /// - /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. - /// - /// The first source type, to convert from. - /// The second source type, to convert from. - /// The destination type, to convert to. - private sealed class LambdaMultiConverter : IMultiValueConverter + private readonly Func _convert; + private readonly Func _convertBack; + + public LambdaMultiConverter(Func convert, Func convertBack = null) { - private readonly Func _convert; - private readonly Func _convertBack; + _convert = convert; + _convertBack = convertBack; + } - public LambdaMultiConverter(Func convert, Func convertBack = null) + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length == 2 && TryConvert(values[0], out TFrom1 t1) && TryConvert(values[1], out TFrom2 t2)) { - _convert = convert; - _convertBack = convertBack; + return _convert(t1, t2); } - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + return DependencyProperty.UnsetValue; + + static bool TryConvert(object o, out T t) { - if (values.Length == 2 && TryConvert(values[0], out TFrom1 t1) && TryConvert(values[1], out TFrom2 t2)) + if (o is T tt) { - return _convert(t1, t2); + t = tt; + return true; } - return DependencyProperty.UnsetValue; - - static bool TryConvert(object o, out T t) - { - if (o is T tt) - { - t = tt; - return true; - } - - t = default; - return o is null; - } + t = default; + return o is null; } + } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + if (_convertBack is not null && value is TTo to) { - if (_convertBack is not null && value is TTo to) - { - var values = _convertBack(to); - return new object[] { values.Item1, values.Item2 }; - } - - return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; + var values = _convertBack(to); + return new object[] { values.Item1, values.Item2 }; } + + return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; } + } - /// - /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. - /// - /// The first source type, to convert from. - /// The second source type, to convert from. - /// The third source type, to convert from. - /// The destination type, to convert to. - private sealed class LambdaMultiConverter : IMultiValueConverter + /// + /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. + /// + /// The first source type, to convert from. + /// The second source type, to convert from. + /// The third source type, to convert from. + /// The destination type, to convert to. + private sealed class LambdaMultiConverter : IMultiValueConverter + { + private readonly Func _convert; + private readonly Func _convertBack; + + public LambdaMultiConverter(Func convert, Func convertBack = null) { - private readonly Func _convert; - private readonly Func _convertBack; + _convert = convert; + _convertBack = convertBack; + } - public LambdaMultiConverter(Func convert, Func convertBack = null) + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length == 3 && TryConvert(values[0], out TFrom1 t1) && TryConvert(values[1], out TFrom2 t2) && TryConvert(values[2], out TFrom3 t3)) { - _convert = convert; - _convertBack = convertBack; + return _convert(t1, t2, t3); } - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + return DependencyProperty.UnsetValue; + + static bool TryConvert(object o, out T t) { - if (values.Length == 3 && TryConvert(values[0], out TFrom1 t1) && TryConvert(values[1], out TFrom2 t2) && TryConvert(values[2], out TFrom3 t3)) + if (o is T tt) { - return _convert(t1, t2, t3); + t = tt; + return true; } - return DependencyProperty.UnsetValue; - - static bool TryConvert(object o, out T t) - { - if (o is T tt) - { - t = tt; - return true; - } - - t = default; - return o is null; - } + t = default; + return o is null; } + } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + if (_convertBack is not null && value is TTo to) { - if (_convertBack is not null && value is TTo to) - { - var values = _convertBack(to); - return new object[] { values.Item1, values.Item2, values.Item3 }; - } - - return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; + var values = _convertBack(to); + return new object[] { values.Item1, values.Item2, values.Item3 }; } + + return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; } + } - /// - /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. - /// - /// The first source type, to convert from. - /// The second source type, to convert from. - /// The third source type, to convert from. - /// The fourth source type, to convert from. - /// The destination type, to convert to. - private sealed class LambdaMultiConverter : IMultiValueConverter + /// + /// Expresses a one or two-way value converter using one or two lambda expressions, respectively. + /// + /// The first source type, to convert from. + /// The second source type, to convert from. + /// The third source type, to convert from. + /// The fourth source type, to convert from. + /// The destination type, to convert to. + private sealed class LambdaMultiConverter : IMultiValueConverter + { + private readonly Func _convert; + private readonly Func _convertBack; + + public LambdaMultiConverter(Func convert, Func convertBack = null) { - private readonly Func _convert; - private readonly Func _convertBack; + _convert = convert; + _convertBack = convertBack; + } - public LambdaMultiConverter(Func convert, Func convertBack = null) + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length == 4 && TryConvert(values[0], out TFrom1 t1) && TryConvert(values[1], out TFrom2 t2) && TryConvert(values[2], out TFrom3 t3) && TryConvert(values[3], out TFrom4 t4)) { - _convert = convert; - _convertBack = convertBack; + return _convert(t1, t2, t3, t4); } - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + return DependencyProperty.UnsetValue; + + static bool TryConvert(object o, out T t) { - if (values.Length == 4 && TryConvert(values[0], out TFrom1 t1) && TryConvert(values[1], out TFrom2 t2) && TryConvert(values[2], out TFrom3 t3) && TryConvert(values[3], out TFrom4 t4)) + if (o is T tt) { - return _convert(t1, t2, t3, t4); + t = tt; + return true; } - return DependencyProperty.UnsetValue; - - static bool TryConvert(object o, out T t) - { - if (o is T tt) - { - t = tt; - return true; - } - - t = default; - return o is null; - } + t = default; + return o is null; } + } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + if (_convertBack is not null && value is TTo to) { - if (_convertBack is not null && value is TTo to) - { - var values = _convertBack(to); - return new object[] { values.Item1, values.Item2, values.Item3, values.Item4 }; - } - - return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue, DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; + var values = _convertBack(to); + return new object[] { values.Item1, values.Item2, values.Item3, values.Item4 }; } + + return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue, DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs index c0fc88127c..fba86d830c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/WPF/Converters/Converters.cs @@ -5,97 +5,96 @@ using System.Windows; using System.Windows.Data; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +internal sealed class MultiValueBoolToBool_And : IMultiValueConverter { - internal sealed class MultiValueBoolToBool_And : IMultiValueConverter - { - private static readonly object s_false = false; - private static readonly object s_true = true; + private static readonly object s_false = false; + private static readonly object s_true = true; - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + foreach (object v in values) { - foreach (object v in values) + // Any false, the result is false + if (s_false.Equals(v)) { - // Any false, the result is false - if (s_false.Equals(v)) - { - return s_false; - } + return s_false; } - - return s_true; } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotImplementedException("ConvertBack should NOT be invoked"); - } + return s_true; } - internal sealed class InverseBooleanConverter : IValueConverter + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return !(bool)value; - } + throw new NotImplementedException("ConvertBack should NOT be invoked"); + } +} - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return !(bool)value; - } +internal sealed class InverseBooleanConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return !(bool)value; } - // Copied from CPS Converters.cs - internal static partial class Converters + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - private const string DimensionSeparator = " & "; + return !(bool)value; + } +} - public static ImmutableArray ImmutableStringArray => ImmutableArray.Empty; +// Copied from CPS Converters.cs +internal static partial class Converters +{ + private const string DimensionSeparator = " & "; + + public static ImmutableArray ImmutableStringArray => ImmutableArray.Empty; - public static IMultiValueConverter DimensionNames { get; } = new LambdaMultiConverter, ImmutableArray, string>(GetDimensionNames); + public static IMultiValueConverter DimensionNames { get; } = new LambdaMultiConverter, ImmutableArray, string>(GetDimensionNames); - public static IValueConverter BoolToVisibilityConverter { get; } = new LambdaConverter(b => b ? Visibility.Visible : Visibility.Collapsed); + public static IValueConverter BoolToVisibilityConverter { get; } = new LambdaConverter(b => b ? Visibility.Visible : Visibility.Collapsed); - public static IValueConverter IsEnteredUserAliasStringEnabled { get; } = new LambdaConverter(isStatic => !isStatic); + public static IValueConverter IsEnteredUserAliasStringEnabled { get; } = new LambdaConverter(isStatic => !isStatic); - public static IMultiValueConverter IsListViewAliasStringEnabled { get; } = new LambdaMultiConverter((isReadOnly, isStatic) => !isReadOnly && !isStatic); + public static IMultiValueConverter IsListViewAliasStringEnabled { get; } = new LambdaMultiConverter((isReadOnly, isStatic) => !isReadOnly && !isStatic); - public static IMultiValueConverter IsIsStaticCheckboxEnabled { get; } = new LambdaMultiConverter((alias, isReadOnly) => !isReadOnly && alias.Length == 0); + public static IMultiValueConverter IsIsStaticCheckboxEnabled { get; } = new LambdaMultiConverter((alias, isReadOnly) => !isReadOnly && alias.Length == 0); - public static IValueConverter IsStaticCheckboxText { get; } = new LambdaConverter(alias => alias.Length == 0 ? "Yes" : "No"); + public static IValueConverter IsStaticCheckboxText { get; } = new LambdaConverter(alias => alias.Length == 0 ? "Yes" : "No"); - public static IValueConverter InvertBoolean { get; } = new LambdaConverter(b => !b); + public static IValueConverter InvertBoolean { get; } = new LambdaConverter(b => !b); - private static string GetDimensionNames(ImmutableDictionary map, ImmutableArray dimensions) + private static string GetDimensionNames(ImmutableDictionary map, ImmutableArray dimensions) + { + if (map.IsEmpty || dimensions.IsEmpty) { - if (map.IsEmpty || dimensions.IsEmpty) - { - return string.Empty; - } + return string.Empty; + } - if (map.Count == 1) - { - return map.First().Value; - } + if (map.Count == 1) + { + return map.First().Value; + } - var sb = new StringBuilder(); + var sb = new StringBuilder(); - foreach (string? dimension in dimensions) + foreach (string? dimension in dimensions) + { + if (!map.TryGetValue(dimension, out string? value)) { - if (!map.TryGetValue(dimension, out string? value)) - { - continue; - } - - if (sb.Length != 0) - { - sb.Append(DimensionSeparator); - } + continue; + } - sb.Append(value); + if (sb.Length != 0) + { + sb.Append(DimensionSeparator); } - return sb.ToString(); + sb.Append(value); } + + return sb.ToString(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Annotations.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Annotations.cs index 537fa4005e..f48a408864 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Annotations.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Annotations.cs @@ -5,146 +5,145 @@ // These are copies of the annotation types in the .NET Core BCL; we define them here so we can use them in .NET Framework. #if NETFRAMEWORK -namespace System.Diagnostics.CodeAnalysis +namespace System.Diagnostics.CodeAnalysis; + +/// Specifies that null is allowed as an input even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class AllowNullAttribute : Attribute +{ } + +/// Specifies that null is disallowed as an input even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +internal sealed class DisallowNullAttribute : Attribute +{ } + +/// Specifies that an output may be null even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class MaybeNullAttribute : Attribute +{ } + +/// Specifies that an output will not be null even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +internal sealed class NotNullAttribute : Attribute +{ } + +/// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class MaybeNullWhenAttribute : Attribute { - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute - { } - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class DisallowNullAttribute : Attribute - { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute - { } - - /// Specifies that an output will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute - { } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } +/// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class NotNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} - /// Specifies that the output will be non-null if the named parameter is non-null. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } +/// Specifies that the output will be non-null if the named parameter is non-null. +[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +internal sealed class NotNullIfNotNullAttribute : Attribute +{ + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } +} - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { } +/// Applied to a method that will never return under any circumstance. +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +internal sealed class DoesNotReturnAttribute : Attribute +{ } - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } - } +/// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class DoesNotReturnIfAttribute : Attribute +{ + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } +} + +/// Specifies that the method or property will ensure that the listed field and property members have not-null values. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +internal sealed class MemberNotNullAttribute : Attribute +{ + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } +} - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute +/// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +internal sealed class MemberNotNullWhenAttribute : Attribute +{ + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } + ReturnValue = returnValue; + Members = new[] { member }; } - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } + ReturnValue = returnValue; + Members = members; } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } } #endif diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildExtensions.cs index 087211fa4d..82ddd67f2e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildExtensions.cs @@ -3,21 +3,20 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; -namespace Microsoft.VisualStudio.Build +namespace Microsoft.VisualStudio.Build; + +internal static class BuildExtensions { - internal static class BuildExtensions + /// + /// Gets the unescaped, unevaluated value. + /// + /// + /// is . + /// + public static string GetUnescapedValue(this ProjectPropertyElement element) { - /// - /// Gets the unescaped, unevaluated value. - /// - /// - /// is . - /// - public static string GetUnescapedValue(this ProjectPropertyElement element) - { - Requires.NotNull(element); + Requires.NotNull(element); - return ProjectCollection.Unescape(element.Value); - } + return ProjectCollection.Unescape(element.Value); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildUtilities.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildUtilities.cs index ef1d32ee94..ec71c7dd97 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildUtilities.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Build/BuildUtilities.cs @@ -5,230 +5,229 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.Build +namespace Microsoft.VisualStudio.Build; + +/// +/// Utility class to manipulate MsBuild projects. +/// +internal static class BuildUtilities { + private const char Delimiter = ';'; + /// - /// Utility class to manipulate MsBuild projects. + /// Returns a value indicating whether the specified property has a condition that + /// always evaluates to . /// - internal static class BuildUtilities + public static bool HasWellKnownConditionsThatAlwaysEvaluateToTrue(ProjectPropertyElement element) { - private const char Delimiter = ';'; + Requires.NotNull(element); + + // We look for known conditions that evaluate to true so that + // projects can have patterns where they opt in/out of target + // frameworks based on whether they are inside Visual Studio or not. + + // For example: + // + // net461;net452 + // net461 + + return element.Condition is + "" or + "true" or + "'$(OS)' == 'Windows_NT'" or + "'$(BuildingInsideVisualStudio)' == 'true'"; + } - /// - /// Returns a value indicating whether the specified property has a condition that - /// always evaluates to . - /// - public static bool HasWellKnownConditionsThatAlwaysEvaluateToTrue(ProjectPropertyElement element) - { - Requires.NotNull(element); - - // We look for known conditions that evaluate to true so that - // projects can have patterns where they opt in/out of target - // frameworks based on whether they are inside Visual Studio or not. - - // For example: - // - // net461;net452 - // net461 - - return element.Condition is - "" or - "true" or - "'$(OS)' == 'Windows_NT'" or - "'$(BuildingInsideVisualStudio)' == 'true'"; - } + /// + /// Gets a project property. + /// + /// Xml representation of the MsBuild project. + /// Name of the property. + /// Requested project property. Null if the property is not present. + public static ProjectPropertyElement? GetProperty(ProjectRootElement project, string propertyName) + { + Requires.NotNull(project); - /// - /// Gets a project property. - /// - /// Xml representation of the MsBuild project. - /// Name of the property. - /// Requested project property. Null if the property is not present. - public static ProjectPropertyElement? GetProperty(ProjectRootElement project, string propertyName) - { - Requires.NotNull(project); + return project.Properties + .FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparisons.PropertyNames)); + } - return project.Properties - .FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparisons.PropertyNames)); - } + /// + /// Gets the individual values of a delimited property. + /// + /// Value of the property to evaluate. + /// Collection of individual values in the property. + public static IEnumerable GetPropertyValues(string propertyValue) + { + HashSet? seen = null; - /// - /// Gets the individual values of a delimited property. - /// - /// Value of the property to evaluate. - /// Collection of individual values in the property. - public static IEnumerable GetPropertyValues(string propertyValue) + // We need to ensure that we return values in the specified order. + foreach (string value in new LazyStringSplit(propertyValue, Delimiter)) { - HashSet? seen = null; + string s = value.Trim(); - // We need to ensure that we return values in the specified order. - foreach (string value in new LazyStringSplit(propertyValue, Delimiter)) + if (!string.IsNullOrEmpty(s)) { - string s = value.Trim(); - - if (!string.IsNullOrEmpty(s)) + if (seen is null) + { + seen = new HashSet(StringComparers.PropertyValues) { s }; + yield return s; + } + else { - if (seen is null) + if (seen.Add(s)) { - seen = new HashSet(StringComparers.PropertyValues) { s }; yield return s; } - else - { - if (seen.Add(s)) - { - yield return s; - } - } } } } + } - /// - /// Appends a value to a delimited property. If the property does not exist it will be added. - /// - /// Xml representation of the MsBuild project. - /// Original evaluated value of the property. - /// Property name. - /// Value to add to the property. - public static void AppendPropertyValue(ProjectRootElement project, string evaluatedPropertyValue, string propertyName, string valueToAppend) - { - Requires.NotNull(project); - Requires.NotNull(evaluatedPropertyValue); - Requires.NotNullOrEmpty(propertyName); - - ProjectPropertyElement property = GetOrAddProperty(project, propertyName); - var newValue = PooledStringBuilder.GetInstance(); - foreach (string value in GetPropertyValues(evaluatedPropertyValue)) - { - newValue.Append(value); - newValue.Append(Delimiter); - } + /// + /// Appends a value to a delimited property. If the property does not exist it will be added. + /// + /// Xml representation of the MsBuild project. + /// Original evaluated value of the property. + /// Property name. + /// Value to add to the property. + public static void AppendPropertyValue(ProjectRootElement project, string evaluatedPropertyValue, string propertyName, string valueToAppend) + { + Requires.NotNull(project); + Requires.NotNull(evaluatedPropertyValue); + Requires.NotNullOrEmpty(propertyName); - newValue.Append(valueToAppend); - property.Value = newValue.ToStringAndFree(); + ProjectPropertyElement property = GetOrAddProperty(project, propertyName); + var newValue = PooledStringBuilder.GetInstance(); + foreach (string value in GetPropertyValues(evaluatedPropertyValue)) + { + newValue.Append(value); + newValue.Append(Delimiter); } - /// - /// Renames a value inside a delimited property. - /// - /// Xml representation of the MsBuild project. - /// Original evaluated value of the property. - /// Property name. - /// Value to remove from the property. - /// - /// If the property is not present it will be added. This means that the evaluated property - /// value came from one of the project imports. - /// - public static void RemovePropertyValue(ProjectRootElement project, string evaluatedPropertyValue, string propertyName, string valueToRemove) + newValue.Append(valueToAppend); + property.Value = newValue.ToStringAndFree(); + } + + /// + /// Renames a value inside a delimited property. + /// + /// Xml representation of the MsBuild project. + /// Original evaluated value of the property. + /// Property name. + /// Value to remove from the property. + /// + /// If the property is not present it will be added. This means that the evaluated property + /// value came from one of the project imports. + /// + public static void RemovePropertyValue(ProjectRootElement project, string evaluatedPropertyValue, string propertyName, string valueToRemove) + { + Requires.NotNull(project); + Requires.NotNull(evaluatedPropertyValue); + Requires.NotNullOrEmpty(propertyName); + + ProjectPropertyElement property = GetOrAddProperty(project, propertyName); + var newValue = new StringBuilder(); + bool valueFound = false; + foreach (string value in GetPropertyValues(evaluatedPropertyValue)) { - Requires.NotNull(project); - Requires.NotNull(evaluatedPropertyValue); - Requires.NotNullOrEmpty(propertyName); - - ProjectPropertyElement property = GetOrAddProperty(project, propertyName); - var newValue = new StringBuilder(); - bool valueFound = false; - foreach (string value in GetPropertyValues(evaluatedPropertyValue)) + if (!string.Equals(value, valueToRemove, StringComparisons.PropertyValues)) { - if (!string.Equals(value, valueToRemove, StringComparisons.PropertyValues)) - { - if (newValue.Length != 0) - { - newValue.Append(Delimiter); - } - - newValue.Append(value); - } - else + if (newValue.Length != 0) { - valueFound = true; + newValue.Append(Delimiter); } - } - property.Value = newValue.ToString(); - - if (!valueFound) + newValue.Append(value); + } + else { - throw new ArgumentException(string.Format(Resources.MsBuildMissingValueToRemove, valueToRemove, propertyName), nameof(valueToRemove)); + valueFound = true; } } - /// - /// Renames a value inside a delimited property. - /// - /// Xml representation of the MsBuild project. - /// Original evaluated value of the property. - /// Property name. - /// Current property value. - /// New property value. - /// - /// If the property is not present it will be added. This means that the evaluated property - /// value came from one of the project imports. - /// - public static void RenamePropertyValue(ProjectRootElement project, string evaluatedPropertyValue, string propertyName, string? oldValue, string newValue) + property.Value = newValue.ToString(); + + if (!valueFound) { - Requires.NotNull(project); - Requires.NotNull(evaluatedPropertyValue); - Requires.NotNullOrEmpty(propertyName); - - ProjectPropertyElement property = GetOrAddProperty(project, propertyName); - var value = new StringBuilder(); - bool valueFound = false; - foreach (string propertyValue in GetPropertyValues(evaluatedPropertyValue)) - { - if (value.Length != 0) - { - value.Append(Delimiter); - } + throw new ArgumentException(string.Format(Resources.MsBuildMissingValueToRemove, valueToRemove, propertyName), nameof(valueToRemove)); + } + } - if (string.Equals(propertyValue, oldValue, StringComparisons.PropertyValues)) - { - value.Append(newValue); - valueFound = true; - } - else - { - value.Append(propertyValue); - } + /// + /// Renames a value inside a delimited property. + /// + /// Xml representation of the MsBuild project. + /// Original evaluated value of the property. + /// Property name. + /// Current property value. + /// New property value. + /// + /// If the property is not present it will be added. This means that the evaluated property + /// value came from one of the project imports. + /// + public static void RenamePropertyValue(ProjectRootElement project, string evaluatedPropertyValue, string propertyName, string? oldValue, string newValue) + { + Requires.NotNull(project); + Requires.NotNull(evaluatedPropertyValue); + Requires.NotNullOrEmpty(propertyName); + + ProjectPropertyElement property = GetOrAddProperty(project, propertyName); + var value = new StringBuilder(); + bool valueFound = false; + foreach (string propertyValue in GetPropertyValues(evaluatedPropertyValue)) + { + if (value.Length != 0) + { + value.Append(Delimiter); } - property.Value = value.ToString(); - - if (!valueFound) + if (string.Equals(propertyValue, oldValue, StringComparisons.PropertyValues)) { - throw new ArgumentException(string.Format(Resources.MsBuildMissingValueToRename, oldValue, propertyName), nameof(oldValue)); + value.Append(newValue); + valueFound = true; + } + else + { + value.Append(propertyValue); } } - /// - /// Adds a property to the first property group. If there are no property groups it will create one. - /// - /// Xml representation of the MsBuild project. - /// Property name. - public static ProjectPropertyElement GetOrAddProperty(ProjectRootElement project, string propertyName) + property.Value = value.ToString(); + + if (!valueFound) { - Requires.NotNull(project); - ProjectPropertyElement? property = GetProperty(project, propertyName); + throw new ArgumentException(string.Format(Resources.MsBuildMissingValueToRename, oldValue, propertyName), nameof(oldValue)); + } + } + + /// + /// Adds a property to the first property group. If there are no property groups it will create one. + /// + /// Xml representation of the MsBuild project. + /// Property name. + public static ProjectPropertyElement GetOrAddProperty(ProjectRootElement project, string propertyName) + { + Requires.NotNull(project); + ProjectPropertyElement? property = GetProperty(project, propertyName); - if (property is not null) + if (property is not null) + { + return property; + } + else + { + ProjectPropertyGroupElement propertyGroup; + if (project.PropertyGroups.Count == 0) { - return property; + propertyGroup = project.AddPropertyGroup(); } else { - ProjectPropertyGroupElement propertyGroup; - if (project.PropertyGroups.Count == 0) - { - propertyGroup = project.AddPropertyGroup(); - } - else - { - propertyGroup = project.PropertyGroups.First(); - } - - return propertyGroup.AddProperty(propertyName, string.Empty); + propertyGroup = project.PropertyGroups.First(); } + + return propertyGroup.AddProperty(propertyName, string.Empty); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ConcurrentHashSet.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ConcurrentHashSet.cs index 8688b1f041..e085ed4f47 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ConcurrentHashSet.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ConcurrentHashSet.cs @@ -2,67 +2,66 @@ using Microsoft; -namespace System.Collections.Concurrent +namespace System.Collections.Concurrent; + +internal class ConcurrentHashSet : IConcurrentHashSet where T: notnull { - internal class ConcurrentHashSet : IConcurrentHashSet where T: notnull + private static readonly object s_hashSetObject = new(); + + private readonly ConcurrentDictionary _map; + + public ConcurrentHashSet() + : this(EqualityComparer.Default) { - private static readonly object s_hashSetObject = new(); + } - private readonly ConcurrentDictionary _map; + public ConcurrentHashSet(IEqualityComparer comparer) + { + _map = new(comparer); + } - public ConcurrentHashSet() - : this(EqualityComparer.Default) - { - } + public int Count => _map.Count; - public ConcurrentHashSet(IEqualityComparer comparer) - { - _map = new(comparer); - } + public IEnumerator GetEnumerator() + { + return _map.Keys.GetEnumerator(); + } - public int Count => _map.Count; + IEnumerator IEnumerable.GetEnumerator() + { + return ((ConcurrentHashSet)this).GetEnumerator(); + } - public IEnumerator GetEnumerator() - { - return _map.Keys.GetEnumerator(); - } + public bool Add(T item) + { + return _map.TryAdd(item, s_hashSetObject); + } - IEnumerator IEnumerable.GetEnumerator() - { - return ((ConcurrentHashSet)this).GetEnumerator(); - } + public bool AddRange(IEnumerable elements) + { + Requires.NotNull(elements); - public bool Add(T item) + bool changed = false; + foreach (var element in elements) { - return _map.TryAdd(item, s_hashSetObject); + changed |= _map.TryAdd(element, s_hashSetObject); } - public bool AddRange(IEnumerable elements) - { - Requires.NotNull(elements); - - bool changed = false; - foreach (var element in elements) - { - changed |= _map.TryAdd(element, s_hashSetObject); - } - - return changed; - } + return changed; + } - public bool Contains(T item) - { - return _map.ContainsKey(item); - } + public bool Contains(T item) + { + return _map.ContainsKey(item); + } - public bool Remove(T item) - { - return _map.TryRemove(item, out _); - } + public bool Remove(T item) + { + return _map.TryRemove(item, out _); + } - public void Clear() - { - _map.Clear(); - } + public void Clear() + { + _map.Clear(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/DictionaryEqualityComparer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/DictionaryEqualityComparer.cs index 1678130ab2..af35d468b6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/DictionaryEqualityComparer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/DictionaryEqualityComparer.cs @@ -1,96 +1,95 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Collections +namespace Microsoft.VisualStudio.Collections; + +/// +/// Compares instances for equality of keys and values. +/// Also compares equality of item order. +/// +/// The type of key in the dictionaries to compare. +/// The type of value in the dictionaries to compare. +internal sealed class DictionaryEqualityComparer : IEqualityComparer> + where TKey : notnull { /// - /// Compares instances for equality of keys and values. - /// Also compares equality of item order. + /// Initializes a new instance of the class. /// - /// The type of key in the dictionaries to compare. - /// The type of value in the dictionaries to compare. - internal sealed class DictionaryEqualityComparer : IEqualityComparer> - where TKey : notnull + private DictionaryEqualityComparer() { - /// - /// Initializes a new instance of the class. - /// - private DictionaryEqualityComparer() + } + + /// + /// Gets a dictionary equality comparer instance appropriate for dictionaries that use the default key comparer for the type. + /// + internal static DictionaryEqualityComparer Instance { get; } = new(); + + /// + /// Checks two dictionaries for equality. + /// + public bool Equals(Dictionary? x, Dictionary? y) + { + if (ReferenceEquals(x, y)) { + return true; } - /// - /// Gets a dictionary equality comparer instance appropriate for dictionaries that use the default key comparer for the type. - /// - internal static DictionaryEqualityComparer Instance { get; } = new(); - - /// - /// Checks two dictionaries for equality. - /// - public bool Equals(Dictionary? x, Dictionary? y) + if (x is null || y is null) { - if (ReferenceEquals(x, y)) - { - return true; - } + return false; + } - if (x is null || y is null) - { - return false; - } + if (x.Count != y.Count) + { + return false; + } - if (x.Count != y.Count) - { - return false; - } + if (x.Count == 0) + { + // both dictionaries are empty, so bail out early to avoid + // enumerator allocation + return true; + } - if (x.Count == 0) - { - // both dictionaries are empty, so bail out early to avoid - // enumerator allocation - return true; - } + // NOTE this uses the comparer from x, not from y + IEqualityComparer keyComparer = x.Comparer ?? EqualityComparer.Default; + IEqualityComparer valueComparer = EqualityComparer.Default; - // NOTE this uses the comparer from x, not from y - IEqualityComparer keyComparer = x.Comparer ?? EqualityComparer.Default; - IEqualityComparer valueComparer = EqualityComparer.Default; + // Compare items based on their order, as we care about preserving order in launch profiles + using Dictionary.Enumerator enumerator1 = x.GetEnumerator(); + using Dictionary.Enumerator enumerator2 = y.GetEnumerator(); - // Compare items based on their order, as we care about preserving order in launch profiles - using Dictionary.Enumerator enumerator1 = x.GetEnumerator(); - using Dictionary.Enumerator enumerator2 = y.GetEnumerator(); + while (enumerator1.MoveNext() && enumerator2.MoveNext()) + { + (TKey key1, TValue value1) = enumerator1.Current; + (TKey key2, TValue value2) = enumerator2.Current; - while (enumerator1.MoveNext() && enumerator2.MoveNext()) + if (!keyComparer.Equals(key1, key2) || !valueComparer.Equals(value1, value2)) { - (TKey key1, TValue value1) = enumerator1.Current; - (TKey key2, TValue value2) = enumerator2.Current; - - if (!keyComparer.Equals(key1, key2) || !valueComparer.Equals(value1, value2)) - { - return false; - } + return false; } - - return true; } - /// - /// Calculates a hash code for a dictionary. - /// - public int GetHashCode(Dictionary obj) + return true; + } + + /// + /// Calculates a hash code for a dictionary. + /// + public int GetHashCode(Dictionary obj) + { + int hashCode = 0; + + if (obj is not null) { - int hashCode = 0; + IEqualityComparer keyComparer = obj.Comparer ?? EqualityComparer.Default; + IEqualityComparer valueComparer = EqualityComparer.Default; - if (obj is not null) + foreach ((TKey key, TValue value) in obj) { - IEqualityComparer keyComparer = obj.Comparer ?? EqualityComparer.Default; - IEqualityComparer valueComparer = EqualityComparer.Default; - - foreach ((TKey key, TValue value) in obj) - { - hashCode += keyComparer.GetHashCode(key) ^ valueComparer.GetHashCode(value); - } + hashCode += keyComparer.GetHashCode(key) ^ valueComparer.GetHashCode(value); } - - return hashCode; } + + return hashCode; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/EnumerableExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/EnumerableExtensions.cs index 0d295c743e..f2a969de7b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/EnumerableExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/EnumerableExtensions.cs @@ -1,79 +1,78 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Collections +namespace Microsoft.VisualStudio.Collections; + +internal static class EnumerableExtensions { - internal static class EnumerableExtensions + // + // Summary: + // Creates a System.Collections.Generic.Dictionary`2 from an System.Collections.Generic.IEnumerable`1 + // according to a specified key selector function, a comparer, and an element selector + // function. Unlike Enumerable.ToDictionary, this implementation allows callers to specify whether + // duplicate keys produced by the keySelector should not result in an ArgumentException + // + // Parameters: + // source: + // An System.Collections.Generic.IEnumerable`1 to create a System.Collections.Generic.Dictionary`2 + // from. + // + // keySelector: + // A function to extract a key from each element. + // + // elementSelector: + // A transform function to produce a result element value from each element. + // + // comparer: + // An System.Collections.Generic.IEqualityComparer`1 to compare keys. + // + // ignoreDuplicateKeys: + // A flag indicating whether an ArgumentException should be thrown if the keySelector produces duplicate keys. + // + // Type parameters: + // TSource: + // The type of the elements of source. + // + // TKey: + // The type of the key returned by keySelector. + // + // TElement: + // The type of the value returned by elementSelector. + // + // Returns: + // A System.Collections.Generic.Dictionary`2 that contains values of type TElement + // selected from the input sequence. + // + // Exceptions: + // T:System.ArgumentNullException: + // source or keySelector or elementSelector is null.-or- keySelector produces a + // key that is null. + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, bool ignoreDuplicateKeys) where TKey: notnull { - // - // Summary: - // Creates a System.Collections.Generic.Dictionary`2 from an System.Collections.Generic.IEnumerable`1 - // according to a specified key selector function, a comparer, and an element selector - // function. Unlike Enumerable.ToDictionary, this implementation allows callers to specify whether - // duplicate keys produced by the keySelector should not result in an ArgumentException - // - // Parameters: - // source: - // An System.Collections.Generic.IEnumerable`1 to create a System.Collections.Generic.Dictionary`2 - // from. - // - // keySelector: - // A function to extract a key from each element. - // - // elementSelector: - // A transform function to produce a result element value from each element. - // - // comparer: - // An System.Collections.Generic.IEqualityComparer`1 to compare keys. - // - // ignoreDuplicateKeys: - // A flag indicating whether an ArgumentException should be thrown if the keySelector produces duplicate keys. - // - // Type parameters: - // TSource: - // The type of the elements of source. - // - // TKey: - // The type of the key returned by keySelector. - // - // TElement: - // The type of the value returned by elementSelector. - // - // Returns: - // A System.Collections.Generic.Dictionary`2 that contains values of type TElement - // selected from the input sequence. - // - // Exceptions: - // T:System.ArgumentNullException: - // source or keySelector or elementSelector is null.-or- keySelector produces a - // key that is null. - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer, bool ignoreDuplicateKeys) where TKey: notnull - { - if (!ignoreDuplicateKeys) - return source.ToDictionary(keySelector, elementSelector, comparer); - - if (source is null) - throw new ArgumentNullException(nameof(source)); + if (!ignoreDuplicateKeys) + return source.ToDictionary(keySelector, elementSelector, comparer); - if (keySelector is null) - throw new ArgumentNullException(nameof(keySelector)); + if (source is null) + throw new ArgumentNullException(nameof(source)); - if (elementSelector is null) - throw new ArgumentNullException(nameof(elementSelector)); - - Dictionary result = new(comparer); + if (keySelector is null) + throw new ArgumentNullException(nameof(keySelector)); - foreach (var item in source) - { - var key = keySelector(item); + if (elementSelector is null) + throw new ArgumentNullException(nameof(elementSelector)); + + Dictionary result = new(comparer); - if (key is null) - throw new ArgumentNullException(nameof(key)); + foreach (var item in source) + { + var key = keySelector(item); - var element = elementSelector(item); - result[key] = element; - } + if (key is null) + throw new ArgumentNullException(nameof(key)); - return result; + var element = elementSelector(item); + result[key] = element; } + + return result; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/IConcurrentHashSet.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/IConcurrentHashSet.cs index c74c1d6711..22bc29b1e1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/IConcurrentHashSet.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/IConcurrentHashSet.cs @@ -1,56 +1,55 @@ // 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.md file in the project root for more information. -namespace System.Collections.Concurrent +namespace System.Collections.Concurrent; + +internal interface IConcurrentHashSet : IEnumerable { - internal interface IConcurrentHashSet : IEnumerable - { - /// - /// The number of elements contained in the System.Collections.Concurrent.IConcurrentHashSet`1. - /// - int Count { get; } + /// + /// The number of elements contained in the System.Collections.Concurrent.IConcurrentHashSet`1. + /// + int Count { get; } - /// - /// Adds an item to the System.Collections.Concurrent.IConcurrentHashSet`1. - /// - /// The object to add to the System.Collections.Concurrent.IConcurrentHashSet`1 - /// - /// true if the item was added to the System.Collections.Concurrent.IConcurrentHashSet`1; otherwise, false - /// - bool Add(T item); + /// + /// Adds an item to the System.Collections.Concurrent.IConcurrentHashSet`1. + /// + /// The object to add to the System.Collections.Concurrent.IConcurrentHashSet`1 + /// + /// true if the item was added to the System.Collections.Concurrent.IConcurrentHashSet`1; otherwise, false + /// + bool Add(T item); - /// - /// Determines whether the System.Collections.Concurrent.IConcurrentHashSet`1 contains the specified item. - /// - /// The item to locate in the System.Collections.Concurrent.IConcurrentHashSet`1. - /// - /// true if the System.Collections.Concurrent.IConcurrentHashSet`1 contains the item; otherwise, false. - /// - bool Contains(T item); + /// + /// Determines whether the System.Collections.Concurrent.IConcurrentHashSet`1 contains the specified item. + /// + /// The item to locate in the System.Collections.Concurrent.IConcurrentHashSet`1. + /// + /// true if the System.Collections.Concurrent.IConcurrentHashSet`1 contains the item; otherwise, false. + /// + bool Contains(T item); - /// - /// Removes a specific object from the System.Collections.Concurrent.IConcurrentHashSet`1 - /// - /// The object to remove from the System.Collections.Concurrent.IConcurrentHashSet`1. - /// - /// true if item was successfully removed from the System.Collections.Concurrent.IConcurrentHashSet`1; - /// otherwise, false. This method also returns false if item is not found in the - /// System.Collections.Concurrent.IConcurrentHashSet`1. - /// - bool Remove(T item); + /// + /// Removes a specific object from the System.Collections.Concurrent.IConcurrentHashSet`1 + /// + /// The object to remove from the System.Collections.Concurrent.IConcurrentHashSet`1. + /// + /// true if item was successfully removed from the System.Collections.Concurrent.IConcurrentHashSet`1; + /// otherwise, false. This method also returns false if item is not found in the + /// System.Collections.Concurrent.IConcurrentHashSet`1. + /// + bool Remove(T item); - /// - /// Adds all the elements in a sequence to the set. - /// - /// The objects to add to the System.Collections.Concurrent.IConcurrentHashSet`1 - /// - /// true if at least one item was added to the System.Collections.Concurrent.IConcurrentHashSet`1; - /// otherwise, false - /// - bool AddRange(IEnumerable elements); + /// + /// Adds all the elements in a sequence to the set. + /// + /// The objects to add to the System.Collections.Concurrent.IConcurrentHashSet`1 + /// + /// true if at least one item was added to the System.Collections.Concurrent.IConcurrentHashSet`1; + /// otherwise, false + /// + bool AddRange(IEnumerable elements); - /// - /// Removes all elements from System.Collections.Concurrent.IConcurrentHashSet`1 - /// - void Clear(); - } + /// + /// Removes all elements from System.Collections.Concurrent.IConcurrentHashSet`1 + /// + void Clear(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringDictionary.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringDictionary.cs index 1d697b6861..3a7d703885 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringDictionary.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringDictionary.cs @@ -1,13 +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.md file in the project root for more information. -namespace System.Collections.Immutable +namespace System.Collections.Immutable; + +internal static class ImmutableStringDictionary { - internal static class ImmutableStringDictionary - { - public static readonly ImmutableDictionary EmptyOrdinal - = ImmutableDictionary.Empty; + public static readonly ImmutableDictionary EmptyOrdinal + = ImmutableDictionary.Empty; - public static readonly ImmutableDictionary EmptyOrdinalIgnoreCase - = ImmutableDictionary.Empty.WithComparers(StringComparer.OrdinalIgnoreCase); - } + public static readonly ImmutableDictionary EmptyOrdinalIgnoreCase + = ImmutableDictionary.Empty.WithComparers(StringComparer.OrdinalIgnoreCase); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringHashSet.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringHashSet.cs index eb403f99a2..79184038d3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringHashSet.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Collections/ImmutableStringHashSet.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio; -namespace System.Collections.Immutable +namespace System.Collections.Immutable; + +internal static class ImmutableStringHashSet { - internal static class ImmutableStringHashSet - { - public static readonly ImmutableHashSet EmptyOrdinal = ImmutableHashSet.Empty; + public static readonly ImmutableHashSet EmptyOrdinal = ImmutableHashSet.Empty; - public static readonly ImmutableHashSet EmptyOrdinalIgnoreCase = ImmutableHashSet.Empty.WithComparer(StringComparer.OrdinalIgnoreCase); + public static readonly ImmutableHashSet EmptyOrdinalIgnoreCase = ImmutableHashSet.Empty.WithComparer(StringComparer.OrdinalIgnoreCase); - // For semantic string comparers. + // For semantic string comparers. - public static readonly ImmutableHashSet EmptyRuleNames = ImmutableHashSet.Create(StringComparers.RuleNames); + public static readonly ImmutableHashSet EmptyRuleNames = ImmutableHashSet.Create(StringComparers.RuleNames); - public static readonly ImmutableHashSet EmptyVisualStudioSetupComponentIds = ImmutableHashSet.Create(StringComparers.VisualStudioSetupComponentIds); - } + public static readonly ImmutableHashSet EmptyVisualStudioSetupComponentIds = ImmutableHashSet.Create(StringComparers.VisualStudioSetupComponentIds); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ComparableExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ComparableExtensions.cs index 1c6f70f4ac..01a765b1de 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ComparableExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ComparableExtensions.cs @@ -1,86 +1,85 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +/// +/// Provides extensions for instances. +/// +internal static class ComparableExtensions { /// - /// Provides extensions for instances. + /// Returns a value indicating whether the current instance is later than the + /// specified instance. /// - internal static class ComparableExtensions + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static bool IsLaterThan(this IComparable source, IComparable comparable) { - /// - /// Returns a value indicating whether the current instance is later than the - /// specified instance. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static bool IsLaterThan(this IComparable source, IComparable comparable) - { - Requires.NotNull(source); - Requires.NotNull(comparable); + Requires.NotNull(source); + Requires.NotNull(comparable); - return source.CompareTo(comparable) > 0; - } + return source.CompareTo(comparable) > 0; + } - /// - /// Returns a value indicating whether the current instance is later than or - /// equal to the specified instance. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static bool IsLaterThanOrEqualTo(this IComparable source, IComparable comparable) - { - Requires.NotNull(source); - Requires.NotNull(comparable); + /// + /// Returns a value indicating whether the current instance is later than or + /// equal to the specified instance. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static bool IsLaterThanOrEqualTo(this IComparable source, IComparable comparable) + { + Requires.NotNull(source); + Requires.NotNull(comparable); - return source.CompareTo(comparable) >= 0; - } + return source.CompareTo(comparable) >= 0; + } - /// - /// Returns a value indicating whether the current instance is earlier than the - /// specified instance. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static bool IsEarlierThan(this IComparable source, IComparable comparable) - { - Requires.NotNull(source); - Requires.NotNull(comparable); + /// + /// Returns a value indicating whether the current instance is earlier than the + /// specified instance. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static bool IsEarlierThan(this IComparable source, IComparable comparable) + { + Requires.NotNull(source); + Requires.NotNull(comparable); - return source.CompareTo(comparable) < 0; - } + return source.CompareTo(comparable) < 0; + } - /// - /// Returns a value indicating whether the current instance is earlier than or equal - /// to the specified instance. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static bool IsEarlierThanOrEqualTo(this IComparable source, IComparable comparable) - { - Requires.NotNull(source); - Requires.NotNull(comparable); + /// + /// Returns a value indicating whether the current instance is earlier than or equal + /// to the specified instance. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static bool IsEarlierThanOrEqualTo(this IComparable source, IComparable comparable) + { + Requires.NotNull(source); + Requires.NotNull(comparable); - return source.CompareTo(comparable) <= 0; - } + return source.CompareTo(comparable) <= 0; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Composition/OrderPrecedenceImportCollectionExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Composition/OrderPrecedenceImportCollectionExtensions.cs index 0d177b66e9..e599bec906 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Composition/OrderPrecedenceImportCollectionExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Composition/OrderPrecedenceImportCollectionExtensions.cs @@ -3,111 +3,110 @@ using Microsoft.VisualStudio.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.Composition +namespace Microsoft.VisualStudio.Composition; + +internal static class OrderPrecedenceImportCollectionExtensions { - internal static class OrderPrecedenceImportCollectionExtensions + /// + /// Produces the sequence of imports within a , + /// omitting any that throw MEF exceptions. + /// + /// The type of import. + /// The collection of imports. + /// + /// true to only enumerate imports whose values have previously been created. + /// This is useful in methods to avoid MEF + /// from accidentally creating values during a container disposal. + /// + /// The safely constructed sequence of extensions. + internal static IEnumerable ExtensionValues(this OrderPrecedenceImportCollection extensions, bool onlyCreatedValues = false) { - /// - /// Produces the sequence of imports within a , - /// omitting any that throw MEF exceptions. - /// - /// The type of import. - /// The collection of imports. - /// - /// true to only enumerate imports whose values have previously been created. - /// This is useful in methods to avoid MEF - /// from accidentally creating values during a container disposal. - /// - /// The safely constructed sequence of extensions. - internal static IEnumerable ExtensionValues(this OrderPrecedenceImportCollection extensions, bool onlyCreatedValues = false) - { - Requires.NotNull(extensions); - string traceErrorMessage = "Roslyn project system extension rejected due to exception: {0}"; + Requires.NotNull(extensions); + string traceErrorMessage = "Roslyn project system extension rejected due to exception: {0}"; - foreach (Lazy extension in extensions) + foreach (Lazy extension in extensions) + { + T value; + try { - T value; - try - { - if (onlyCreatedValues && !extension.IsValueCreated) - { - continue; - } - - value = extension.Value; - } - catch (CompositionContractMismatchException ex) + if (onlyCreatedValues && !extension.IsValueCreated) { - TraceUtilities.TraceError(traceErrorMessage, ex); - continue; - } - catch (CompositionException ex) - { - TraceUtilities.TraceError(traceErrorMessage, ex); continue; } - yield return value; + value = extension.Value; } - } - - public static T? FirstOrDefaultValue(this OrderPrecedenceImportCollection imports, Func predicate) where T : class - { - Requires.NotNull(imports); - - foreach (Lazy import in imports) + catch (CompositionContractMismatchException ex) { - T value = import.Value; - if (predicate(value)) - { - return value; - } + TraceUtilities.TraceError(traceErrorMessage, ex); + continue; + } + catch (CompositionException ex) + { + TraceUtilities.TraceError(traceErrorMessage, ex); + continue; } - return null; + yield return value; } + } - public static TImport? FirstOrDefaultValue(this OrderPrecedenceImportCollection imports, Func predicate, TArg arg) where TImport : class - { - Requires.NotNull(imports); + public static T? FirstOrDefaultValue(this OrderPrecedenceImportCollection imports, Func predicate) where T : class + { + Requires.NotNull(imports); - foreach (Lazy import in imports) + foreach (Lazy import in imports) + { + T value = import.Value; + if (predicate(value)) { - TImport value = import.Value; - if (predicate(value, arg)) - { - return value; - } + return value; } - - return null; } - public static ImmutableArray ToImmutableValueArray(this OrderPrecedenceImportCollection imports) - { - Requires.NotNull(imports); + return null; + } - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(imports.Count); + public static TImport? FirstOrDefaultValue(this OrderPrecedenceImportCollection imports, Func predicate, TArg arg) where TImport : class + { + Requires.NotNull(imports); - foreach (Lazy import in imports) + foreach (Lazy import in imports) + { + TImport value = import.Value; + if (predicate(value, arg)) { - builder.Add(import.Value); + return value; } - - return builder.MoveToImmutable(); } - public static Dictionary ToValueDictionary(this OrderPrecedenceImportCollection imports, Func keySelector, IEqualityComparer? comparer = null) where TKey: notnull + return null; + } + + public static ImmutableArray ToImmutableValueArray(this OrderPrecedenceImportCollection imports) + { + Requires.NotNull(imports); + + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(imports.Count); + + foreach (Lazy import in imports) { - var dictionary = new Dictionary(comparer); + builder.Add(import.Value); + } - foreach (Lazy import in imports) - { - TImport value = import.Value; - dictionary.Add(keySelector(value), value); - } + return builder.MoveToImmutable(); + } + + public static Dictionary ToValueDictionary(this OrderPrecedenceImportCollection imports, Func keySelector, IEqualityComparer? comparer = null) where TKey: notnull + { + var dictionary = new Dictionary(comparer); - return dictionary; + foreach (Lazy import in imports) + { + TImport value = import.Value; + dictionary.Add(keySelector(value), value); } + + return dictionary; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Delimiter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Delimiter.cs index 87191ee02e..8850070485 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Delimiter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Delimiter.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +/// +/// Contains commonly-used delimiters used for splitting instances. +/// +internal static class Delimiter { /// - /// Contains commonly-used delimiters used for splitting instances. + /// Single, static instance of an array that contains a semi-colon ';', which is used to split strings, etc. /// - internal static class Delimiter - { - /// - /// Single, static instance of an array that contains a semi-colon ';', which is used to split strings, etc. - /// - internal static readonly char[] Semicolon = new char[] { ';' }; + internal static readonly char[] Semicolon = new char[] { ';' }; - /// - /// Single, static instance of an array that contains a period '.', which is used to split strings, etc. - /// - internal static readonly char[] Period = new char[] { '.' }; + /// + /// Single, static instance of an array that contains a period '.', which is used to split strings, etc. + /// + internal static readonly char[] Period = new char[] { '.' }; - /// - /// Single, static instance of an array that contains '+' and '-' characters. - /// - internal static readonly char[] PlusAndMinus = new char[] { '+', '-' }; + /// + /// Single, static instance of an array that contains '+' and '-' characters. + /// + internal static readonly char[] PlusAndMinus = new char[] { '+', '-' }; - /// - /// Single, static instance of an array that contains platform-specific path separators - /// - internal static readonly char[] Path = new char[] - { - System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar - }; - } + /// + /// Single, static instance of an array that contains platform-specific path separators + /// + internal static readonly char[] Path = new char[] + { + System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar + }; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileExplorer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileExplorer.cs index 9b107ce6bc..30f0751ace 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileExplorer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileExplorer.cs @@ -2,19 +2,18 @@ using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IFileExplorer { - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IFileExplorer - { - /// - /// Opens the containing folder of the specified path in File Explorer, selecting the file if it exists. - /// - void OpenContainingFolder(string path); + /// + /// Opens the containing folder of the specified path in File Explorer, selecting the file if it exists. + /// + void OpenContainingFolder(string path); - /// - /// Opens the contents of the specified folder in File Explorer. - /// - void OpenFolder(string path); - } + /// + /// Opens the contents of the specified folder in File Explorer. + /// + void OpenFolder(string path); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileSystem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileSystem.cs index df3234739b..02b06148e3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileSystem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/IFileSystem.cs @@ -3,56 +3,55 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +/// +/// Provides static methods for the creation, copying, deletion, moving of files and directories. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IFileSystem { /// - /// Provides static methods for the creation, copying, deletion, moving of files and directories. + /// Creates or overwrites an empty file with the specified path. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IFileSystem - { - /// - /// Creates or overwrites an empty file with the specified path. - /// - void Create(string path); - - /// - /// Gets whether is a file or directory that exists on disk. - /// - bool PathExists(string path); - - /// - /// Gets whether is a file that exists on disk. - /// - bool FileExists(string path); - - void RemoveFile(string path); - void CopyFile(string source, string destination, bool overwrite, bool clearReadOnly = false); - Task ReadAllTextAsync(string path); - Stream OpenTextStream(string path); - Task WriteAllTextAsync(string path, string content); - - /// - /// Return the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to, - /// or if the path does not exist or is not accessible. - /// - DateTime GetLastFileWriteTimeOrMinValueUtc(string path); - - /// - /// Return the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to, - /// indicating if the path exists and is accessible. - /// - /// - /// if exists and is accessible; otherwise, . - /// - bool TryGetLastFileWriteTimeUtc(string path, [NotNullWhen(true)] out DateTime? result); - - bool TryGetFileSizeBytes(string path, out long result); - - (long SizeBytes, DateTime WriteTimeUtc)? GetFileSizeAndWriteTimeUtc(string path); - - bool DirectoryExists(string path); - void CreateDirectory(string path); - string GetFullPath(string path); - } + void Create(string path); + + /// + /// Gets whether is a file or directory that exists on disk. + /// + bool PathExists(string path); + + /// + /// Gets whether is a file that exists on disk. + /// + bool FileExists(string path); + + void RemoveFile(string path); + void CopyFile(string source, string destination, bool overwrite, bool clearReadOnly = false); + Task ReadAllTextAsync(string path); + Stream OpenTextStream(string path); + Task WriteAllTextAsync(string path, string content); + + /// + /// Return the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to, + /// or if the path does not exist or is not accessible. + /// + DateTime GetLastFileWriteTimeOrMinValueUtc(string path); + + /// + /// Return the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to, + /// indicating if the path exists and is accessible. + /// + /// + /// if exists and is accessible; otherwise, . + /// + bool TryGetLastFileWriteTimeUtc(string path, [NotNullWhen(true)] out DateTime? result); + + bool TryGetFileSizeBytes(string path, out long result); + + (long SizeBytes, DateTime WriteTimeUtc)? GetFileSizeAndWriteTimeUtc(string path); + + bool DirectoryExists(string path); + void CreateDirectory(string path); + string GetFullPath(string path); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/SimpleFileWatcher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/SimpleFileWatcher.cs index 0aea350d11..a9820fc0ac 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/SimpleFileWatcher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/SimpleFileWatcher.cs @@ -1,68 +1,67 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +/// +/// Simple wrapper around the FileSystemWatcher. +/// +internal sealed class SimpleFileWatcher : IDisposable { - /// - /// Simple wrapper around the FileSystemWatcher. - /// - internal sealed class SimpleFileWatcher : IDisposable + private FileSystemWatcher? _fileWatcher; + private FileSystemEventHandler? _handler; + private RenamedEventHandler? _renameHandler; + + // For unit tests + public SimpleFileWatcher() { - private FileSystemWatcher? _fileWatcher; - private FileSystemEventHandler? _handler; - private RenamedEventHandler? _renameHandler; + } - // For unit tests - public SimpleFileWatcher() + public SimpleFileWatcher(string dirToWatch, bool includeSubDirs, NotifyFilters notifyFilters, string fileFilter, + FileSystemEventHandler? handler, RenamedEventHandler? renameHandler) + { + _fileWatcher = new FileSystemWatcher(dirToWatch) { + IncludeSubdirectories = includeSubDirs, + NotifyFilter = notifyFilters, + Filter = fileFilter + }; + + if (handler is not null) + { + _fileWatcher.Created += handler; + _fileWatcher.Deleted += handler; + _fileWatcher.Changed += handler; } - public SimpleFileWatcher(string dirToWatch, bool includeSubDirs, NotifyFilters notifyFilters, string fileFilter, - FileSystemEventHandler? handler, RenamedEventHandler? renameHandler) + if (renameHandler is not null) { - _fileWatcher = new FileSystemWatcher(dirToWatch) - { - IncludeSubdirectories = includeSubDirs, - NotifyFilter = notifyFilters, - Filter = fileFilter - }; + _fileWatcher.Renamed += renameHandler; + } - if (handler is not null) - { - _fileWatcher.Created += handler; - _fileWatcher.Deleted += handler; - _fileWatcher.Changed += handler; - } + _fileWatcher.EnableRaisingEvents = true; + _handler = handler; + _renameHandler = renameHandler; + } - if (renameHandler is not null) + public void Dispose() + { + if (_fileWatcher is not null) + { + _fileWatcher.EnableRaisingEvents = false; + if (_handler is not null) { - _fileWatcher.Renamed += renameHandler; + _fileWatcher.Created -= _handler; + _fileWatcher.Deleted -= _handler; + _fileWatcher.Changed -= _handler; + _handler = null; } - - _fileWatcher.EnableRaisingEvents = true; - _handler = handler; - _renameHandler = renameHandler; - } - - public void Dispose() - { - if (_fileWatcher is not null) + if (_renameHandler is not null) { - _fileWatcher.EnableRaisingEvents = false; - if (_handler is not null) - { - _fileWatcher.Created -= _handler; - _fileWatcher.Deleted -= _handler; - _fileWatcher.Changed -= _handler; - _handler = null; - } - if (_renameHandler is not null) - { - _fileWatcher.Renamed -= _renameHandler; - _renameHandler = null; - } - _fileWatcher.Dispose(); - _fileWatcher = null; + _fileWatcher.Renamed -= _renameHandler; + _renameHandler = null; } + _fileWatcher.Dispose(); + _fileWatcher = null; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/Win32FileSystem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/Win32FileSystem.cs index 9338a77216..64dd1bf64d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/Win32FileSystem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/IO/Win32FileSystem.cs @@ -2,153 +2,152 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +/// +/// Provides an implementation of that calls through the +/// and classes, and ultimately through Win32 APIs. +/// +[Export(typeof(IFileSystem))] +internal class Win32FileSystem : IFileSystem { - /// - /// Provides an implementation of that calls through the - /// and classes, and ultimately through Win32 APIs. - /// - [Export(typeof(IFileSystem))] - internal class Win32FileSystem : IFileSystem + private static readonly DateTime s_minFileTime = DateTime.FromFileTimeUtc(0); + + public void Create(string path) { - private static readonly DateTime s_minFileTime = DateTime.FromFileTimeUtc(0); + File.Create(path).Dispose(); + } - public void Create(string path) - { - File.Create(path).Dispose(); - } + public bool FileExists(string path) + { + return File.Exists(path); + } - public bool FileExists(string path) - { - return File.Exists(path); - } + public bool PathExists(string path) + { + return File.Exists(path) || Directory.Exists(path); + } - public bool PathExists(string path) + public void RemoveFile(string path) + { + if (FileExists(path)) { - return File.Exists(path) || Directory.Exists(path); + File.Delete(path); } + } - public void RemoveFile(string path) + public void CopyFile(string source, string destination, bool overwrite, bool clearReadOnly) + { + File.Copy(source, destination, overwrite); + + if (clearReadOnly) { - if (FileExists(path)) + FileAttributes attributes = File.GetAttributes(destination); + if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { - File.Delete(path); + File.SetAttributes(destination, attributes & ~FileAttributes.ReadOnly); } } + } - public void CopyFile(string source, string destination, bool overwrite, bool clearReadOnly) - { - File.Copy(source, destination, overwrite); + public async Task ReadAllTextAsync(string path) + { + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); + using var reader = new StreamReader(stream); + return await reader.ReadToEndAsync(); + } - if (clearReadOnly) - { - FileAttributes attributes = File.GetAttributes(destination); - if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) - { - File.SetAttributes(destination, attributes & ~FileAttributes.ReadOnly); - } - } - } + public Stream OpenTextStream(string path) + { + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); + } - public async Task ReadAllTextAsync(string path) - { - using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); - using var reader = new StreamReader(stream); - return await reader.ReadToEndAsync(); - } + public async Task WriteAllTextAsync(string path, string content) + { + using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true); + using var writer = new StreamWriter(stream); + await writer.WriteAsync(content); + } - public Stream OpenTextStream(string path) + public DateTime GetLastFileWriteTimeOrMinValueUtc(string path) + { + if (TryGetLastFileWriteTimeUtc(path, out DateTime? result)) { - return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); + return result.Value; } - public async Task WriteAllTextAsync(string path, string content) - { - using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true); - using var writer = new StreamWriter(stream); - await writer.WriteAsync(content); - } + return DateTime.MinValue; + } - public DateTime GetLastFileWriteTimeOrMinValueUtc(string path) + public bool TryGetLastFileWriteTimeUtc(string path, [NotNullWhen(true)]out DateTime? result) + { + try { - if (TryGetLastFileWriteTimeUtc(path, out DateTime? result)) + result = File.GetLastWriteTimeUtc(path); + if (result != s_minFileTime) { - return result.Value; + return true; } - - return DateTime.MinValue; } - - public bool TryGetLastFileWriteTimeUtc(string path, [NotNullWhen(true)]out DateTime? result) + catch (IOException) { - try - { - result = File.GetLastWriteTimeUtc(path); - if (result != s_minFileTime) - { - return true; - } - } - catch (IOException) - { - } - catch (UnauthorizedAccessException) - { - } - catch (NotSupportedException) - { - } - - result = null; - return false; } - - public bool TryGetFileSizeBytes(string path, out long result) + catch (UnauthorizedAccessException) { - try - { - result = new FileInfo(path).Length; - return true; - } - catch (IOException) - { - } - catch (UnauthorizedAccessException) - { - } - catch (NotSupportedException) - { - } - - result = default; - return false; } - - public (long SizeBytes, DateTime WriteTimeUtc)? GetFileSizeAndWriteTimeUtc(string path) + catch (NotSupportedException) { - var info = new FileInfo(path); + } - if (info.Exists) - { - return (info.Length, info.LastWriteTimeUtc); - } + result = null; + return false; + } - return null; + public bool TryGetFileSizeBytes(string path, out long result) + { + try + { + result = new FileInfo(path).Length; + return true; } - - public bool DirectoryExists(string dirPath) + catch (IOException) { - return Directory.Exists(dirPath); } - - public void CreateDirectory(string dirPath) + catch (UnauthorizedAccessException) + { + } + catch (NotSupportedException) { - Directory.CreateDirectory(dirPath); } - public string GetFullPath(string path) + result = default; + return false; + } + + public (long SizeBytes, DateTime WriteTimeUtc)? GetFileSizeAndWriteTimeUtc(string path) + { + var info = new FileInfo(path); + + if (info.Exists) { - return Path.GetFullPath(path); + return (info.Length, info.LastWriteTimeUtc); } + + return null; + } + + public bool DirectoryExists(string dirPath) + { + return Directory.Exists(dirPath); + } + + public void CreateDirectory(string dirPath) + { + Directory.CreateDirectory(dirPath); + } + + public string GetFullPath(string path) + { + return Path.GetFullPath(path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ImmutableCollectionLinqExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ImmutableCollectionLinqExtensions.cs index 902abbe1d0..8d8e88c5af 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ImmutableCollectionLinqExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ImmutableCollectionLinqExtensions.cs @@ -1,83 +1,82 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class ImmutableCollectionLinqExtensions { - internal static class ImmutableCollectionLinqExtensions + // Most (all?) immutable collections provide non-allocating enumerators. + // + // This class provides replacements for common linq-like extension methods + // that don't box to IEnumerable and can therefore avoid allocation. + + public static int Count(this ImmutableDictionary source, Func, bool> predicate) + where TKey : notnull { - // Most (all?) immutable collections provide non-allocating enumerators. - // - // This class provides replacements for common linq-like extension methods - // that don't box to IEnumerable and can therefore avoid allocation. + int count = 0; - public static int Count(this ImmutableDictionary source, Func, bool> predicate) - where TKey : notnull + foreach (KeyValuePair pair in source) { - int count = 0; - - foreach (KeyValuePair pair in source) + if (predicate(pair)) { - if (predicate(pair)) - { - count++; - } + count++; } - - return count; } - public static bool Any(this ImmutableDictionary source, Func, bool> predicate) - where TKey : notnull - { - foreach (KeyValuePair pair in source) - { - if (predicate(pair)) - return true; - } + return count; + } - return false; + public static bool Any(this ImmutableDictionary source, Func, bool> predicate) + where TKey : notnull + { + foreach (KeyValuePair pair in source) + { + if (predicate(pair)) + return true; } - public static T? FirstOrDefault(this ImmutableArray source, Func predicate, TArg arg) - { - foreach (T obj in source) - { - if (predicate(obj, arg)) - return obj; - } + return false; + } - return default; + public static T? FirstOrDefault(this ImmutableArray source, Func predicate, TArg arg) + { + foreach (T obj in source) + { + if (predicate(obj, arg)) + return obj; } - public static ImmutableArray SelectImmutableArray(this ImmutableArray values, Func selector) - { - if (values.IsDefaultOrEmpty) - return []; + return default; + } - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: values.Length); + public static ImmutableArray SelectImmutableArray(this ImmutableArray values, Func selector) + { + if (values.IsDefaultOrEmpty) + return []; - foreach (TInput value in values) - { - builder.Add(selector(value)); - } + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: values.Length); - return builder.MoveToImmutable(); + foreach (TInput value in values) + { + builder.Add(selector(value)); } - public static ImmutableArray ToImmutableArray(this IImmutableDictionary dictionary, Func factory) - { - if (dictionary.Count == 0) - { - return ImmutableArray.Empty; - } + return builder.MoveToImmutable(); + } - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: dictionary.Count); + public static ImmutableArray ToImmutableArray(this IImmutableDictionary dictionary, Func factory) + { + if (dictionary.Count == 0) + { + return ImmutableArray.Empty; + } - foreach ((TKey key, TValue value) in dictionary) - { - builder.Add(factory(key, value)); - } + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: dictionary.Count); - return builder.MoveToImmutable(); + foreach ((TKey key, TValue value) in dictionary) + { + builder.Add(factory(key, value)); } + + return builder.MoveToImmutable(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Interop/Win32Interop.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Interop/Win32Interop.cs index 6d6254a791..57fde1d05c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Interop/Win32Interop.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Interop/Win32Interop.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Interop +namespace Microsoft.VisualStudio.Interop; + +internal static class Win32Interop { - internal static class Win32Interop - { - /// - /// Represents the Win32 error code for ERROR_FILE_EXISTS. - /// - public const int ERROR_FILE_EXISTS = 80; + /// + /// Represents the Win32 error code for ERROR_FILE_EXISTS. + /// + public const int ERROR_FILE_EXISTS = 80; - /// - /// Returns a HRESULT representing the specified Win32 error code. - /// - internal static int HResultFromWin32(int errorCode) - { - const int FACILITY_WIN32 = 7; - uint hr = errorCode <= 0 ? (uint)errorCode : ((uint)(errorCode & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000); - return (int)hr; - } + /// + /// Returns a HRESULT representing the specified Win32 error code. + /// + internal static int HResultFromWin32(int errorCode) + { + const int FACILITY_WIN32 = 7; + uint hr = errorCode <= 0 ? (uint)errorCode : ((uint)(errorCode & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000); + return (int)hr; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/LinqExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/LinqExtensions.cs index 412587238c..b8af3a4ab1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/LinqExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/LinqExtensions.cs @@ -2,115 +2,114 @@ using System.Diagnostics.Contracts; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class LinqExtensions { - internal static class LinqExtensions + [Pure] + public static T? FirstOrDefault(this IEnumerable source, Func predicate, TArg arg) { - [Pure] - public static T? FirstOrDefault(this IEnumerable source, Func predicate, TArg arg) + foreach (T obj in source) { - foreach (T obj in source) - { - if (predicate(obj, arg)) - return obj; - } - - return default; + if (predicate(obj, arg)) + return obj; } - [Pure] - public static T? SingleOrDefault(this IEnumerable source, Func predicate, TArg arg) + return default; + } + + [Pure] + public static T? SingleOrDefault(this IEnumerable source, Func predicate, TArg arg) + { + using IEnumerator enumerator = source.GetEnumerator(); + + while (enumerator.MoveNext()) { - using IEnumerator enumerator = source.GetEnumerator(); + T match = enumerator.Current; - while (enumerator.MoveNext()) + if (predicate(match, arg)) { - T match = enumerator.Current; - - if (predicate(match, arg)) + // Check all remaining items to ensure there is only a single match + while (enumerator.MoveNext()) { - // Check all remaining items to ensure there is only a single match - while (enumerator.MoveNext()) + if (predicate(enumerator.Current, arg)) { - if (predicate(enumerator.Current, arg)) - { - throw new InvalidOperationException("More than one element matches predicate."); - } + throw new InvalidOperationException("More than one element matches predicate."); } - - return match; } - } - return default; + return match; + } } - [Pure] - public static IEnumerable WhereNotNull(this IEnumerable source) where T : class + return default; + } + + [Pure] + public static IEnumerable WhereNotNull(this IEnumerable source) where T : class + { + foreach (T? item in source) { - foreach (T? item in source) - { - if (item is not null) - yield return item; - } + if (item is not null) + yield return item; } + } - /// - /// Specialisation of - /// that avoids allocation when the sequence is statically known to be an array. - /// - public static bool Any(this T[] array, Func predicate) + /// + /// Specialisation of + /// that avoids allocation when the sequence is statically known to be an array. + /// + public static bool Any(this T[] array, Func predicate) + { + foreach (T item in array) { - foreach (T item in array) + if (predicate(item)) { - if (predicate(item)) - { - return true; - } + return true; } - - return false; } - /// - /// Specialisation of - /// that avoids allocation when the sequence is statically known to be an array. - /// - public static bool Any(this T[] array, Func predicate, TArg arg) + return false; + } + + /// + /// Specialisation of + /// that avoids allocation when the sequence is statically known to be an array. + /// + public static bool Any(this T[] array, Func predicate, TArg arg) + { + foreach (T item in array) { - foreach (T item in array) + if (predicate(item, arg)) { - if (predicate(item, arg)) - { - return true; - } + return true; } - - return false; } - public static ImmutableArray ToImmutableValueArray(this Dictionary source) where TKey: notnull - { - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(source.Count); + return false; + } - foreach ((_, TValue value) in source) - { - builder.Add(value); - } + public static ImmutableArray ToImmutableValueArray(this Dictionary source) where TKey: notnull + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(source.Count); - return builder.MoveToImmutable(); + foreach ((_, TValue value) in source) + { + builder.Add(value); } - public static TOut[] SelectArray(this TIn[] array, Func selector) - { - TOut[] output = new TOut[array.Length]; + return builder.MoveToImmutable(); + } - for (int i = 0; i < array.Length; i++) - { - output[i] = selector(array[i]); - } + public static TOut[] SelectArray(this TIn[] array, Func selector) + { + TOut[] output = new TOut[array.Length]; - return output; + for (int i = 0; i < array.Length; i++) + { + output[i] = selector(array[i]); } + + return output; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/InternPool.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/InternPool.cs index 75506c0050..71ed9e1f5a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/InternPool.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/InternPool.cs @@ -1,52 +1,51 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +/// +/// A thread-safe object pool, backed by and using interlocked +/// operations for optimistic, lock-free concurrent access. +/// +/// +/// Objects added to this pool will never be released. +/// +/// +internal sealed class InternPool where T : class { - /// - /// A thread-safe object pool, backed by and using interlocked - /// operations for optimistic, lock-free concurrent access. - /// - /// - /// Objects added to this pool will never be released. - /// - /// - internal sealed class InternPool where T : class - { - private ImmutableHashSet _set; + private ImmutableHashSet _set; - public int Count => _set.Count; + public int Count => _set.Count; - public InternPool(IEqualityComparer? comparer = null) - { - _set = ImmutableHashSet.Create(comparer); - } + public InternPool(IEqualityComparer? comparer = null) + { + _set = ImmutableHashSet.Create(comparer); + } - public T Intern(T value) - { - // Would be nice if this was on ImmutableInterlocked as - // requested in https://github.com/dotnet/corefx/issues/33653 + public T Intern(T value) + { + // Would be nice if this was on ImmutableInterlocked as + // requested in https://github.com/dotnet/corefx/issues/33653 - ImmutableHashSet priorCollection = Volatile.Read(ref _set); + ImmutableHashSet priorCollection = Volatile.Read(ref _set); - bool successful; - do + bool successful; + do + { + if (priorCollection.TryGetValue(value, out T existingValue)) { - if (priorCollection.TryGetValue(value, out T existingValue)) - { - // The value already exists in the set. Return it. - return existingValue; - } - - ImmutableHashSet updatedCollection = priorCollection.Add(value); - ImmutableHashSet interlockedResult = Interlocked.CompareExchange(ref _set, updatedCollection, priorCollection); - successful = ReferenceEquals(priorCollection, interlockedResult); - priorCollection = interlockedResult; // We already have a volatile read that we can reuse for the next loop + // The value already exists in the set. Return it. + return existingValue; } - while (!successful); - // We won the race-condition and have updated the collection. - // Return the value that is in the collection (as of the Interlocked operation). - return value; + ImmutableHashSet updatedCollection = priorCollection.Add(value); + ImmutableHashSet interlockedResult = Interlocked.CompareExchange(ref _set, updatedCollection, priorCollection); + successful = ReferenceEquals(priorCollection, interlockedResult); + priorCollection = interlockedResult; // We already have a volatile read that we can reuse for the next loop } + while (!successful); + + // We won the race-condition and have updated the collection. + // Return the value that is in the collection (as of the Interlocked operation). + return value; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/ObjectPool.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/ObjectPool.cs index e6188f870c..f9d8bd4422 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/ObjectPool.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/ObjectPool.cs @@ -2,141 +2,140 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +/// +/// Generic implementation of object pooling pattern with predefined pool size limit. The main +/// purpose is that limited number of frequently used objects can be kept in the pool for +/// further recycling. +/// +/// Notes: +/// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there +/// is no space in the pool, extra returned objects will be dropped. +/// +/// 2) it is implied that if object was obtained from a pool, the caller will return it back in +/// a relatively short time. Keeping checked out objects for long durations is ok, but +/// reduces usefulness of pooling. Just new up your own. +/// +/// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. +/// Rationale: +/// If there is no intent for reusing the object, do not use pool - just use "new". +/// +internal class ObjectPool where T : class { - /// - /// Generic implementation of object pooling pattern with predefined pool size limit. The main - /// purpose is that limited number of frequently used objects can be kept in the pool for - /// further recycling. - /// - /// Notes: - /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there - /// is no space in the pool, extra returned objects will be dropped. - /// - /// 2) it is implied that if object was obtained from a pool, the caller will return it back in - /// a relatively short time. Keeping checked out objects for long durations is ok, but - /// reduces usefulness of pooling. Just new up your own. - /// - /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. - /// Rationale: - /// If there is no intent for reusing the object, do not use pool - just use "new". - /// - internal class ObjectPool where T : class + [DebuggerDisplay("{Value,nq}")] + private struct Element { - [DebuggerDisplay("{Value,nq}")] - private struct Element - { - internal T? Value; - } + internal T? Value; + } - // Storage for the pool objects. The first item is stored in a dedicated field because we - // expect to be able to satisfy most requests from it. - private T? _firstItem; - private readonly Element[] _items; + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + private T? _firstItem; + private readonly Element[] _items; - // factory is stored for the lifetime of the pool. We will call this only when pool needs to - // expand. compared to "new T()", Func gives more flexibility to implementers and faster - // than "new T()". - private readonly Func _factory; + // factory is stored for the lifetime of the pool. We will call this only when pool needs to + // expand. compared to "new T()", Func gives more flexibility to implementers and faster + // than "new T()". + private readonly Func _factory; - internal ObjectPool(Func factory) - : this(factory, Environment.ProcessorCount * 2) - { } + internal ObjectPool(Func factory) + : this(factory, Environment.ProcessorCount * 2) + { } - internal ObjectPool(Func factory, int size) - { - Requires.Argument(size >= 1, nameof(size), "must be greater than or equal to one"); - _factory = factory; - _items = new Element[size - 1]; - } + internal ObjectPool(Func factory, int size) + { + Requires.Argument(size >= 1, nameof(size), "must be greater than or equal to one"); + _factory = factory; + _items = new Element[size - 1]; + } + + private T CreateInstance() + { + T inst = _factory(); + return inst; + } - private T CreateInstance() + /// + /// Produces an instance. + /// + /// + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search. + /// + internal T Allocate() + { + // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T? inst = _firstItem; + if (inst is null || inst != Interlocked.CompareExchange(ref _firstItem, null, inst)) { - T inst = _factory(); - return inst; + inst = AllocateSlow(); } - /// - /// Produces an instance. - /// - /// - /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. - /// Note that Free will try to store recycled objects close to the start thus statistically - /// reducing how far we will typically search. - /// - internal T Allocate() + return inst; + } + + private T AllocateSlow() + { + Element[] items = _items; + + for (int i = 0; i < items.Length; i++) { - // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. // Note that the initial read is optimistically not synchronized. That is intentional. // We will interlock only when we have a candidate. in a worst case we may miss some // recently returned objects. Not a big deal. - T? inst = _firstItem; - if (inst is null || inst != Interlocked.CompareExchange(ref _firstItem, null, inst)) + T? inst = items[i].Value; + if (inst is not null) { - inst = AllocateSlow(); - } - - return inst; - } - - private T AllocateSlow() - { - Element[] items = _items; - - for (int i = 0; i < items.Length; i++) - { - // Note that the initial read is optimistically not synchronized. That is intentional. - // We will interlock only when we have a candidate. in a worst case we may miss some - // recently returned objects. Not a big deal. - T? inst = items[i].Value; - if (inst is not null) + if (inst == Interlocked.CompareExchange(ref items[i].Value, null, inst)) { - if (inst == Interlocked.CompareExchange(ref items[i].Value, null, inst)) - { - return inst; - } + return inst; } } + } + + return CreateInstance(); + } - return CreateInstance(); + /// + /// Returns objects to the pool. + /// + /// + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search in Allocate. + /// + internal void Free(T obj) + { + if (_firstItem is null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + _firstItem = obj; + } + else + { + FreeSlow(obj); } + } - /// - /// Returns objects to the pool. - /// - /// - /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. - /// Note that Free will try to store recycled objects close to the start thus statistically - /// reducing how far we will typically search in Allocate. - /// - internal void Free(T obj) + private void FreeSlow(T obj) + { + Element[] items = _items; + for (int i = 0; i < items.Length; i++) { - if (_firstItem is null) + if (items[i].Value is null) { // Intentionally not using interlocked here. // In a worst case scenario two objects may be stored into same slot. // It is very unlikely to happen and will only mean that one of the objects will get collected. - _firstItem = obj; - } - else - { - FreeSlow(obj); - } - } - - private void FreeSlow(T obj) - { - Element[] items = _items; - for (int i = 0; i < items.Length; i++) - { - if (items[i].Value is null) - { - // Intentionally not using interlocked here. - // In a worst case scenario two objects may be stored into same slot. - // It is very unlikely to happen and will only mean that one of the objects will get collected. - items[i].Value = obj; - break; - } + items[i].Value = obj; + break; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.DebuggerProxy.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.DebuggerProxy.cs index b62537b821..48ea5c555c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.DebuggerProxy.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.DebuggerProxy.cs @@ -2,32 +2,31 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +internal sealed partial class PooledArray { - internal sealed partial class PooledArray + private sealed class DebuggerProxy { - private sealed class DebuggerProxy - { - private readonly PooledArray _builder; + private readonly PooledArray _builder; - public DebuggerProxy(PooledArray builder) - { - _builder = builder; - } + public DebuggerProxy(PooledArray builder) + { + _builder = builder; + } - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public T[] A + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] A + { + get { - get + var result = new T[_builder.Count]; + for (int i = 0; i < result.Length; i++) { - var result = new T[_builder.Count]; - for (int i = 0; i < result.Length; i++) - { - result[i] = _builder[i]; - } - - return result; + result[i] = _builder[i]; } + + return result; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.Enumerator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.Enumerator.cs index a60f731faf..080b7089fb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.Enumerator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.Enumerator.cs @@ -1,38 +1,37 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +internal sealed partial class PooledArray { - internal sealed partial class PooledArray + /// + /// struct enumerator used in foreach. + /// + public struct Enumerator : IEnumerator { - /// - /// struct enumerator used in foreach. - /// - public struct Enumerator : IEnumerator - { - private readonly PooledArray _builder; - private int _index; + private readonly PooledArray _builder; + private int _index; - public Enumerator(PooledArray builder) - { - _builder = builder; - _index = -1; - } + public Enumerator(PooledArray builder) + { + _builder = builder; + _index = -1; + } - public T Current => _builder[_index]; + public T Current => _builder[_index]; - public bool MoveNext() - { - _index++; - return _index < _builder.Count; - } + public bool MoveNext() + { + _index++; + return _index < _builder.Count; + } - public void Dispose() - { - } + public void Dispose() + { + } - object System.Collections.IEnumerator.Current => Current!; + object System.Collections.IEnumerator.Current => Current!; - public void Reset() => _index = -1; - } + public void Reset() => _index = -1; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.cs index ea9c4a895b..c01cf553ea 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledArray.cs @@ -4,438 +4,437 @@ #nullable disable -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +[DebuggerDisplay("Count = {Count,nq}")] +[DebuggerTypeProxy(typeof(PooledArray<>.DebuggerProxy))] +internal sealed partial class PooledArray : IReadOnlyCollection, IReadOnlyList { - [DebuggerDisplay("Count = {Count,nq}")] - [DebuggerTypeProxy(typeof(PooledArray<>.DebuggerProxy))] - internal sealed partial class PooledArray : IReadOnlyCollection, IReadOnlyList + private readonly ImmutableArray.Builder _builder; + + private readonly ObjectPool> _pool; + + public PooledArray(int size) { - private readonly ImmutableArray.Builder _builder; + _builder = ImmutableArray.CreateBuilder(size); + } - private readonly ObjectPool> _pool; + public PooledArray() + : this(8) + { } - public PooledArray(int size) - { - _builder = ImmutableArray.CreateBuilder(size); - } + private PooledArray(ObjectPool> pool) + : this() + { + _pool = pool; + } - public PooledArray() - : this(8) - { } + /// + /// Realizes the array. + /// + public ImmutableArray ToImmutable() + { + return _builder.ToImmutable(); + } - private PooledArray(ObjectPool> pool) - : this() + public int Count + { + get { - _pool = pool; + return _builder.Count; } - - /// - /// Realizes the array. - /// - public ImmutableArray ToImmutable() + set { - return _builder.ToImmutable(); + _builder.Count = value; } + } - public int Count + public T this[int index] + { + get { - get - { - return _builder.Count; - } - set - { - _builder.Count = value; - } + return _builder[index]; } - public T this[int index] + set { - get - { - return _builder[index]; - } - - set - { - _builder[index] = value; - } + _builder[index] = value; } + } - /// - /// Write to slot . - /// Fills in unallocated slots preceding the , if any. - /// - public void SetItem(int index, T value) + /// + /// Write to slot . + /// Fills in unallocated slots preceding the , if any. + /// + public void SetItem(int index, T value) + { + while (index > _builder.Count) { - while (index > _builder.Count) - { - _builder.Add(default); - } + _builder.Add(default); + } - if (index == _builder.Count) - { - _builder.Add(value); - } - else - { - _builder[index] = value; - } + if (index == _builder.Count) + { + _builder.Add(value); + } + else + { + _builder[index] = value; } + } - public void Add(T item) => _builder.Add(item); + public void Add(T item) => _builder.Add(item); - public void Insert(int index, T item) => _builder.Insert(index, item); + public void Insert(int index, T item) => _builder.Insert(index, item); - public void EnsureCapacity(int capacity) + public void EnsureCapacity(int capacity) + { + if (_builder.Capacity < capacity) { - if (_builder.Capacity < capacity) - { - _builder.Capacity = capacity; - } + _builder.Capacity = capacity; } + } - public void Clear() => _builder.Clear(); + public void Clear() => _builder.Clear(); - public bool Contains(T item) => _builder.Contains(item); + public bool Contains(T item) => _builder.Contains(item); - public int IndexOf(T item) => _builder.IndexOf(item); + public int IndexOf(T item) => _builder.IndexOf(item); - public int IndexOf(T item, IEqualityComparer equalityComparer) - => _builder.IndexOf(item, 0, _builder.Count, equalityComparer); + public int IndexOf(T item, IEqualityComparer equalityComparer) + => _builder.IndexOf(item, 0, _builder.Count, equalityComparer); - public int IndexOf(T item, int startIndex, int count) - => _builder.IndexOf(item, startIndex, count); + public int IndexOf(T item, int startIndex, int count) + => _builder.IndexOf(item, startIndex, count); - public int FindIndex(Predicate match) - => FindIndex(0, Count, match); + public int FindIndex(Predicate match) + => FindIndex(0, Count, match); - public int FindIndex(int startIndex, Predicate match) - => FindIndex(startIndex, Count - startIndex, match); + public int FindIndex(int startIndex, Predicate match) + => FindIndex(startIndex, Count - startIndex, match); - public int FindIndex(int startIndex, int count, Predicate match) + public int FindIndex(int startIndex, int count, Predicate match) + { + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) { - int endIndex = startIndex + count; - for (int i = startIndex; i < endIndex; i++) + if (match(_builder[i])) { - if (match(_builder[i])) - { - return i; - } + return i; } - - return -1; } - public void RemoveAt(int index) => _builder.RemoveAt(index); - - public void RemoveLast() => _builder.RemoveAt(_builder.Count - 1); + return -1; + } - public void ReverseContents() => _builder.Reverse(); + public void RemoveAt(int index) => _builder.RemoveAt(index); - public void Sort() => _builder.Sort(); + public void RemoveLast() => _builder.RemoveAt(_builder.Count - 1); - public void Sort(IComparer comparer) - { - _builder.Sort(comparer); - } + public void ReverseContents() => _builder.Reverse(); - public void Sort(Comparison compare) - => Sort(Comparer.Create(compare)); + public void Sort() => _builder.Sort(); - public void Sort(int startIndex, IComparer comparer) - => _builder.Sort(startIndex, _builder.Count - startIndex, comparer); + public void Sort(IComparer comparer) + { + _builder.Sort(comparer); + } - public T[] ToArray() => _builder.ToArray(); + public void Sort(Comparison compare) + => Sort(Comparer.Create(compare)); - public void CopyTo(T[] array, int start) => _builder.CopyTo(array, start); + public void Sort(int startIndex, IComparer comparer) + => _builder.Sort(startIndex, _builder.Count - startIndex, comparer); - public T Last() => _builder[_builder.Count - 1]; + public T[] ToArray() => _builder.ToArray(); - public T First() => _builder[0]; + public void CopyTo(T[] array, int start) => _builder.CopyTo(array, start); - public bool Any() => _builder.Count > 0; + public T Last() => _builder[_builder.Count - 1]; - /// - /// Realizes the array. - /// - public ImmutableArray ToImmutableOrNull() - => Count == 0 ? default : ToImmutable(); + public T First() => _builder[0]; - /// - /// Realizes the array, downcasting each element to a derived type. - /// - public ImmutableArray ToDowncastedImmutable() - where U : T - { - if (Count == 0) - { - return ImmutableArray.Empty; - } + public bool Any() => _builder.Count > 0; - var tmp = PooledArray.GetInstance(Count); - foreach (T i in this) - { - tmp.Add((U)i); - } + /// + /// Realizes the array. + /// + public ImmutableArray ToImmutableOrNull() + => Count == 0 ? default : ToImmutable(); - return tmp.ToImmutableAndFree(); + /// + /// Realizes the array, downcasting each element to a derived type. + /// + public ImmutableArray ToDowncastedImmutable() + where U : T + { + if (Count == 0) + { + return ImmutableArray.Empty; } - /// - /// Realizes the array and disposes the builder in one operation. - /// - public ImmutableArray ToImmutableAndFree() + var tmp = PooledArray.GetInstance(Count); + foreach (T i in this) { - ImmutableArray result; - if (_builder.Capacity == Count) - { - result = _builder.MoveToImmutable(); - } - else - { - result = ToImmutable(); - } - - Free(); - return result; + tmp.Add((U)i); } - public T[] ToArrayAndFree() + return tmp.ToImmutableAndFree(); + } + + /// + /// Realizes the array and disposes the builder in one operation. + /// + public ImmutableArray ToImmutableAndFree() + { + ImmutableArray result; + if (_builder.Capacity == Count) { - T[] result = ToArray(); - Free(); - return result; + result = _builder.MoveToImmutable(); } - - // To implement Poolable, you need two things: - // 1) Expose Freeing primitive. - public void Free() + else { - ObjectPool> pool = _pool; - if (pool is not null) + result = ToImmutable(); + } + + Free(); + return result; + } + + public T[] ToArrayAndFree() + { + T[] result = ToArray(); + Free(); + return result; + } + + // To implement Poolable, you need two things: + // 1) Expose Freeing primitive. + public void Free() + { + ObjectPool> pool = _pool; + if (pool is not null) + { + // We do not want to retain (potentially indefinitely) very large builders + // while the chance that we will need their size is diminishingly small. + // It makes sense to constrain the size to some "not too small" number. + // Overall perf does not seem to be very sensitive to this number, so I picked 128 as a limit. + if (_builder.Capacity < 128) { - // We do not want to retain (potentially indefinitely) very large builders - // while the chance that we will need their size is diminishingly small. - // It makes sense to constrain the size to some "not too small" number. - // Overall perf does not seem to be very sensitive to this number, so I picked 128 as a limit. - if (_builder.Capacity < 128) + if (Count != 0) { - if (Count != 0) - { - Clear(); - } - - pool.Free(this); - return; + Clear(); } + + pool.Free(this); + return; } } + } - // 2) Expose the pool or the way to create a pool or the way to get an instance. - // for now we will expose both and figure which way works better - private static readonly ObjectPool> s_poolInstance = CreatePool(); - public static PooledArray GetInstance() - { - PooledArray builder = s_poolInstance.Allocate(); - return builder; - } + // 2) Expose the pool or the way to create a pool or the way to get an instance. + // for now we will expose both and figure which way works better + private static readonly ObjectPool> s_poolInstance = CreatePool(); + public static PooledArray GetInstance() + { + PooledArray builder = s_poolInstance.Allocate(); + return builder; + } + + public static PooledArray GetInstance(int capacity) + { + PooledArray builder = GetInstance(); + builder.EnsureCapacity(capacity); + return builder; + } - public static PooledArray GetInstance(int capacity) + public static PooledArray GetInstance(int capacity, T fillWithValue) + { + PooledArray builder = GetInstance(); + builder.EnsureCapacity(capacity); + + for (int i = 0; i < capacity; i++) { - PooledArray builder = GetInstance(); - builder.EnsureCapacity(capacity); - return builder; + builder.Add(fillWithValue); } - public static PooledArray GetInstance(int capacity, T fillWithValue) - { - PooledArray builder = GetInstance(); - builder.EnsureCapacity(capacity); + return builder; + } - for (int i = 0; i < capacity; i++) - { - builder.Add(fillWithValue); - } + public static ObjectPool> CreatePool() + { + // We use a default size of 128 objects in the pool + // This makes it likely that we can handle all use cases + // even if many consumers require objects from the pool + // in practice we expect 128 allocated objects in the pool + // to be rare. A normal operating set should be around 10. + return CreatePool(128); + } - return builder; - } + public static ObjectPool> CreatePool(int size) + { + ObjectPool> pool = null; + pool = new ObjectPool>(() => new PooledArray(pool), size); + return pool; + } - public static ObjectPool> CreatePool() - { - // We use a default size of 128 objects in the pool - // This makes it likely that we can handle all use cases - // even if many consumers require objects from the pool - // in practice we expect 128 allocated objects in the pool - // to be rare. A normal operating set should be around 10. - return CreatePool(128); - } + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } - public static ObjectPool> CreatePool(int size) - { - ObjectPool> pool = null; - pool = new ObjectPool>(() => new PooledArray(pool), size); - return pool; - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() + internal Dictionary> ToDictionary(Func keySelector, IEqualityComparer comparer = null) + { + if (Count == 1) { - return GetEnumerator(); + var dictionary1 = new Dictionary>(1, comparer); + T value = this[0]; + dictionary1.Add(keySelector(value), ImmutableArray.Create(value)); + return dictionary1; } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + if (Count == 0) { - return GetEnumerator(); + return new Dictionary>(comparer); } - internal Dictionary> ToDictionary(Func keySelector, IEqualityComparer comparer = null) + // bucketize + // prevent reallocation. it may not have 'count' entries, but it won't have more. + var accumulator = new Dictionary>(Count, comparer); + for (int i = 0; i < Count; i++) { - if (Count == 1) + T item = this[i]; + K key = keySelector(item); + if (!accumulator.TryGetValue(key, out PooledArray bucket)) { - var dictionary1 = new Dictionary>(1, comparer); - T value = this[0]; - dictionary1.Add(keySelector(value), ImmutableArray.Create(value)); - return dictionary1; + bucket = GetInstance(); + accumulator.Add(key, bucket); } - if (Count == 0) - { - return new Dictionary>(comparer); - } + bucket.Add(item); + } - // bucketize - // prevent reallocation. it may not have 'count' entries, but it won't have more. - var accumulator = new Dictionary>(Count, comparer); - for (int i = 0; i < Count; i++) - { - T item = this[i]; - K key = keySelector(item); - if (!accumulator.TryGetValue(key, out PooledArray bucket)) - { - bucket = GetInstance(); - accumulator.Add(key, bucket); - } + var dictionary = new Dictionary>(accumulator.Count, comparer); - bucket.Add(item); - } + // freeze + foreach (KeyValuePair> pair in accumulator) + { + dictionary.Add(pair.Key, pair.Value.ToImmutableAndFree()); + } - var dictionary = new Dictionary>(accumulator.Count, comparer); + return dictionary; + } - // freeze - foreach (KeyValuePair> pair in accumulator) - { - dictionary.Add(pair.Key, pair.Value.ToImmutableAndFree()); - } + public void AddRange(PooledArray items) + { + _builder.AddRange(items._builder); + } - return dictionary; - } + public void AddRange(PooledArray items) where U : T + { + _builder.AddRange(items._builder); + } - public void AddRange(PooledArray items) - { - _builder.AddRange(items._builder); - } + public void AddRange(ImmutableArray items) + { + _builder.AddRange(items); + } - public void AddRange(PooledArray items) where U : T - { - _builder.AddRange(items._builder); - } + public void AddRange(ImmutableArray items, int length) + { + _builder.AddRange(items, length); + } - public void AddRange(ImmutableArray items) - { - _builder.AddRange(items); - } + public void AddRange(ImmutableArray items) where S : class, T + { + AddRange(ImmutableArray.CastUp(items)); + } - public void AddRange(ImmutableArray items, int length) + public void AddRange(T[] items, int start, int length) + { + for (int i = start, end = start + length; i < end; i++) { - _builder.AddRange(items, length); + Add(items[i]); } + } - public void AddRange(ImmutableArray items) where S : class, T - { - AddRange(ImmutableArray.CastUp(items)); - } + public void AddRange(IEnumerable items) + { + _builder.AddRange(items); + } - public void AddRange(T[] items, int start, int length) - { - for (int i = start, end = start + length; i < end; i++) - { - Add(items[i]); - } - } + public void AddRange(params T[] items) + { + _builder.AddRange(items); + } - public void AddRange(IEnumerable items) - { - _builder.AddRange(items); - } + public void AddRange(T[] items, int length) + { + _builder.AddRange(items, length); + } - public void AddRange(params T[] items) - { - _builder.AddRange(items); - } + public void Clip(int limit) + { + _builder.Count = limit; + } - public void AddRange(T[] items, int length) - { - _builder.AddRange(items, length); - } + public void ZeroInit(int count) + { + _builder.Clear(); + _builder.Count = count; + } - public void Clip(int limit) + public void AddMany(T item, int count) + { + for (int i = 0; i < count; i++) { - _builder.Count = limit; + Add(item); } + } - public void ZeroInit(int count) - { - _builder.Clear(); - _builder.Count = count; - } + public void RemoveDuplicates() + { + var set = PooledHashSet.GetInstance(); - public void AddMany(T item, int count) + int j = 0; + for (int i = 0; i < Count; i++) { - for (int i = 0; i < count; i++) + if (set.Add(this[i])) { - Add(item); + this[j] = this[i]; + j++; } } - public void RemoveDuplicates() - { - var set = PooledHashSet.GetInstance(); - - int j = 0; - for (int i = 0; i < Count; i++) - { - if (set.Add(this[i])) - { - this[j] = this[i]; - j++; - } - } + Clip(j); + set.Free(); + } - Clip(j); - set.Free(); - } + public ImmutableArray SelectDistinct(Func selector) + { + var result = PooledArray.GetInstance(Count); + var set = PooledHashSet.GetInstance(); - public ImmutableArray SelectDistinct(Func selector) + foreach (T item in this) { - var result = PooledArray.GetInstance(Count); - var set = PooledHashSet.GetInstance(); - - foreach (T item in this) + S selected = selector(item); + if (set.Add(selected)) { - S selected = selector(item); - if (set.Add(selected)) - { - result.Add(selected); - } + result.Add(selected); } - - set.Free(); - return result.ToImmutableAndFree(); } + + set.Free(); + return result.ToImmutableAndFree(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledDictionary.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledDictionary.cs index 5f4cbd01fb..abcc8cb5de 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledDictionary.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledDictionary.cs @@ -1,48 +1,47 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +// Dictionary that can be recycled via an object pool +// NOTE: these dictionaries always have the default comparer. +internal class PooledDictionary : Dictionary + where K : notnull { - // Dictionary that can be recycled via an object pool - // NOTE: these dictionaries always have the default comparer. - internal class PooledDictionary : Dictionary - where K : notnull + private readonly ObjectPool> _pool; + + private PooledDictionary(ObjectPool> pool) + { + _pool = pool; + } + + public ImmutableDictionary ToImmutableDictionaryAndFree() + { + var result = this.ToImmutableDictionary(); + Free(); + return result; + } + + public void Free() + { + Clear(); + _pool?.Free(this); + } + + // global pool + private static readonly ObjectPool> s_poolInstance = CreatePool(); + + // if someone needs to create a pool; + public static ObjectPool> CreatePool() + { + ObjectPool>? pool = null; + pool = new ObjectPool>(() => new PooledDictionary(pool!), 128); + return pool; + } + + public static PooledDictionary GetInstance() { - private readonly ObjectPool> _pool; - - private PooledDictionary(ObjectPool> pool) - { - _pool = pool; - } - - public ImmutableDictionary ToImmutableDictionaryAndFree() - { - var result = this.ToImmutableDictionary(); - Free(); - return result; - } - - public void Free() - { - Clear(); - _pool?.Free(this); - } - - // global pool - private static readonly ObjectPool> s_poolInstance = CreatePool(); - - // if someone needs to create a pool; - public static ObjectPool> CreatePool() - { - ObjectPool>? pool = null; - pool = new ObjectPool>(() => new PooledDictionary(pool!), 128); - return pool; - } - - public static PooledDictionary GetInstance() - { - PooledDictionary instance = s_poolInstance.Allocate(); - // Debug.Assert(instance.Count == 0); - return instance; - } + PooledDictionary instance = s_poolInstance.Allocate(); + // Debug.Assert(instance.Count == 0); + return instance; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledHashSet.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledHashSet.cs index 12e0554868..5dd2bda486 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledHashSet.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledHashSet.cs @@ -1,39 +1,38 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +// HashSet that can be recycled via an object pool +// NOTE: these HashSets always have the default comparer. +internal class PooledHashSet : HashSet { - // HashSet that can be recycled via an object pool - // NOTE: these HashSets always have the default comparer. - internal class PooledHashSet : HashSet - { - private readonly ObjectPool> _pool; + private readonly ObjectPool> _pool; - private PooledHashSet(ObjectPool> pool) - { - _pool = pool; - } + private PooledHashSet(ObjectPool> pool) + { + _pool = pool; + } - public void Free() - { - Clear(); - _pool?.Free(this); - } + public void Free() + { + Clear(); + _pool?.Free(this); + } - // global pool - private static readonly ObjectPool> s_poolInstance = CreatePool(); + // global pool + private static readonly ObjectPool> s_poolInstance = CreatePool(); - // if someone needs to create a pool; - public static ObjectPool> CreatePool() - { - ObjectPool>? pool = null; - pool = new ObjectPool>(() => new PooledHashSet(pool!), 128); - return pool; - } + // if someone needs to create a pool; + public static ObjectPool> CreatePool() + { + ObjectPool>? pool = null; + pool = new ObjectPool>(() => new PooledHashSet(pool!), 128); + return pool; + } - public static PooledHashSet GetInstance() - { - PooledHashSet instance = s_poolInstance.Allocate(); - return instance; - } + public static PooledHashSet GetInstance() + { + PooledHashSet instance = s_poolInstance.Allocate(); + return instance; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledStringBuilder.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledStringBuilder.cs index 7b462e31a0..665fd19ca0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledStringBuilder.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/PooledObjects/PooledStringBuilder.cs @@ -2,134 +2,133 @@ using System.Text; -namespace Microsoft.VisualStudio.Buffers.PooledObjects +namespace Microsoft.VisualStudio.Buffers.PooledObjects; + +/// +/// The usage is: +/// var sb = PooledStringBuilder.GetInstance(); +/// ... Do Stuff... +/// sb.ToStringAndFree() +/// +internal class PooledStringBuilder { - /// - /// The usage is: - /// var sb = PooledStringBuilder.GetInstance(); - /// ... Do Stuff... - /// sb.ToStringAndFree() - /// - internal class PooledStringBuilder + private readonly StringBuilder _builder = new(); + private readonly ObjectPool _pool; + + private PooledStringBuilder(ObjectPool pool) { - private readonly StringBuilder _builder = new(); - private readonly ObjectPool _pool; + Requires.NotNull(pool); + _pool = pool; + } - private PooledStringBuilder(ObjectPool pool) - { - Requires.NotNull(pool); - _pool = pool; - } + public int Length { get => _builder.Length; set => _builder.Length = value; } - public int Length { get => _builder.Length; set => _builder.Length = value; } + public void Free() + { + StringBuilder builder = _builder; - public void Free() + // do not store builders that are too large. + if (builder.Capacity <= 1024) { - StringBuilder builder = _builder; - - // do not store builders that are too large. - if (builder.Capacity <= 1024) - { - builder.Clear(); - _pool.Free(this); - } + builder.Clear(); + _pool.Free(this); } + } - public string ToStringAndFree() - { - string result = _builder.ToString(); - Free(); - - return result; - } + public string ToStringAndFree() + { + string result = _builder.ToString(); + Free(); - public string ToStringAndFree(int startIndex, int length) - { - string result = _builder.ToString(startIndex, length); - Free(); + return result; + } - return result; - } + public string ToStringAndFree(int startIndex, int length) + { + string result = _builder.ToString(startIndex, length); + Free(); - // global pool - private static readonly ObjectPool s_poolInstance = CreatePool(); + return result; + } - /// - /// If someone need to create a private pool - /// - /// The size of the pool. - public static ObjectPool CreatePool(int size = 32) - { - ObjectPool? pool = null; - pool = new ObjectPool(() => new PooledStringBuilder(pool!), size); - return pool; - } + // global pool + private static readonly ObjectPool s_poolInstance = CreatePool(); - public static PooledStringBuilder GetInstance() - { - PooledStringBuilder builder = s_poolInstance.Allocate(); - //Debug.Assert(builder.Builder.Length == 0); - return builder; - } + /// + /// If someone need to create a private pool + /// + /// The size of the pool. + public static ObjectPool CreatePool(int size = 32) + { + ObjectPool? pool = null; + pool = new ObjectPool(() => new PooledStringBuilder(pool!), size); + return pool; + } - public static implicit operator StringBuilder(PooledStringBuilder obj) => obj._builder; - - public char this[int index] { get => _builder[index]; set => _builder[index] = value; } - public void Append(double value) => _builder.Append(value); - public void Append(char[] value) => _builder.Append(value); - public void Append(object value) => _builder.Append(value); - public void Append(ulong value) => _builder.Append(value); - public void Append(uint value) => _builder.Append(value); - public void Append(ushort value) => _builder.Append(value); - public void Append(decimal value) => _builder.Append(value); - public void Append(float value) => _builder.Append(value); - public void Append(int value) => _builder.Append(value); - public void Append(short value) => _builder.Append(value); - public void Append(char value) => _builder.Append(value); - public void Append(long value) => _builder.Append(value); - public void Append(sbyte value) => _builder.Append(value); - public void Append(byte value) => _builder.Append(value); - public void Append(char[] value, int startIndex, int charCount) => _builder.Append(value, startIndex, charCount); - public void Append(string value) => _builder.Append(value); - public void Append(string value, int startIndex, int count) => _builder.Append(value, startIndex, count); - public void Append(char value, int repeatCount) => _builder.Append(value, repeatCount); - public void Append(bool value) => _builder.Append(value); - public void AppendFormat(IFormatProvider provider, string format, params object[] args) => _builder.AppendFormat(provider, format, args); - public void AppendFormat(string format, object arg0, object arg1, object arg2) => _builder.AppendFormat(format, arg0, arg1, arg2); - public void AppendFormat(string format, params object[] args) => _builder.AppendFormat(format, args); - public void AppendFormat(IFormatProvider provider, string format, object arg0) => _builder.AppendFormat(provider, format, arg0); - public void AppendFormat(IFormatProvider provider, string format, object arg0, object arg1) => _builder.AppendFormat(provider, format, arg0, arg1); - public void AppendFormat(IFormatProvider provider, string format, object arg0, object arg1, object arg2) => _builder.AppendFormat(provider, format, arg0, arg1, arg2); - public void AppendFormat(string format, object arg0) => _builder.AppendFormat(format, arg0); - public void AppendFormat(string format, object arg0, object arg1) => _builder.AppendFormat(format, arg0, arg1); - public void AppendLine() => _builder.AppendLine(); - public void AppendLine(string value) => _builder.AppendLine(value); - public void Clear() => _builder.Clear(); - public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) => _builder.CopyTo(sourceIndex, destination, destinationIndex, count); - public int EnsureCapacity(int capacity) => _builder.EnsureCapacity(capacity); - public bool Equals(StringBuilder sb) => _builder.Equals(sb); - public void Insert(int index, object value) => _builder.Insert(index, value); - public void Insert(int index, byte value) => _builder.Insert(index, value); - public void Insert(int index, ulong value) => _builder.Insert(index, value); - public void Insert(int index, uint value) => _builder.Insert(index, value); - public void Insert(int index, string value) => _builder.Insert(index, value); - public void Insert(int index, decimal value) => _builder.Insert(index, value); - public void Insert(int index, string value, int count) => _builder.Insert(index, value, count); - public void Insert(int index, bool value) => _builder.Insert(index, value); - public void Insert(int index, ushort value) => _builder.Insert(index, value); - public void Insert(int index, short value) => _builder.Insert(index, value); - public void Insert(int index, char value) => _builder.Insert(index, value); - public void Insert(int index, sbyte value) => _builder.Insert(index, value); - public void Insert(int index, char[] value, int startIndex, int charCount) => _builder.Insert(index, value, startIndex, charCount); - public void Insert(int index, int value) => _builder.Insert(index, value); - public void Insert(int index, long value) => _builder.Insert(index, value); - public void Insert(int index, float value) => _builder.Insert(index, value); - public void Insert(int index, double value) => _builder.Insert(index, value); - public void Insert(int index, char[] value) => _builder.Insert(index, value); - public void Remove(int startIndex, int length) => _builder.Remove(startIndex, length); - public void Replace(string oldValue, string newValue) => _builder.Replace(oldValue, newValue); - public void Replace(string oldValue, string newValue, int startIndex, int count) => _builder.Replace(oldValue, newValue, startIndex, count); - public void Replace(char oldChar, char newChar) => _builder.Replace(oldChar, newChar); - public void Replace(char oldChar, char newChar, int startIndex, int count) => _builder.Replace(oldChar, newChar, startIndex, count); + public static PooledStringBuilder GetInstance() + { + PooledStringBuilder builder = s_poolInstance.Allocate(); + //Debug.Assert(builder.Builder.Length == 0); + return builder; } + + public static implicit operator StringBuilder(PooledStringBuilder obj) => obj._builder; + + public char this[int index] { get => _builder[index]; set => _builder[index] = value; } + public void Append(double value) => _builder.Append(value); + public void Append(char[] value) => _builder.Append(value); + public void Append(object value) => _builder.Append(value); + public void Append(ulong value) => _builder.Append(value); + public void Append(uint value) => _builder.Append(value); + public void Append(ushort value) => _builder.Append(value); + public void Append(decimal value) => _builder.Append(value); + public void Append(float value) => _builder.Append(value); + public void Append(int value) => _builder.Append(value); + public void Append(short value) => _builder.Append(value); + public void Append(char value) => _builder.Append(value); + public void Append(long value) => _builder.Append(value); + public void Append(sbyte value) => _builder.Append(value); + public void Append(byte value) => _builder.Append(value); + public void Append(char[] value, int startIndex, int charCount) => _builder.Append(value, startIndex, charCount); + public void Append(string value) => _builder.Append(value); + public void Append(string value, int startIndex, int count) => _builder.Append(value, startIndex, count); + public void Append(char value, int repeatCount) => _builder.Append(value, repeatCount); + public void Append(bool value) => _builder.Append(value); + public void AppendFormat(IFormatProvider provider, string format, params object[] args) => _builder.AppendFormat(provider, format, args); + public void AppendFormat(string format, object arg0, object arg1, object arg2) => _builder.AppendFormat(format, arg0, arg1, arg2); + public void AppendFormat(string format, params object[] args) => _builder.AppendFormat(format, args); + public void AppendFormat(IFormatProvider provider, string format, object arg0) => _builder.AppendFormat(provider, format, arg0); + public void AppendFormat(IFormatProvider provider, string format, object arg0, object arg1) => _builder.AppendFormat(provider, format, arg0, arg1); + public void AppendFormat(IFormatProvider provider, string format, object arg0, object arg1, object arg2) => _builder.AppendFormat(provider, format, arg0, arg1, arg2); + public void AppendFormat(string format, object arg0) => _builder.AppendFormat(format, arg0); + public void AppendFormat(string format, object arg0, object arg1) => _builder.AppendFormat(format, arg0, arg1); + public void AppendLine() => _builder.AppendLine(); + public void AppendLine(string value) => _builder.AppendLine(value); + public void Clear() => _builder.Clear(); + public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) => _builder.CopyTo(sourceIndex, destination, destinationIndex, count); + public int EnsureCapacity(int capacity) => _builder.EnsureCapacity(capacity); + public bool Equals(StringBuilder sb) => _builder.Equals(sb); + public void Insert(int index, object value) => _builder.Insert(index, value); + public void Insert(int index, byte value) => _builder.Insert(index, value); + public void Insert(int index, ulong value) => _builder.Insert(index, value); + public void Insert(int index, uint value) => _builder.Insert(index, value); + public void Insert(int index, string value) => _builder.Insert(index, value); + public void Insert(int index, decimal value) => _builder.Insert(index, value); + public void Insert(int index, string value, int count) => _builder.Insert(index, value, count); + public void Insert(int index, bool value) => _builder.Insert(index, value); + public void Insert(int index, ushort value) => _builder.Insert(index, value); + public void Insert(int index, short value) => _builder.Insert(index, value); + public void Insert(int index, char value) => _builder.Insert(index, value); + public void Insert(int index, sbyte value) => _builder.Insert(index, value); + public void Insert(int index, char[] value, int startIndex, int charCount) => _builder.Insert(index, value, startIndex, charCount); + public void Insert(int index, int value) => _builder.Insert(index, value); + public void Insert(int index, long value) => _builder.Insert(index, value); + public void Insert(int index, float value) => _builder.Insert(index, value); + public void Insert(int index, double value) => _builder.Insert(index, value); + public void Insert(int index, char[] value) => _builder.Insert(index, value); + public void Remove(int startIndex, int length) => _builder.Remove(startIndex, length); + public void Replace(string oldValue, string newValue) => _builder.Replace(oldValue, newValue); + public void Replace(string oldValue, string newValue, int startIndex, int count) => _builder.Replace(oldValue, newValue, startIndex, count); + public void Replace(char oldChar, char newChar) => _builder.Replace(oldChar, newChar); + public void Replace(char oldChar, char newChar, int startIndex, int count) => _builder.Replace(oldChar, newChar, startIndex, count); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractActiveConfiguredValue.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractActiveConfiguredValue.cs index b02e315e7b..a2647cf3b4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractActiveConfiguredValue.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractActiveConfiguredValue.cs @@ -1,67 +1,66 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides the base class for and . +/// +internal abstract class AbstractActiveConfiguredValue : OnceInitializedOnceDisposed { - /// - /// Provides the base class for and . - /// - internal abstract class AbstractActiveConfiguredValue : OnceInitializedOnceDisposed + private T _value = default!; + private readonly UnconfiguredProject _project; + private readonly IProjectThreadingService _threadingService; + private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; + + protected AbstractActiveConfiguredValue(UnconfiguredProject project, IActiveConfiguredProjectProvider activeConfiguredProjectProvider, IProjectThreadingService threadingService) { - private T _value = default!; - private readonly UnconfiguredProject _project; - private readonly IProjectThreadingService _threadingService; - private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; + _project = project; + _activeConfiguredProjectProvider = activeConfiguredProjectProvider; + _threadingService = threadingService; + } - protected AbstractActiveConfiguredValue(UnconfiguredProject project, IActiveConfiguredProjectProvider activeConfiguredProjectProvider, IProjectThreadingService threadingService) + public T Value + { + get { - _project = project; - _activeConfiguredProjectProvider = activeConfiguredProjectProvider; - _threadingService = threadingService; - } + EnsureInitialized(); - public T Value - { - get - { - EnsureInitialized(); - - return _value!; - } + return _value!; } + } - protected override void Initialize() - { - _activeConfiguredProjectProvider.Changed += OnActiveConfigurationChanged; + protected override void Initialize() + { + _activeConfiguredProjectProvider.Changed += OnActiveConfigurationChanged; - ConfiguredProject? configuredProject = _activeConfiguredProjectProvider.ActiveConfiguredProject; - if (configuredProject is null) + ConfiguredProject? configuredProject = _activeConfiguredProjectProvider.ActiveConfiguredProject; + if (configuredProject is null) + { + _threadingService.ExecuteSynchronously(async () => { - _threadingService.ExecuteSynchronously(async () => - { - configuredProject = await _project.GetSuggestedConfiguredProjectAsync(); - }); - } + configuredProject = await _project.GetSuggestedConfiguredProjectAsync(); + }); + } - Assumes.NotNull(configuredProject); + Assumes.NotNull(configuredProject); - SetValueForConfiguration(configuredProject); - } + SetValueForConfiguration(configuredProject); + } - protected override void Dispose(bool disposing) - { - _activeConfiguredProjectProvider.Changed -= OnActiveConfigurationChanged; - } + protected override void Dispose(bool disposing) + { + _activeConfiguredProjectProvider.Changed -= OnActiveConfigurationChanged; + } - protected abstract T GetValue(ConfiguredProject project); + protected abstract T GetValue(ConfiguredProject project); - private void OnActiveConfigurationChanged(object? sender, ActiveConfigurationChangedEventArgs e) - { - SetValueForConfiguration(e.NowActive); - } + private void OnActiveConfigurationChanged(object? sender, ActiveConfigurationChangedEventArgs e) + { + SetValueForConfiguration(e.NowActive); + } - private void SetValueForConfiguration(ConfiguredProject project) - { - _value = GetValue(project); - } + private void SetValueForConfiguration(ConfiguredProject project) + { + _value = GetValue(project); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractMultiLifetimeComponent.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractMultiLifetimeComponent.cs index 8b94c48534..aa3e196ab7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractMultiLifetimeComponent.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/AbstractMultiLifetimeComponent.cs @@ -2,130 +2,129 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// An base class that simplifies the lifetime of +/// a component that is loaded and unloaded multiple times. +/// +internal abstract class AbstractMultiLifetimeComponent : OnceInitializedOnceDisposedAsync + where T : class, IMultiLifetimeInstance { + private readonly object _lock = new(); + private TaskCompletionSource<(T instance, JoinableTask initializeAsyncTask)> _instanceTaskSource = new(TaskCreationOptions.RunContinuationsAsynchronously); + + protected AbstractMultiLifetimeComponent(JoinableTaskContextNode joinableTaskContextNode) + : base(joinableTaskContextNode) + { + } + /// - /// An base class that simplifies the lifetime of - /// a component that is loaded and unloaded multiple times. + /// Returns a task that will complete when current has completed + /// loading and has published its instance. /// - internal abstract class AbstractMultiLifetimeComponent : OnceInitializedOnceDisposedAsync - where T : class, IMultiLifetimeInstance + /// + /// The result is awaited and the is unloaded. + /// + /// -or + /// + /// The result is awaited and is cancelled. + /// + /// + /// This method does not initiate loading of the , however, + /// it will join the load when it starts. + /// + protected async Task WaitForLoadedAsync(CancellationToken cancellationToken = default) { - private readonly object _lock = new(); - private TaskCompletionSource<(T instance, JoinableTask initializeAsyncTask)> _instanceTaskSource = new(TaskCreationOptions.RunContinuationsAsynchronously); - - protected AbstractMultiLifetimeComponent(JoinableTaskContextNode joinableTaskContextNode) - : base(joinableTaskContextNode) - { - } - - /// - /// Returns a task that will complete when current has completed - /// loading and has published its instance. - /// - /// - /// The result is awaited and the is unloaded. - /// - /// -or - /// - /// The result is awaited and is cancelled. - /// - /// - /// This method does not initiate loading of the , however, - /// it will join the load when it starts. - /// - protected async Task WaitForLoadedAsync(CancellationToken cancellationToken = default) - { - // Wait until LoadAsync has been called, force switching to thread-pool in case - // there's already someone waiting for us on the UI thread. + // Wait until LoadAsync has been called, force switching to thread-pool in case + // there's already someone waiting for us on the UI thread. #pragma warning disable RS0030 // Do not used banned APIs - (T instance, JoinableTask initializeAsyncTask) = await _instanceTaskSource.Task.WithCancellation(cancellationToken) - .ConfigureAwait(false); + (T instance, JoinableTask initializeAsyncTask) = await _instanceTaskSource.Task.WithCancellation(cancellationToken) + .ConfigureAwait(false); #pragma warning restore RS0030 - // Now join Instance.InitializeAsync so that if someone is waiting on the UI thread for us, - // the instance is allowed to switch to that thread to complete if needed. - await initializeAsyncTask.JoinAsync(cancellationToken); + // Now join Instance.InitializeAsync so that if someone is waiting on the UI thread for us, + // the instance is allowed to switch to that thread to complete if needed. + await initializeAsyncTask.JoinAsync(cancellationToken); - return instance; - } - - public async Task LoadAsync() - { - await InitializeAsync(); - - await LoadCoreAsync(); - } - - public async Task LoadCoreAsync() - { - JoinableTask initializeAsyncTask; - - lock (_lock) - { - if (!_instanceTaskSource.Task.IsCompleted) - { - (T instance, JoinableTask initializeAsyncTask) result = CreateInitializedInstance(); - _instanceTaskSource.SetResult(result); - } + return instance; + } - Assumes.True(_instanceTaskSource.Task.IsCompleted); + public async Task LoadAsync() + { + await InitializeAsync(); - // Should throw TaskCanceledException if already cancelled in Dispose - (_, initializeAsyncTask) = _instanceTaskSource.Task.GetAwaiter().GetResult(); - } + await LoadCoreAsync(); + } - await initializeAsyncTask; - } + public async Task LoadCoreAsync() + { + JoinableTask initializeAsyncTask; - public Task UnloadAsync() + lock (_lock) { - T? instance = null; - lock (_lock) + if (!_instanceTaskSource.Task.IsCompleted) { - if (_instanceTaskSource.Task.IsCompleted) - { - // Should throw TaskCanceledException if already cancelled in Dispose - (instance, _) = _instanceTaskSource.Task.GetAwaiter().GetResult(); - _instanceTaskSource = new TaskCompletionSource<(T instance, JoinableTask initializeAsyncTask)>(TaskCreationOptions.RunContinuationsAsynchronously); - } + (T instance, JoinableTask initializeAsyncTask) result = CreateInitializedInstance(); + _instanceTaskSource.SetResult(result); } - if (instance is not null) - { - return instance.DisposeAsync(); - } + Assumes.True(_instanceTaskSource.Task.IsCompleted); - return Task.CompletedTask; + // Should throw TaskCanceledException if already cancelled in Dispose + (_, initializeAsyncTask) = _instanceTaskSource.Task.GetAwaiter().GetResult(); } - protected override async Task DisposeCoreAsync(bool initialized) - { - await UnloadAsync(); + await initializeAsyncTask; + } - lock (_lock) + public Task UnloadAsync() + { + T? instance = null; + lock (_lock) + { + if (_instanceTaskSource.Task.IsCompleted) { - _instanceTaskSource.TrySetCanceled(); + // Should throw TaskCanceledException if already cancelled in Dispose + (instance, _) = _instanceTaskSource.Task.GetAwaiter().GetResult(); + _instanceTaskSource = new TaskCompletionSource<(T instance, JoinableTask initializeAsyncTask)>(TaskCreationOptions.RunContinuationsAsynchronously); } } - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + if (instance is not null) { - return Task.CompletedTask; + return instance.DisposeAsync(); } - /// - /// Creates a new instance of the underlying . - /// - protected abstract T CreateInstance(); + return Task.CompletedTask; + } + + protected override async Task DisposeCoreAsync(bool initialized) + { + await UnloadAsync(); - private (T instance, JoinableTask initializeAsyncTask) CreateInitializedInstance() + lock (_lock) { - T instance = CreateInstance(); + _instanceTaskSource.TrySetCanceled(); + } + } - JoinableTask initializeAsyncTask = JoinableFactory.RunAsync(instance.InitializeAsync); + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } - return (instance, initializeAsyncTask); - } + /// + /// Creates a new instance of the underlying . + /// + protected abstract T CreateInstance(); + + private (T instance, JoinableTask initializeAsyncTask) CreateInitializedInstance() + { + T instance = CreateInstance(); + + JoinableTask initializeAsyncTask = JoinableFactory.RunAsync(instance.InitializeAsync); + + return (instance, initializeAsyncTask); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredObjects.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredObjects.cs index 81ba9e740e..1f9b528586 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredObjects.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredObjects.cs @@ -1,65 +1,64 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Represents a set of ordered active configured objects, such as objects or +/// objects, and the names of the configuration dimensions that participated in the calculation of the active configured objects. +/// +/// +/// The type of the active configured objects, typically or . +/// +internal class ActiveConfiguredObjects { /// - /// Represents a set of ordered active configured objects, such as objects or - /// objects, and the names of the configuration dimensions that participated in the calculation of the active configured objects. + /// Initializes a new instance of with the specified objects and configurations + /// dimension names. /// - /// - /// The type of the active configured objects, typically or . - /// - internal class ActiveConfiguredObjects + /// + /// An of the active configured objects. + /// + /// + /// An containing the names of the configuration dimensions that participated in + /// the calculation of the active configured objects, or empty if no dimensions participated in the calculation. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is empty. + /// + public ActiveConfiguredObjects(ImmutableArray objects, IImmutableSet dimensionNames) { - /// - /// Initializes a new instance of with the specified objects and configurations - /// dimension names. - /// - /// - /// An of the active configured objects. - /// - /// - /// An containing the names of the configuration dimensions that participated in - /// the calculation of the active configured objects, or empty if no dimensions participated in the calculation. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// is empty. - /// - public ActiveConfiguredObjects(ImmutableArray objects, IImmutableSet dimensionNames) - { - Requires.NotNull(dimensionNames); - Requires.Argument(!objects.IsDefaultOrEmpty, nameof(objects), "Must not be default or empty."); + Requires.NotNull(dimensionNames); + Requires.Argument(!objects.IsDefaultOrEmpty, nameof(objects), "Must not be default or empty."); - Objects = objects; - DimensionNames = dimensionNames; - } + Objects = objects; + DimensionNames = dimensionNames; + } - /// - /// Gets the active configured objects. - /// - /// - /// An of the active configured objects. - /// - /// - /// The order in the returned matches the declared ordered within - /// the project file. - /// - public ImmutableArray Objects { get; } + /// + /// Gets the active configured objects. + /// + /// + /// An of the active configured objects. + /// + /// + /// The order in the returned matches the declared ordered within + /// the project file. + /// + public ImmutableArray Objects { get; } - /// - /// Gets the names of the configuration dimensions that participated in the calculation of the active configured objects. - /// - /// - /// An containing the names of the configuration dimensions that participated in the - /// calculation of the active configured objects, or empty if no dimensions participated in the calculation. - /// - public IImmutableSet DimensionNames { get; } - } + /// + /// Gets the names of the configuration dimensions that participated in the calculation of the active configured objects. + /// + /// + /// An containing the names of the configuration dimensions that participated in the + /// calculation of the active configured objects, or empty if no dimensions participated in the calculation. + /// + public IImmutableSet DimensionNames { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsLoader.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsLoader.cs index 2b579f4fdf..ed4fc8199c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsLoader.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsLoader.cs @@ -2,70 +2,69 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Force loads the active objects so that any configured project-level +/// services, such as evaluation and build services, are started. +/// + +internal class ActiveConfiguredProjectsLoader : OnceInitializedOnceDisposed { - /// - /// Force loads the active objects so that any configured project-level - /// services, such as evaluation and build services, are started. - /// + private readonly UnconfiguredProject _project; + private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; + private readonly IUnconfiguredProjectTasksService _tasksService; + private readonly ITargetBlock>> _targetBlock; + private IDisposable? _subscription; - internal class ActiveConfiguredProjectsLoader : OnceInitializedOnceDisposed + [ImportingConstructor] + public ActiveConfiguredProjectsLoader(UnconfiguredProject project, IActiveConfigurationGroupService activeConfigurationGroupService, IUnconfiguredProjectTasksService tasksService) { - private readonly UnconfiguredProject _project; - private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; - private readonly IUnconfiguredProjectTasksService _tasksService; - private readonly ITargetBlock>> _targetBlock; - private IDisposable? _subscription; - - [ImportingConstructor] - public ActiveConfiguredProjectsLoader(UnconfiguredProject project, IActiveConfigurationGroupService activeConfigurationGroupService, IUnconfiguredProjectTasksService tasksService) - { - _project = project; - _activeConfigurationGroupService = activeConfigurationGroupService; - _tasksService = tasksService; - _targetBlock = DataflowBlockFactory.CreateActionBlock>>(OnActiveConfigurationsChangedAsync, project, ProjectFaultSeverity.LimitedFunctionality); - } + _project = project; + _activeConfigurationGroupService = activeConfigurationGroupService; + _tasksService = tasksService; + _targetBlock = DataflowBlockFactory.CreateActionBlock>>(OnActiveConfigurationsChangedAsync, project, ProjectFaultSeverity.LimitedFunctionality); + } - [ProjectAutoLoad(startAfter: ProjectLoadCheckpoint.ProjectInitialCapabilitiesEstablished)] - // NOTE we use the language service capability here to prevent loading configurations of shared projects. - [AppliesTo(ProjectCapability.DotNetLanguageService)] - public Task InitializeAsync() - { - EnsureInitialized(); - return Task.CompletedTask; - } + [ProjectAutoLoad(startAfter: ProjectLoadCheckpoint.ProjectInitialCapabilitiesEstablished)] + // NOTE we use the language service capability here to prevent loading configurations of shared projects. + [AppliesTo(ProjectCapability.DotNetLanguageService)] + public Task InitializeAsync() + { + EnsureInitialized(); + return Task.CompletedTask; + } - /// - /// Exposed for unit testing only. - /// - internal ITargetBlock>> TargetBlock => _targetBlock; + /// + /// Exposed for unit testing only. + /// + internal ITargetBlock>> TargetBlock => _targetBlock; - protected override void Initialize() - { - _subscription = _activeConfigurationGroupService.ActiveConfigurationGroupSource.SourceBlock.LinkTo( - target: _targetBlock, - linkOptions: DataflowOption.PropagateCompletion); - } + protected override void Initialize() + { + _subscription = _activeConfigurationGroupService.ActiveConfigurationGroupSource.SourceBlock.LinkTo( + target: _targetBlock, + linkOptions: DataflowOption.PropagateCompletion); + } - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) + { + if (disposing) { - if (disposing) - { - _subscription?.Dispose(); - _targetBlock.Complete(); - } + _subscription?.Dispose(); + _targetBlock.Complete(); } + } - private async Task OnActiveConfigurationsChangedAsync(IProjectVersionedValue> e) + private async Task OnActiveConfigurationsChangedAsync(IProjectVersionedValue> e) + { + foreach (ProjectConfiguration configuration in e.Value) { - foreach (ProjectConfiguration configuration in e.Value) + // Make sure we aren't currently unloading, or we don't unload while we load the configuration + await _tasksService.LoadedProjectAsync(() => { - // Make sure we aren't currently unloading, or we don't unload while we load the configuration - await _tasksService.LoadedProjectAsync(() => - { - return _project.LoadConfiguredProjectAsync(configuration); - }); - } + return _project.LoadConfiguredProjectAsync(configuration); + }); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsProvider.cs index a16a7bfc0c..e0423c67c8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredProjectsProvider.cs @@ -3,180 +3,179 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; using Microsoft.VisualStudio.ProjectSystem.Configuration; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Export(typeof(IActiveConfiguredProjectsProvider))] +internal class ActiveConfiguredProjectsProvider : IActiveConfiguredProjectsProvider { - [Export(typeof(IActiveConfiguredProjectsProvider))] - internal class ActiveConfiguredProjectsProvider : IActiveConfiguredProjectsProvider + // A project configuration is considered active if its dimensions matches the active solution configuration skipping + // any ignored dimensions names (provided by IActiveConfiguredProjectsDimensionProvider instances): + // + // For example, given the following cross-targeting project: + // + // -> All known project configurations: + // + // Configuration Platform TargetFramework + // ------------------------------------------- + // Debug | AnyCPU | net45 + // Debug | AnyCPU | net46 + // Release | AnyCPU | net45 + // Release | AnyCPU | net46 + // + // -> Active solution configuration: + // + // Debug | AnyCPU | net45 + // + // -> Active configurations return by this class: + // + // Debug | AnyCPU | net45 + // Debug | AnyCPU | net46 + // + // Whereas, given the following non-cross-targeting project: + // + // -> All known project configurations: + // + // Configuration Platform + // ------------------------ + // Debug | AnyCPU + // Release | AnyCPU + // + // -> Active solution configuration: + // + // Debug | AnyCPU + // + // -> Active configurations return by this class: + // + // Debug | AnyCPU + + private readonly IUnconfiguredProjectServices _services; + private readonly UnconfiguredProject _project; + + [ImportingConstructor] + public ActiveConfiguredProjectsProvider(IUnconfiguredProjectServices services, UnconfiguredProject project) { - // A project configuration is considered active if its dimensions matches the active solution configuration skipping - // any ignored dimensions names (provided by IActiveConfiguredProjectsDimensionProvider instances): - // - // For example, given the following cross-targeting project: - // - // -> All known project configurations: - // - // Configuration Platform TargetFramework - // ------------------------------------------- - // Debug | AnyCPU | net45 - // Debug | AnyCPU | net46 - // Release | AnyCPU | net45 - // Release | AnyCPU | net46 - // - // -> Active solution configuration: - // - // Debug | AnyCPU | net45 - // - // -> Active configurations return by this class: - // - // Debug | AnyCPU | net45 - // Debug | AnyCPU | net46 - // - // Whereas, given the following non-cross-targeting project: - // - // -> All known project configurations: - // - // Configuration Platform - // ------------------------ - // Debug | AnyCPU - // Release | AnyCPU - // - // -> Active solution configuration: - // - // Debug | AnyCPU - // - // -> Active configurations return by this class: - // - // Debug | AnyCPU - - private readonly IUnconfiguredProjectServices _services; - private readonly UnconfiguredProject _project; - - [ImportingConstructor] - public ActiveConfiguredProjectsProvider(IUnconfiguredProjectServices services, UnconfiguredProject project) - { - _services = services; - _project = project; + _services = services; + _project = project; - DimensionProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + DimensionProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - public OrderPrecedenceImportCollection DimensionProviders { get; } + [ImportMany] + public OrderPrecedenceImportCollection DimensionProviders { get; } - public async Task?> GetActiveConfiguredProjectsMapAsync() - { - ActiveConfiguredObjects? projects = await GetActiveConfiguredProjectsAsync(); + public async Task?> GetActiveConfiguredProjectsMapAsync() + { + ActiveConfiguredObjects? projects = await GetActiveConfiguredProjectsAsync(); - if (projects?.Objects.IsEmpty != false) - { - return null; - } + if (projects?.Objects.IsEmpty != false) + { + return null; + } - var builder = PooledDictionary.GetInstance(); + var builder = PooledDictionary.GetInstance(); - bool isCrossTargeting = projects.Objects.All(project => project.ProjectConfiguration.IsCrossTargeting()); + bool isCrossTargeting = projects.Objects.All(project => project.ProjectConfiguration.IsCrossTargeting()); - if (isCrossTargeting) - { - foreach (ConfiguredProject project in projects.Objects) - { - string targetFramework = project.ProjectConfiguration.Dimensions[ConfigurationGeneral.TargetFrameworkProperty]; - builder.Add(targetFramework, project); - } - } - else + if (isCrossTargeting) + { + foreach (ConfiguredProject project in projects.Objects) { - builder.Add(string.Empty, projects.Objects[0]); + string targetFramework = project.ProjectConfiguration.Dimensions[ConfigurationGeneral.TargetFrameworkProperty]; + builder.Add(targetFramework, project); } - - return builder.ToImmutableDictionaryAndFree(); } - - public async Task?> GetActiveConfiguredProjectsAsync() + else { - ActiveConfiguredObjects? configurations = await GetActiveProjectConfigurationsAsync(); + builder.Add(string.Empty, projects.Objects[0]); + } - if (configurations is null) - { - return null; - } + return builder.ToImmutableDictionaryAndFree(); + } - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(configurations.Objects.Length); + public async Task?> GetActiveConfiguredProjectsAsync() + { + ActiveConfiguredObjects? configurations = await GetActiveProjectConfigurationsAsync(); - foreach (ProjectConfiguration configuration in configurations.Objects) - { - ConfiguredProject project = await _project.LoadConfiguredProjectAsync(configuration); + if (configurations is null) + { + return null; + } - builder.Add(project); - } + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(configurations.Objects.Length); + + foreach (ProjectConfiguration configuration in configurations.Objects) + { + ConfiguredProject project = await _project.LoadConfiguredProjectAsync(configuration); - return new ActiveConfiguredObjects(builder.MoveToImmutable(), configurations.DimensionNames); + builder.Add(project); } - public async Task?> GetActiveProjectConfigurationsAsync() - { - ProjectConfiguration? activeSolutionConfiguration = _services.ActiveConfiguredProjectProvider?.ActiveProjectConfiguration; + return new ActiveConfiguredObjects(builder.MoveToImmutable(), configurations.DimensionNames); + } - if (activeSolutionConfiguration is null) - { - return null; - } + public async Task?> GetActiveProjectConfigurationsAsync() + { + ProjectConfiguration? activeSolutionConfiguration = _services.ActiveConfiguredProjectProvider?.ActiveProjectConfiguration; - IProjectConfigurationsService? projectConfigurationsService = _services.ProjectConfigurationsService; - Assumes.Present(projectConfigurationsService); + if (activeSolutionConfiguration is null) + { + return null; + } - IImmutableSet configurations = await projectConfigurationsService.GetKnownProjectConfigurationsAsync(); + IProjectConfigurationsService? projectConfigurationsService = _services.ProjectConfigurationsService; + Assumes.Present(projectConfigurationsService); - var builder = PooledArray.GetInstance(); - IImmutableSet dimensionNames = GetDimensionNames(); + IImmutableSet configurations = await projectConfigurationsService.GetKnownProjectConfigurationsAsync(); - foreach (ProjectConfiguration configuration in configurations) - { - if (IsActiveConfigurationCandidate(activeSolutionConfiguration, configuration, dimensionNames)) - { - builder.Add(configuration); - } - } + var builder = PooledArray.GetInstance(); + IImmutableSet dimensionNames = GetDimensionNames(); - if (builder.Count == 0) - { - // Active config is different to the known configs, - // however we should still return it - builder.Add(activeSolutionConfiguration); + foreach (ProjectConfiguration configuration in configurations) + { + if (IsActiveConfigurationCandidate(activeSolutionConfiguration, configuration, dimensionNames)) + { + builder.Add(configuration); } + } - return new ActiveConfiguredObjects(builder.ToImmutableAndFree(), dimensionNames); + if (builder.Count == 0) + { + // Active config is different to the known configs, + // however we should still return it + builder.Add(activeSolutionConfiguration); } - private IImmutableSet GetDimensionNames() - { - ImmutableHashSet.Builder builder = ImmutableHashSet.CreateBuilder(StringComparers.ConfigurationDimensionNames); + return new ActiveConfiguredObjects(builder.ToImmutableAndFree(), dimensionNames); + } - foreach (Lazy dimensionProvider in DimensionProviders) - { - builder.Add(dimensionProvider.Value.DimensionName); - } + private IImmutableSet GetDimensionNames() + { + ImmutableHashSet.Builder builder = ImmutableHashSet.CreateBuilder(StringComparers.ConfigurationDimensionNames); - return builder.ToImmutable(); + foreach (Lazy dimensionProvider in DimensionProviders) + { + builder.Add(dimensionProvider.Value.DimensionName); } - private static bool IsActiveConfigurationCandidate(ProjectConfiguration activeSolutionConfiguration, ProjectConfiguration configuration, IImmutableSet ignoredDimensionNames) + return builder.ToImmutable(); + } + + private static bool IsActiveConfigurationCandidate(ProjectConfiguration activeSolutionConfiguration, ProjectConfiguration configuration, IImmutableSet ignoredDimensionNames) + { + foreach ((string dimensionName, string dimensionValue) in activeSolutionConfiguration.Dimensions) { - foreach ((string dimensionName, string dimensionValue) in activeSolutionConfiguration.Dimensions) + if (ignoredDimensionNames.Contains(dimensionName)) { - if (ignoredDimensionNames.Contains(dimensionName)) - { - continue; - } - - if (!configuration.Dimensions.TryGetValue(dimensionName, out string? otherDimensionValue) || - !string.Equals(dimensionValue, otherDimensionValue, StringComparisons.ConfigurationDimensionNames)) - { - return false; - } + continue; } - return true; + if (!configuration.Dimensions.TryGetValue(dimensionName, out string? otherDimensionValue) || + !string.Equals(dimensionValue, otherDimensionValue, StringComparisons.ConfigurationDimensionNames)) + { + return false; + } } + + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValue.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValue.cs index 3fc064c943..f516214e73 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValue.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValue.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Export(typeof(IActiveConfiguredValue<>))] +internal class ActiveConfiguredValue : AbstractActiveConfiguredValue, IActiveConfiguredValue + where T : class? { - [Export(typeof(IActiveConfiguredValue<>))] - internal class ActiveConfiguredValue : AbstractActiveConfiguredValue, IActiveConfiguredValue - where T : class? + [ImportingConstructor] + public ActiveConfiguredValue( + UnconfiguredProject project, + IActiveConfiguredProjectProvider activeConfiguredProjectProvider, + IProjectThreadingService threadingService) + : base(project, activeConfiguredProjectProvider, threadingService) { - [ImportingConstructor] - public ActiveConfiguredValue( - UnconfiguredProject project, - IActiveConfiguredProjectProvider activeConfiguredProjectProvider, - IProjectThreadingService threadingService) - : base(project, activeConfiguredProjectProvider, threadingService) - { - } + } - protected override T GetValue(ConfiguredProject project) - { - return project.Services.ExportProvider.GetExportedValueOrDefault()!; - } + protected override T GetValue(ConfiguredProject project) + { + return project.Services.ExportProvider.GetExportedValueOrDefault()!; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValues.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValues.cs index 488c955915..1d4a093ce9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValues.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ActiveConfiguredValues.cs @@ -2,44 +2,43 @@ using Microsoft.VisualStudio.Composition; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Export(typeof(IActiveConfiguredValues<>))] +internal class ActiveConfiguredValues : + AbstractActiveConfiguredValue, // NOTE: Typed as 'object' because of https://github.com/microsoft/vs-mef/issues/180 + IActiveConfiguredValues + where T : class { - [Export(typeof(IActiveConfiguredValues<>))] - internal class ActiveConfiguredValues : - AbstractActiveConfiguredValue, // NOTE: Typed as 'object' because of https://github.com/microsoft/vs-mef/issues/180 - IActiveConfiguredValues - where T : class + [ImportingConstructor] + public ActiveConfiguredValues( + UnconfiguredProject project, + IActiveConfiguredProjectProvider activeConfiguredProjectProvider, + IProjectThreadingService threadingService) + : base(project, activeConfiguredProjectProvider, threadingService) { - [ImportingConstructor] - public ActiveConfiguredValues( - UnconfiguredProject project, - IActiveConfiguredProjectProvider activeConfiguredProjectProvider, - IProjectThreadingService threadingService) - : base(project, activeConfiguredProjectProvider, threadingService) - { - } + } - public IEnumerable> Values => (IEnumerable>)Value; + public IEnumerable> Values => (IEnumerable>)Value; - protected override object GetValue(ConfiguredProject project) - { - // Get the "natural" (unfiltered) export provider so that can we pull all the possible - // values, not just the ones that are applicable to the current set of capabilities when - // we call this. - // - // This so that when capabilities change over time, the resulting OrderPrecedenceImportCollection - // responds to the changes and filters the list based on the new set of capabilities. - // - // This basically mimics importing OrderPrecedenceImportCollection directly. - ExportProvider provider = project.Services.ExportProvider.GetExportedValue(); - - var values = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - foreach (Lazy value in provider.GetExports()) - { - values.Add(value); - } + protected override object GetValue(ConfiguredProject project) + { + // Get the "natural" (unfiltered) export provider so that can we pull all the possible + // values, not just the ones that are applicable to the current set of capabilities when + // we call this. + // + // This so that when capabilities change over time, the resulting OrderPrecedenceImportCollection + // responds to the changes and filters the list based on the new set of capabilities. + // + // This basically mimics importing OrderPrecedenceImportCollection directly. + ExportProvider provider = project.Services.ExportProvider.GetExportedValue(); - return values; + var values = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + foreach (Lazy value in provider.GetExports()) + { + values.Add(value); } + + return values; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/BuildProperty.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/BuildProperty.cs index 7fb9e4d2de..20e91ab1bb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/BuildProperty.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/BuildProperty.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Contains common MSBuild build properties. +/// +internal static class BuildProperty { /// - /// Contains common MSBuild build properties. + /// Indicates whether CoreCompile target should skip compiler execution completely. /// - internal static class BuildProperty - { - /// - /// Indicates whether CoreCompile target should skip compiler execution completely. - /// - public static string SkipCompilerExecution = nameof(SkipCompilerExecution); + public static string SkipCompilerExecution = nameof(SkipCompilerExecution); - /// - /// Indicates whether CoreCompile target should output the command-line - /// that would have been passed to Csc.exe and Vbc.exe. - /// - public static string ProvideCommandLineArgs = nameof(ProvideCommandLineArgs); + /// + /// Indicates whether CoreCompile target should output the command-line + /// that would have been passed to Csc.exe and Vbc.exe. + /// + public static string ProvideCommandLineArgs = nameof(ProvideCommandLineArgs); - /// - /// Indicates whether Csc/Vbc tasks should call into the in-proc host compiler. - /// - public static string UseHostCompilerIfAvailable = nameof(UseHostCompilerIfAvailable); + /// + /// Indicates whether Csc/Vbc tasks should call into the in-proc host compiler. + /// + public static string UseHostCompilerIfAvailable = nameof(UseHostCompilerIfAvailable); - /// - /// Represents the GUID of the project used for uniqueness. - /// - public static string ProjectGuid = nameof(ProjectGuid); - } + /// + /// Represents the GUID of the project used for uniqueness. + /// + public static string ProjectGuid = nameof(ProjectGuid); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/CommandLineDesignTimeBuildPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/CommandLineDesignTimeBuildPropertiesProvider.cs index 87addab1ae..e73452fb7e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/CommandLineDesignTimeBuildPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/CommandLineDesignTimeBuildPropertiesProvider.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Influences design-time builds that causes CoreCompile to return the command-line +/// arguments that it would have passed to the compiler, instead of calling it. +/// +[ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: true)] +[AppliesTo(ProjectCapability.DotNet)] +internal class CommandLineDesignTimeBuildPropertiesProvider : StaticGlobalPropertiesProviderBase { - /// - /// Influences design-time builds that causes CoreCompile to return the command-line - /// arguments that it would have passed to the compiler, instead of calling it. - /// - [ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: true)] - [AppliesTo(ProjectCapability.DotNet)] - internal class CommandLineDesignTimeBuildPropertiesProvider : StaticGlobalPropertiesProviderBase - { - private static readonly Task> s_buildProperties = Task.FromResult>( - Empty.PropertiesMap.Add(BuildProperty.SkipCompilerExecution, "true") // Don't run the compiler - .Add(BuildProperty.ProvideCommandLineArgs, "true")); // Get csc/vbc to output command-line args + private static readonly Task> s_buildProperties = Task.FromResult>( + Empty.PropertiesMap.Add(BuildProperty.SkipCompilerExecution, "true") // Don't run the compiler + .Add(BuildProperty.ProvideCommandLineArgs, "true")); // Get csc/vbc to output command-line args - [ImportingConstructor] - public CommandLineDesignTimeBuildPropertiesProvider(IProjectService projectService) - : base(projectService.Services) - { - } + [ImportingConstructor] + public CommandLineDesignTimeBuildPropertiesProvider(IProjectService projectService) + : base(projectService.Services) + { + } - public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - return s_buildProperties; - } + public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + return s_buildProperties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildDesignTimeBuildPropertyProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildDesignTimeBuildPropertyProvider.cs index 19fb53f515..648f23e1d5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildDesignTimeBuildPropertyProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildDesignTimeBuildPropertyProvider.cs @@ -1,34 +1,33 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Design time build property provider for . +/// +[ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: true)] +[AppliesTo(ProjectCapability.Pack)] +internal class GeneratePackageOnBuildDesignTimeBuildPropertyProvider : StaticGlobalPropertiesProviderBase { + private readonly Task> _properties; + /// - /// Design time build property provider for . + /// Initializes a new instance of the class. /// - [ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: true)] - [AppliesTo(ProjectCapability.Pack)] - internal class GeneratePackageOnBuildDesignTimeBuildPropertyProvider : StaticGlobalPropertiesProviderBase + [ImportingConstructor] + internal GeneratePackageOnBuildDesignTimeBuildPropertyProvider(IProjectService projectService) + : base(projectService.Services) { - private readonly Task> _properties; - - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - internal GeneratePackageOnBuildDesignTimeBuildPropertyProvider(IProjectService projectService) - : base(projectService.Services) - { - // Never generate NuGet package during design time build. - _properties = Task.FromResult>(Empty.PropertiesMap.Add(ConfigurationGeneralBrowseObject.GeneratePackageOnBuildProperty, "false")); - } + // Never generate NuGet package during design time build. + _properties = Task.FromResult>(Empty.PropertiesMap.Add(ConfigurationGeneralBrowseObject.GeneratePackageOnBuildProperty, "false")); + } - /// - /// Gets the set of global properties that should apply to the project(s) in this scope. - /// - /// A map whose keys are case insensitive. Never null, but may be empty. - public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - return _properties; - } + /// + /// Gets the set of global properties that should apply to the project(s) in this scope. + /// + /// A map whose keys are case insensitive. Never null, but may be empty. + public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + return _properties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildPropertyProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildPropertyProvider.cs index 4eeffd7f3f..ee44d537e4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildPropertyProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/GeneratePackageOnBuildPropertyProvider.cs @@ -1,47 +1,46 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Build property provider for for solution build. +/// +[ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: false)] +[Export(typeof(GeneratePackageOnBuildPropertyProvider))] +[AppliesTo(ProjectCapability.Pack)] +internal class GeneratePackageOnBuildPropertyProvider : StaticGlobalPropertiesProviderBase { + private Task> _properties = Task.FromResult>(Empty.PropertiesMap); + /// - /// Build property provider for for solution build. + /// Initializes a new instance of the class. /// - [ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: false)] - [Export(typeof(GeneratePackageOnBuildPropertyProvider))] - [AppliesTo(ProjectCapability.Pack)] - internal class GeneratePackageOnBuildPropertyProvider : StaticGlobalPropertiesProviderBase + [ImportingConstructor] + internal GeneratePackageOnBuildPropertyProvider(IProjectService projectService) + : base(projectService.Services) { - private Task> _properties = Task.FromResult>(Empty.PropertiesMap); - - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - internal GeneratePackageOnBuildPropertyProvider(IProjectService projectService) - : base(projectService.Services) - { - } + } - /// - /// Overrides the value of GeneratePackageOnBuild to the value specified, or resets to the project property value if is passed in. - /// - public void OverrideGeneratePackageOnBuild(bool? value) - { - _properties = Task.FromResult>( - value switch - { - null => Empty.PropertiesMap, - true => Empty.PropertiesMap.Add(ConfigurationGeneralBrowseObject.GeneratePackageOnBuildProperty, "true"), - false => Empty.PropertiesMap.Add(ConfigurationGeneralBrowseObject.GeneratePackageOnBuildProperty, "false") - }); - } + /// + /// Overrides the value of GeneratePackageOnBuild to the value specified, or resets to the project property value if is passed in. + /// + public void OverrideGeneratePackageOnBuild(bool? value) + { + _properties = Task.FromResult>( + value switch + { + null => Empty.PropertiesMap, + true => Empty.PropertiesMap.Add(ConfigurationGeneralBrowseObject.GeneratePackageOnBuildProperty, "true"), + false => Empty.PropertiesMap.Add(ConfigurationGeneralBrowseObject.GeneratePackageOnBuildProperty, "false") + }); + } - /// - /// Gets the set of global properties that should apply to the project(s) in this scope. - /// - /// A map whose keys are case insensitive. Never null, but may be empty. - public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - return _properties; - } + /// + /// Gets the set of global properties that should apply to the project(s) in this scope. + /// + /// A map whose keys are case insensitive. Never null, but may be empty. + public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + return _properties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildManager2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildManager2.cs index 6ec604dcea..dcd8b0f2e3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildManager2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildManager2.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build -{ +namespace Microsoft.VisualStudio.ProjectSystem.Build; + #pragma warning disable CS0618 // Type or member is obsolete - IImplicitlyTriggeredBuildManager is marked obsolete as it may eventually be replaced with a different API. - internal interface IImplicitlyTriggeredBuildManager2 : IImplicitlyTriggeredBuildManager +internal interface IImplicitlyTriggeredBuildManager2 : IImplicitlyTriggeredBuildManager #pragma warning restore CS0618 // Type or member is obsolete - { - /// - /// An alternative to - /// that takes the set of startup projects associated with this implicit build, if - /// any. Consumers should call one or the other of these methods, but not both. - /// - /// - /// Note that the set of startup projects is not always associated with an implicit - /// build. For example, if the build is happening so that VS can run unit tests the - /// set of startup projects isn't relevant and is unlikely to include the unit test - /// project, anyway. In those sorts of cases it is expected that - /// will be used instead. - /// - void OnBuildStart(ImmutableArray startupProjectFullPaths); - } +{ + /// + /// An alternative to + /// that takes the set of startup projects associated with this implicit build, if + /// any. Consumers should call one or the other of these methods, but not both. + /// + /// + /// Note that the set of startup projects is not always associated with an implicit + /// build. For example, if the build is happening so that VS can run unit tests the + /// set of startup projects isn't relevant and is unlikely to include the unit test + /// project, anyway. In those sorts of cases it is expected that + /// will be used instead. + /// + void OnBuildStart(ImmutableArray startupProjectFullPaths); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildState.cs index 677d6cb928..4a982ec167 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/IImplicitlyTriggeredBuildState.cs @@ -2,33 +2,32 @@ using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build +namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build; + +/// +/// A counterpart to the that exposes +/// whether or not we are currently in an implicitly-triggered build (that is, a +/// build that is run as an incidental part of F5, Ctrl+F5, running tests, etc.). +/// This is primarily intended for use by implementations of +/// to adjust global build properties. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IImplicitlyTriggeredBuildState { /// - /// A counterpart to the that exposes - /// whether or not we are currently in an implicitly-triggered build (that is, a - /// build that is run as an incidental part of F5, Ctrl+F5, running tests, etc.). - /// This is primarily intended for use by implementations of - /// to adjust global build properties. + /// Indicates if the current build (if any) is implicitly triggered. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IImplicitlyTriggeredBuildState - { - /// - /// Indicates if the current build (if any) is implicitly triggered. - /// - bool IsImplicitlyTriggeredBuild { get; } + bool IsImplicitlyTriggeredBuild { get; } - /// - /// The full paths to any startup projects associated with an implicitly triggered - /// build. - /// - /// - /// Even if there are designated startup projects in VS, not every implicit build is - /// associated with those projects. For example, the startup projects are not - /// relevant when running an implicit build as part of executing or debugging unit - /// tests. - /// - ImmutableArray StartupProjectFullPaths { get; } - } + /// + /// The full paths to any startup projects associated with an implicitly triggered + /// build. + /// + /// + /// Even if there are designated startup projects in VS, not every implicit build is + /// associated with those projects. For example, the startup projects are not + /// relevant when running an implicit build as part of executing or debugging unit + /// tests. + /// + ImmutableArray StartupProjectFullPaths { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyActiveConfiguredProjectReadyToBuild.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyActiveConfiguredProjectReadyToBuild.cs index bcd905844f..86442dc66e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyActiveConfiguredProjectReadyToBuild.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyActiveConfiguredProjectReadyToBuild.cs @@ -1,74 +1,73 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +[Export(typeof(IConfiguredProjectReadyToBuild))] +[AppliesTo(ProjectCapability.DotNetLanguageService)] +[Order(Order.Default)] +internal sealed class ImplicitlyActiveConfiguredProjectReadyToBuild : IConfiguredProjectReadyToBuild, IDisposable { - [Export(typeof(IConfiguredProjectReadyToBuild))] - [AppliesTo(ProjectCapability.DotNetLanguageService)] - [Order(Order.Default)] - internal sealed class ImplicitlyActiveConfiguredProjectReadyToBuild : IConfiguredProjectReadyToBuild, IDisposable - { - private readonly ConfiguredProject _configuredProject; - private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; + private readonly ConfiguredProject _configuredProject; + private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; - private TaskCompletionSource _activationTask; + private TaskCompletionSource _activationTask; - [ImportingConstructor] - public ImplicitlyActiveConfiguredProjectReadyToBuild( - ConfiguredProject configuredProject, - IActiveConfiguredProjectProvider activeConfiguredProjectProvider) - { - _configuredProject = configuredProject; - _activeConfiguredProjectProvider = activeConfiguredProjectProvider; - _activationTask = new TaskCompletionSource(); + [ImportingConstructor] + public ImplicitlyActiveConfiguredProjectReadyToBuild( + ConfiguredProject configuredProject, + IActiveConfiguredProjectProvider activeConfiguredProjectProvider) + { + _configuredProject = configuredProject; + _activeConfiguredProjectProvider = activeConfiguredProjectProvider; + _activationTask = new TaskCompletionSource(); - _activeConfiguredProjectProvider.Changed += ActiveConfiguredProject_Changed; - } + _activeConfiguredProjectProvider.Changed += ActiveConfiguredProject_Changed; + } - private void ActiveConfiguredProject_Changed(object sender, ActiveConfigurationChangedEventArgs e) - { - _ = GetLatestActivationTask(); - } + private void ActiveConfiguredProject_Changed(object sender, ActiveConfigurationChangedEventArgs e) + { + _ = GetLatestActivationTask(); + } - public bool IsValidToBuild => GetLatestActivationTask().IsCompleted; + public bool IsValidToBuild => GetLatestActivationTask().IsCompleted; - public Task WaitReadyToBuildAsync() => GetLatestActivationTask(); + public Task WaitReadyToBuildAsync() => GetLatestActivationTask(); - private Task GetLatestActivationTask() + private Task GetLatestActivationTask() + { + lock (_configuredProject) { - lock (_configuredProject) + bool previouslyActive = _activationTask.Task.IsCompleted; + bool nowActive = IsActive(); + if (previouslyActive) { - bool previouslyActive = _activationTask.Task.IsCompleted; - bool nowActive = IsActive(); - if (previouslyActive) - { - if (!nowActive) - { - _activationTask = new TaskCompletionSource(); - } - } - else if (nowActive) + if (!nowActive) { - _activationTask.TrySetResult(); + _activationTask = new TaskCompletionSource(); } - - return _activationTask.Task; } - } + else if (nowActive) + { + _activationTask.TrySetResult(); + } - public void Dispose() - { - _activationTask.TrySetCanceled(); - _activeConfiguredProjectProvider.Changed -= ActiveConfiguredProject_Changed; + return _activationTask.Task; } + } - private bool IsActive() - { - ProjectConfiguration? activeConfig = _activeConfiguredProjectProvider.ActiveProjectConfiguration; + public void Dispose() + { + _activationTask.TrySetCanceled(); + _activeConfiguredProjectProvider.Changed -= ActiveConfiguredProject_Changed; + } - if (activeConfig is null) - return false; + private bool IsActive() + { + ProjectConfiguration? activeConfig = _activeConfiguredProjectProvider.ActiveProjectConfiguration; - return _configuredProject.ProjectConfiguration.EqualIgnoringTargetFramework(activeConfig); - } + if (activeConfig is null) + return false; + + return _configuredProject.ProjectConfiguration.EqualIgnoringTargetFramework(activeConfig); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyTriggeredBuildManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyTriggeredBuildManager.cs index 92a9639c71..710c9c3438 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyTriggeredBuildManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/ImplicitlyTriggeredBuildManager.cs @@ -2,41 +2,40 @@ using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build -{ +namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build; + #pragma warning disable CS0618 // Type or member is obsolete - IImplicitlyTriggeredBuildManager is marked obsolete as it may eventually be replaced with a different API. - /// - /// Build manager for implicitly triggered builds from commands such as Run/Debug Tests, Start Debugging, etc. - /// - [Export(typeof(IImplicitlyTriggeredBuildManager))] - [Export(typeof(IImplicitlyTriggeredBuildState))] - [AppliesTo(ProjectCapability.DotNet)] - internal sealed partial class ImplicitlyTriggeredBuildManager : - IImplicitlyTriggeredBuildManager, - IImplicitlyTriggeredBuildManager2, - IImplicitlyTriggeredBuildState +/// +/// Build manager for implicitly triggered builds from commands such as Run/Debug Tests, Start Debugging, etc. +/// +[Export(typeof(IImplicitlyTriggeredBuildManager))] +[Export(typeof(IImplicitlyTriggeredBuildState))] +[AppliesTo(ProjectCapability.DotNet)] +internal sealed partial class ImplicitlyTriggeredBuildManager : + IImplicitlyTriggeredBuildManager, + IImplicitlyTriggeredBuildManager2, + IImplicitlyTriggeredBuildState #pragma warning restore CS0618 // Type or member is obsolete - { - private bool _isImplicitlyTriggeredBuild; - private ImmutableArray _startupProjectFullPaths = ImmutableArray.Empty; +{ + private bool _isImplicitlyTriggeredBuild; + private ImmutableArray _startupProjectFullPaths = ImmutableArray.Empty; - public bool IsImplicitlyTriggeredBuild => _isImplicitlyTriggeredBuild; + public bool IsImplicitlyTriggeredBuild => _isImplicitlyTriggeredBuild; - public ImmutableArray StartupProjectFullPaths => _startupProjectFullPaths; + public ImmutableArray StartupProjectFullPaths => _startupProjectFullPaths; - public void OnBuildStart() - => OnBuildStart(ImmutableArray.Empty); + public void OnBuildStart() + => OnBuildStart(ImmutableArray.Empty); - public void OnBuildStart(ImmutableArray startupProjectFullPaths) - { - _isImplicitlyTriggeredBuild = true; - _startupProjectFullPaths = startupProjectFullPaths; - } + public void OnBuildStart(ImmutableArray startupProjectFullPaths) + { + _isImplicitlyTriggeredBuild = true; + _startupProjectFullPaths = startupProjectFullPaths; + } - public void OnBuildEndOrCancel() - { - _isImplicitlyTriggeredBuild = false; - _startupProjectFullPaths = ImmutableArray.Empty; - } + public void OnBuildEndOrCancel() + { + _isImplicitlyTriggeredBuild = false; + _startupProjectFullPaths = ImmutableArray.Empty; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/OutputGroup.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/OutputGroup.cs index bbff0612a1..546c7b4cfa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/OutputGroup.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/OutputGroup.cs @@ -2,42 +2,41 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// A set of outputs generated by a project that fit under some common category. +/// +[DebuggerDisplay("Output Group {Name} ({TargetName})")] +internal class OutputGroup : IOutputGroup { /// - /// A set of outputs generated by a project that fit under some common category. + /// Initializes a new instance of the class. /// - [DebuggerDisplay("Output Group {Name} ({TargetName})")] - internal class OutputGroup : IOutputGroup + internal OutputGroup(string name, string targetName, string displayName, string? description, IImmutableList>> items, bool successful) { - /// - /// Initializes a new instance of the class. - /// - internal OutputGroup(string name, string targetName, string displayName, string? description, IImmutableList>> items, bool successful) - { - Requires.NotNullOrEmpty(name); - Requires.NotNullOrEmpty(targetName); - Requires.NotNullOrEmpty(displayName); - Requires.NotNull(items); - - Name = name; - TargetName = targetName; - DisplayName = displayName; - Description = description; - Outputs = items; - IsSuccessful = successful; - } + Requires.NotNullOrEmpty(name); + Requires.NotNullOrEmpty(targetName); + Requires.NotNullOrEmpty(displayName); + Requires.NotNull(items); + + Name = name; + TargetName = targetName; + DisplayName = displayName; + Description = description; + Outputs = items; + IsSuccessful = successful; + } - public string TargetName { get; } + public string TargetName { get; } - public string Name { get; } + public string Name { get; } - public string DisplayName { get; } + public string DisplayName { get; } - public string? Description { get; } + public string? Description { get; } - public bool IsSuccessful { get; } + public bool IsSuccessful { get; } - public IImmutableList>> Outputs { get; } - } + public IImmutableList>> Outputs { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishItemsOutputGroupProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishItemsOutputGroupProvider.cs index b6a65f149b..864ffad252 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishItemsOutputGroupProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishItemsOutputGroupProvider.cs @@ -2,52 +2,51 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +[Export(typeof(IOutputGroupProvider))] +[AppliesTo(ProjectCapabilities.VisualStudioWellKnownOutputGroups)] +[Order(Order.Default)] +internal class PublishItemsOutputGroupProvider : IOutputGroupProvider { - [Export(typeof(IOutputGroupProvider))] - [AppliesTo(ProjectCapabilities.VisualStudioWellKnownOutputGroups)] - [Order(Order.Default)] - internal class PublishItemsOutputGroupProvider : IOutputGroupProvider + private const string PublishItemsOutputGroupTargetName = "PublishItemsOutputGroup"; + + /// + /// Collection containing a single "publish items" output group. + /// This is a singleton instance, shared across all projects/configurations. + /// + private static readonly ImmutableHashSet s_outputGroups = ImmutableHashSet.Create( + new OutputGroup( + name: "PublishItems", + targetName: PublishItemsOutputGroupTargetName, + displayName: Resources.OutputGroupPublishItemsDisplayName, + description: Resources.OutputGroupPublishItemsDescription, + items: ImmutableList>>.Empty, + successful: false)); + + private readonly AsyncLazy> _outputGroups; + + [ImportingConstructor] + internal PublishItemsOutputGroupProvider( + IProjectAccessor projectAccessor, + ConfiguredProject configuredProject, + IProjectThreadingService projectThreadingService) { - private const string PublishItemsOutputGroupTargetName = "PublishItemsOutputGroup"; - - /// - /// Collection containing a single "publish items" output group. - /// This is a singleton instance, shared across all projects/configurations. - /// - private static readonly ImmutableHashSet s_outputGroups = ImmutableHashSet.Create( - new OutputGroup( - name: "PublishItems", - targetName: PublishItemsOutputGroupTargetName, - displayName: Resources.OutputGroupPublishItemsDisplayName, - description: Resources.OutputGroupPublishItemsDescription, - items: ImmutableList>>.Empty, - successful: false)); - - private readonly AsyncLazy> _outputGroups; - - [ImportingConstructor] - internal PublishItemsOutputGroupProvider( - IProjectAccessor projectAccessor, - ConfiguredProject configuredProject, - IProjectThreadingService projectThreadingService) + _outputGroups = new AsyncLazy>( + GetOutputGroupMetadataAsync, + projectThreadingService.JoinableTaskFactory); + + async Task> GetOutputGroupMetadataAsync() { - _outputGroups = new AsyncLazy>( - GetOutputGroupMetadataAsync, - projectThreadingService.JoinableTaskFactory); - - async Task> GetOutputGroupMetadataAsync() - { - bool hasPublishItemsTarget = await projectAccessor.OpenProjectForReadAsync( - configuredProject, - project => project.Targets.ContainsKey(PublishItemsOutputGroupTargetName)); - - return hasPublishItemsTarget - ? s_outputGroups - : ImmutableHashSet.Empty; - } - } + bool hasPublishItemsTarget = await projectAccessor.OpenProjectForReadAsync( + configuredProject, + project => project.Targets.ContainsKey(PublishItemsOutputGroupTargetName)); - public Task> OutputGroups => _outputGroups.GetValueAsync(); + return hasPublishItemsTarget + ? s_outputGroups + : ImmutableHashSet.Empty; + } } + + public Task> OutputGroups => _outputGroups.GetValueAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishableProjectConfigProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishableProjectConfigProvider.cs index 269755b2a6..a719372859 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishableProjectConfigProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/PublishableProjectConfigProvider.cs @@ -2,29 +2,28 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Provides publishable project config for projects that support click once publishing. +/// +[Export(typeof(IPublishProvider))] +[AppliesTo(ProjectCapability.DotNet)] +internal class PublishableProjectConfigProvider : IPublishProvider { - /// - /// Provides publishable project config for projects that support click once publishing. - /// - [Export(typeof(IPublishProvider))] - [AppliesTo(ProjectCapability.DotNet)] - internal class PublishableProjectConfigProvider : IPublishProvider + public Task IsPublishSupportedAsync() { - public Task IsPublishSupportedAsync() - { - // No support for ClickOnce publishing for now. - return TaskResult.False; - } + // No support for ClickOnce publishing for now. + return TaskResult.False; + } - public Task PublishAsync(CancellationToken cancellationToken, TextWriter outputPaneWriter) - { - throw new InvalidOperationException(); - } + public Task PublishAsync(CancellationToken cancellationToken, TextWriter outputPaneWriter) + { + throw new InvalidOperationException(); + } - public Task ShowPublishPromptAsync() - { - throw new InvalidOperationException(); - } + public Task ShowPublishPromptAsync() + { + throw new InvalidOperationException(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProvider.cs index 416f546b23..08f2c288bb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProvider.cs @@ -2,65 +2,64 @@ using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build +namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build; + +/// +/// Global properties provider for implicitly triggered builds from commands such as Run/Debug Tests, Start Debugging, etc. +/// that should skip running analyzers in order to reduce build times. +/// This provider does not affect the property collection for design time builds. +/// +/// +/// Currently, the provider is only for CPS based SDK-style projects, not for legacy csproj projects. +/// https://github.com/dotnet/project-system/issues/7346 tracks implementing the project system support for legacy csproj projects. +/// +[ExportBuildGlobalPropertiesProvider] +[AppliesTo(ProjectCapability.DotNet)] +internal sealed partial class SkipAnalyzersGlobalPropertiesProvider : StaticGlobalPropertiesProviderBase { - /// - /// Global properties provider for implicitly triggered builds from commands such as Run/Debug Tests, Start Debugging, etc. - /// that should skip running analyzers in order to reduce build times. - /// This provider does not affect the property collection for design time builds. - /// - /// - /// Currently, the provider is only for CPS based SDK-style projects, not for legacy csproj projects. - /// https://github.com/dotnet/project-system/issues/7346 tracks implementing the project system support for legacy csproj projects. - /// - [ExportBuildGlobalPropertiesProvider] - [AppliesTo(ProjectCapability.DotNet)] - internal sealed partial class SkipAnalyzersGlobalPropertiesProvider : StaticGlobalPropertiesProviderBase - { - private const string IsImplicitlyTriggeredBuildPropertyName = "IsImplicitlyTriggeredBuild"; + private const string IsImplicitlyTriggeredBuildPropertyName = "IsImplicitlyTriggeredBuild"; - private const string FastUpToDateCheckIgnoresKindsGlobalPropertyName = "FastUpToDateCheckIgnoresKinds"; - private const string FastUpToDateCheckIgnoresKindsGlobalPropertyValue = "ImplicitBuild"; + private const string FastUpToDateCheckIgnoresKindsGlobalPropertyName = "FastUpToDateCheckIgnoresKinds"; + private const string FastUpToDateCheckIgnoresKindsGlobalPropertyValue = "ImplicitBuild"; - private readonly ImmutableDictionary _regularBuildProperties; - private readonly ImmutableDictionary _implicitlyTriggeredBuildProperties; + private readonly ImmutableDictionary _regularBuildProperties; + private readonly ImmutableDictionary _implicitlyTriggeredBuildProperties; - private readonly IImplicitlyTriggeredBuildState _implicitlyTriggeredBuildState; - private readonly IProjectSystemOptions _projectSystemOptions; + private readonly IImplicitlyTriggeredBuildState _implicitlyTriggeredBuildState; + private readonly IProjectSystemOptions _projectSystemOptions; - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - public SkipAnalyzersGlobalPropertiesProvider(UnconfiguredProject unconfiguredProject, - IImplicitlyTriggeredBuildState implicitlyTriggeredBuildState, - IProjectSystemOptions projectSystemOptions) - : base(unconfiguredProject.Services) - { - _implicitlyTriggeredBuildState = implicitlyTriggeredBuildState; - _projectSystemOptions = projectSystemOptions; + /// + /// Initializes a new instance of the class. + /// + [ImportingConstructor] + public SkipAnalyzersGlobalPropertiesProvider(UnconfiguredProject unconfiguredProject, + IImplicitlyTriggeredBuildState implicitlyTriggeredBuildState, + IProjectSystemOptions projectSystemOptions) + : base(unconfiguredProject.Services) + { + _implicitlyTriggeredBuildState = implicitlyTriggeredBuildState; + _projectSystemOptions = projectSystemOptions; - _regularBuildProperties = ImmutableStringDictionary.EmptyOrdinalIgnoreCase; + _regularBuildProperties = ImmutableStringDictionary.EmptyOrdinalIgnoreCase; - _implicitlyTriggeredBuildProperties = _regularBuildProperties - .Add(IsImplicitlyTriggeredBuildPropertyName, "true") - .Add(FastUpToDateCheckIgnoresKindsGlobalPropertyName, FastUpToDateCheckIgnoresKindsGlobalPropertyValue); - } + _implicitlyTriggeredBuildProperties = _regularBuildProperties + .Add(IsImplicitlyTriggeredBuildPropertyName, "true") + .Add(FastUpToDateCheckIgnoresKindsGlobalPropertyName, FastUpToDateCheckIgnoresKindsGlobalPropertyValue); + } - /// - /// Gets the set of global properties that should apply to the project(s) in this scope. - /// - /// A new dictionary whose keys are case insensitive. Never null, but may be empty. - public override async Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - bool useImplicitlyTriggeredBuildProperties = _implicitlyTriggeredBuildState.IsImplicitlyTriggeredBuild - && await _projectSystemOptions.GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(cancellationToken); + /// + /// Gets the set of global properties that should apply to the project(s) in this scope. + /// + /// A new dictionary whose keys are case insensitive. Never null, but may be empty. + public override async Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + bool useImplicitlyTriggeredBuildProperties = _implicitlyTriggeredBuildState.IsImplicitlyTriggeredBuild + && await _projectSystemOptions.GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(cancellationToken); - ImmutableDictionary globalProperties = useImplicitlyTriggeredBuildProperties - ? _implicitlyTriggeredBuildProperties - : _regularBuildProperties; + ImmutableDictionary globalProperties = useImplicitlyTriggeredBuildProperties + ? _implicitlyTriggeredBuildProperties + : _regularBuildProperties; - return globalProperties; - } + return globalProperties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProvider.cs index fb4cab6052..b0c70d7eb4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProvider.cs @@ -3,86 +3,85 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.ProjectSystem.Managed.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Build properties provider for cross-targeting startup projects to ensure that +/// they build only for the target framework that will be run by F5/Ctrl+F5. +/// +/// +/// The intent here is to speed up the inner-loop development cycle by only building +/// the target framework that is relevant to the user. Note that this overrides +/// global build properties in , +/// so it needs to have a higher value. +/// +[ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: false)] +[AppliesTo(ProjectCapability.DotNet + " & " + ProjectCapability.SingleTargetBuildForStartupProjects)] +[Order(Order.BeforeDefault)] +internal class StartupProjectSingleTargetGlobalBuildPropertyProvider : StaticGlobalPropertiesProviderBase { + private readonly Task> _empty = Task.FromResult>(Empty.PropertiesMap); + private ImmutableDictionary> _propertiesByTargetFramework = ImmutableStringDictionary>.EmptyOrdinalIgnoreCase; + + private readonly ConfiguredProject _configuredProject; + private readonly IActiveDebugFrameworkServices _activeDebugFrameworkServices; + private readonly IImplicitlyTriggeredBuildState _implicitlyTriggeredBuildState; + private readonly IProjectSystemOptions _projectSystemOptions; + /// - /// Build properties provider for cross-targeting startup projects to ensure that - /// they build only for the target framework that will be run by F5/Ctrl+F5. + /// Initializes a new instance of the class. /// - /// - /// The intent here is to speed up the inner-loop development cycle by only building - /// the target framework that is relevant to the user. Note that this overrides - /// global build properties in , - /// so it needs to have a higher value. - /// - [ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: false)] - [AppliesTo(ProjectCapability.DotNet + " & " + ProjectCapability.SingleTargetBuildForStartupProjects)] - [Order(Order.BeforeDefault)] - internal class StartupProjectSingleTargetGlobalBuildPropertyProvider : StaticGlobalPropertiesProviderBase + [ImportingConstructor] + internal StartupProjectSingleTargetGlobalBuildPropertyProvider( + IProjectService projectService, + ConfiguredProject configuredProject, + IActiveDebugFrameworkServices activeDebugFrameworkServices, + IImplicitlyTriggeredBuildState implicitlyTriggeredBuildState, + IProjectSystemOptions projectSystemOptions) + : base(projectService.Services) { - private readonly Task> _empty = Task.FromResult>(Empty.PropertiesMap); - private ImmutableDictionary> _propertiesByTargetFramework = ImmutableStringDictionary>.EmptyOrdinalIgnoreCase; - - private readonly ConfiguredProject _configuredProject; - private readonly IActiveDebugFrameworkServices _activeDebugFrameworkServices; - private readonly IImplicitlyTriggeredBuildState _implicitlyTriggeredBuildState; - private readonly IProjectSystemOptions _projectSystemOptions; + _configuredProject = configuredProject; + _activeDebugFrameworkServices = activeDebugFrameworkServices; + _implicitlyTriggeredBuildState = implicitlyTriggeredBuildState; + _projectSystemOptions = projectSystemOptions; + } - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - internal StartupProjectSingleTargetGlobalBuildPropertyProvider( - IProjectService projectService, - ConfiguredProject configuredProject, - IActiveDebugFrameworkServices activeDebugFrameworkServices, - IImplicitlyTriggeredBuildState implicitlyTriggeredBuildState, - IProjectSystemOptions projectSystemOptions) - : base(projectService.Services) + public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + // Check: + // - if this is an implicitly-triggered build + // - if there is a single startup project + // - if this is the startup project in question + // - if this is a cross targeting project, i.e. project configuration has a "TargetFramework" dimension + // - if the option to prefer single-target builds is turned on + if (_implicitlyTriggeredBuildState.IsImplicitlyTriggeredBuild + && _implicitlyTriggeredBuildState.StartupProjectFullPaths.Length == 1 + && StringComparers.Paths.Equals(_implicitlyTriggeredBuildState.StartupProjectFullPaths[0], _configuredProject.UnconfiguredProject.FullPath) + && _configuredProject.ProjectConfiguration.IsCrossTargeting()) { - _configuredProject = configuredProject; - _activeDebugFrameworkServices = activeDebugFrameworkServices; - _implicitlyTriggeredBuildState = implicitlyTriggeredBuildState; - _projectSystemOptions = projectSystemOptions; + // Defer async state machine creation until needed. + return DoAsync(); } - public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - // Check: - // - if this is an implicitly-triggered build - // - if there is a single startup project - // - if this is the startup project in question - // - if this is a cross targeting project, i.e. project configuration has a "TargetFramework" dimension - // - if the option to prefer single-target builds is turned on - if (_implicitlyTriggeredBuildState.IsImplicitlyTriggeredBuild - && _implicitlyTriggeredBuildState.StartupProjectFullPaths.Length == 1 - && StringComparers.Paths.Equals(_implicitlyTriggeredBuildState.StartupProjectFullPaths[0], _configuredProject.UnconfiguredProject.FullPath) - && _configuredProject.ProjectConfiguration.IsCrossTargeting()) - { - // Defer async state machine creation until needed. - return DoAsync(); - } - - return _empty; + return _empty; - async Task> DoAsync() + async Task> DoAsync() + { + if (await _projectSystemOptions.GetPreferSingleTargetBuildsForStartupProjectsAsync(cancellationToken)) { - if (await _projectSystemOptions.GetPreferSingleTargetBuildsForStartupProjectsAsync(cancellationToken)) + // We only want to build this for the framework that we will launch. + string? activeDebuggingFramework = await _activeDebugFrameworkServices.GetActiveDebuggingFrameworkPropertyAsync(); + + if (activeDebuggingFramework is not null) { - // We only want to build this for the framework that we will launch. - string? activeDebuggingFramework = await _activeDebugFrameworkServices.GetActiveDebuggingFrameworkPropertyAsync(); - - if (activeDebuggingFramework is not null) - { - return ImmutableInterlocked.GetOrAdd( - ref _propertiesByTargetFramework, - activeDebuggingFramework, - tf => Empty.PropertiesMap.Add(ConfigurationGeneral.TargetFrameworkProperty, tf)); - } + return ImmutableInterlocked.GetOrAdd( + ref _propertiesByTargetFramework, + activeDebuggingFramework, + tf => Empty.PropertiesMap.Add(ConfigurationGeneral.TargetFrameworkProperty, tf)); } - - return Empty.PropertiesMap; } + + return Empty.PropertiesMap; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProvider.cs index 4179f98187..e5f694457b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProvider.cs @@ -1,53 +1,52 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Build properties provider for cross targeting projects to ensure that they are build for all target frameworks when doing an explicit build, not just the active target framework. +/// +/// Passes to as that will guarantee it has a higher priority than providers from CPS. +[ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: false)] +[AppliesTo(ProjectCapability.DotNet)] +[Order(Order.Default)] +internal class TargetFrameworkGlobalBuildPropertyProvider : StaticGlobalPropertiesProviderBase { + private readonly Task> _emptyTargetFrameworkResult; + private readonly Task> _crossTargetingProperties; + + private readonly ConfiguredProject _configuredProject; + /// - /// Build properties provider for cross targeting projects to ensure that they are build for all target frameworks when doing an explicit build, not just the active target framework. + /// Initializes a new instance of the class. /// - /// Passes to as that will guarantee it has a higher priority than providers from CPS. - [ExportBuildGlobalPropertiesProvider(designTimeBuildProperties: false)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - internal class TargetFrameworkGlobalBuildPropertyProvider : StaticGlobalPropertiesProviderBase + [ImportingConstructor] + internal TargetFrameworkGlobalBuildPropertyProvider(IProjectService projectService, ConfiguredProject configuredProject) + : base(projectService.Services) { - private readonly Task> _emptyTargetFrameworkResult; - private readonly Task> _crossTargetingProperties; + _configuredProject = configuredProject; - private readonly ConfiguredProject _configuredProject; + _emptyTargetFrameworkResult = Task.FromResult>(Empty.PropertiesMap); - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - internal TargetFrameworkGlobalBuildPropertyProvider(IProjectService projectService, ConfiguredProject configuredProject) - : base(projectService.Services) - { - _configuredProject = configuredProject; - - _emptyTargetFrameworkResult = Task.FromResult>(Empty.PropertiesMap); + // For a cross targeting project, we want to build for all the targeted frameworks. + // Clear out the TargetFramework property from the configuration. + _crossTargetingProperties = Task.FromResult>( + Empty.PropertiesMap.Add(ConfigurationGeneral.TargetFrameworkProperty, string.Empty)); + } + /// + /// Gets the set of global properties that should apply to the project(s) in this scope. + /// + /// A map whose keys are case insensitive. Never null, but may be empty. + public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + // Check if this is a cross targeting project, i.e. project configuration has a "TargetFramework" dimension. + if (_configuredProject.ProjectConfiguration.IsCrossTargeting()) + { // For a cross targeting project, we want to build for all the targeted frameworks. // Clear out the TargetFramework property from the configuration. - _crossTargetingProperties = Task.FromResult>( - Empty.PropertiesMap.Add(ConfigurationGeneral.TargetFrameworkProperty, string.Empty)); + return _crossTargetingProperties; } - /// - /// Gets the set of global properties that should apply to the project(s) in this scope. - /// - /// A map whose keys are case insensitive. Never null, but may be empty. - public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - // Check if this is a cross targeting project, i.e. project configuration has a "TargetFramework" dimension. - if (_configuredProject.ProjectConfiguration.IsCrossTargeting()) - { - // For a cross targeting project, we want to build for all the targeted frameworks. - // Clear out the TargetFramework property from the configuration. - return _crossTargetingProperties; - } - - return _emptyTargetFrameworkResult; - } + return _emptyTargetFrameworkResult; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider.cs index 535bbf6c32..61d478767b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Build/TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +/// +/// Turns off UseHostCompilerIfAvailable to prevent CoreCompile from always being called during builds, see: https://github.com/dotnet/sdk/issues/708. +/// +[ExportBuildGlobalPropertiesProvider] +[AppliesTo(ProjectCapability.DotNetLanguageService)] +internal class TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider : StaticGlobalPropertiesProviderBase { - /// - /// Turns off UseHostCompilerIfAvailable to prevent CoreCompile from always being called during builds, see: https://github.com/dotnet/sdk/issues/708. - /// - [ExportBuildGlobalPropertiesProvider] - [AppliesTo(ProjectCapability.DotNetLanguageService)] - internal class TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider : StaticGlobalPropertiesProviderBase - { - private static readonly Task> s_buildProperties = Task.FromResult>( - Empty.PropertiesMap.Add(BuildProperty.UseHostCompilerIfAvailable, "false")); + private static readonly Task> s_buildProperties = Task.FromResult>( + Empty.PropertiesMap.Add(BuildProperty.UseHostCompilerIfAvailable, "false")); - [ImportingConstructor] - public TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider(IProjectService projectService) - : base(projectService.Services) - { - } + [ImportingConstructor] + public TurnOffUseHostCompilerIfAvailableBuildPropertiesProvider(IProjectService projectService) + : base(projectService.Services) + { + } - public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) - { - return s_buildProperties; - } + public override Task> GetGlobalPropertiesAsync(CancellationToken cancellationToken) + { + return s_buildProperties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/BaseProjectConfigurationDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/BaseProjectConfigurationDimensionProvider.cs index fef85212f9..7c7c606c38 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/BaseProjectConfigurationDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/BaseProjectConfigurationDimensionProvider.cs @@ -5,264 +5,263 @@ using Microsoft.VisualStudio.Build; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Base project configuration dimension provider +/// +internal abstract class BaseProjectConfigurationDimensionProvider : IProjectConfigurationDimensionsProviderInternal { /// - /// Base project configuration dimension provider + /// Initializes a new instance of the class. /// - internal abstract class BaseProjectConfigurationDimensionProvider : IProjectConfigurationDimensionsProviderInternal + /// Lock service for the project file. + /// Name of the dimension. + /// Name of the project property containing the dimension values. + /// The default value of the dimension, for example "AnyCPU". + protected BaseProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor, string dimensionName, string propertyName, string? dimensionDefaultValue = null) { - /// - /// Initializes a new instance of the class. - /// - /// Lock service for the project file. - /// Name of the dimension. - /// Name of the project property containing the dimension values. - /// The default value of the dimension, for example "AnyCPU". - protected BaseProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor, string dimensionName, string propertyName, string? dimensionDefaultValue = null) - { - Requires.NotNull(projectAccessor); + Requires.NotNull(projectAccessor); - ProjectAccessor = projectAccessor; - DimensionName = dimensionName; - PropertyName = propertyName; - DimensionDefaultValue = dimensionDefaultValue; - } + ProjectAccessor = projectAccessor; + DimensionName = dimensionName; + PropertyName = propertyName; + DimensionDefaultValue = dimensionDefaultValue; + } - public string DimensionName { get; } - public string PropertyName { get; } - public string? DimensionDefaultValue { get; } - public IProjectAccessor ProjectAccessor { get; } - - /// - /// Gets the property values for the dimension. - /// - /// Unconfigured project. - /// Collection of values for the dimension. - /// - /// From . - /// - private async Task> GetOrderedPropertyValuesAsync(UnconfiguredProject project) - { - Requires.NotNull(project); + public string DimensionName { get; } + public string PropertyName { get; } + public string? DimensionDefaultValue { get; } + public IProjectAccessor ProjectAccessor { get; } - ConfiguredProject? configuredProject = await project.GetSuggestedConfiguredProjectAsync(); + /// + /// Gets the property values for the dimension. + /// + /// Unconfigured project. + /// Collection of values for the dimension. + /// + /// From . + /// + private async Task> GetOrderedPropertyValuesAsync(UnconfiguredProject project) + { + Requires.NotNull(project); - Assumes.NotNull(configuredProject); + ConfiguredProject? configuredProject = await project.GetSuggestedConfiguredProjectAsync(); - // Need evaluated property to get inherited properties defined in props or targets. - return await ProjectAccessor.OpenProjectForReadAsync(configuredProject, GetOrderedPropertyValues); - } + Assumes.NotNull(configuredProject); - /// - /// Gets the property values for the dimension. - /// - /// . - /// Collection of values for the dimension. - private ImmutableArray GetOrderedPropertyValues(Project project) - { - Requires.NotNull(project); + // Need evaluated property to get inherited properties defined in props or targets. + return await ProjectAccessor.OpenProjectForReadAsync(configuredProject, GetOrderedPropertyValues); + } - string? propertyValue = project.GetProperty(PropertyName)?.EvaluatedValue; + /// + /// Gets the property values for the dimension. + /// + /// . + /// Collection of values for the dimension. + private ImmutableArray GetOrderedPropertyValues(Project project) + { + Requires.NotNull(project); - if (Strings.IsNullOrEmpty(propertyValue)) - { - return ImmutableArray.Empty; - } - else - { - return BuildUtilities.GetPropertyValues(propertyValue).ToImmutableArray(); - } - } + string? propertyValue = project.GetProperty(PropertyName)?.EvaluatedValue; - /// - /// Gets the defaults values for project configuration dimensions for the given unconfigured project. - /// - /// Unconfigured project. - /// Collection of key/value pairs for the defaults values for the configuration dimensions of this provider for given project. - /// - /// From . - /// The interface expects a collection of key/value pairs containing one or more dimensions along with a single values for each - /// dimension. In this implementation each provider is representing a single dimension. - /// - public virtual async Task>> GetDefaultValuesForDimensionsAsync(UnconfiguredProject project) + if (Strings.IsNullOrEmpty(propertyValue)) { - Requires.NotNull(project); - - ImmutableArray values = await GetOrderedPropertyValuesAsync(project); - if (values.IsEmpty) - { - return Enumerable.Empty>(); - } - else - { - // First value is the default one. - return new[] { new KeyValuePair(DimensionName, values[0]) }; - } + return ImmutableArray.Empty; } - - /// - /// Gets the project configuration dimension and values represented by this provider for the given unconfigured project. - /// - /// Unconfigured project. - /// Collection of key/value pairs for the current values for the configuration dimensions of this provider for given project. - /// - /// From . - /// The interface expects a collection of key/value pairs containing one or more dimensions along with the values for each - /// dimension. In this implementation each provider is representing a single dimension with one or more values. - /// - public virtual async Task>>> GetProjectConfigurationDimensionsAsync(UnconfiguredProject project) + else { - Requires.NotNull(project); - - ImmutableArray values = await GetOrderedPropertyValuesAsync(project); - if (values.IsEmpty) - { - return Enumerable.Empty>>(); - } - else - { - return new[] { new KeyValuePair>(DimensionName, values) }; - } + return BuildUtilities.GetPropertyValues(propertyValue).ToImmutableArray(); } + } - public IEnumerable GetBestGuessDimensionNames(ImmutableArray properties) - { - if (FindDimensionProperty(properties) is not null) - return new string[] { DimensionName }; + /// + /// Gets the defaults values for project configuration dimensions for the given unconfigured project. + /// + /// Unconfigured project. + /// Collection of key/value pairs for the defaults values for the configuration dimensions of this provider for given project. + /// + /// From . + /// The interface expects a collection of key/value pairs containing one or more dimensions along with a single values for each + /// dimension. In this implementation each provider is representing a single dimension. + /// + public virtual async Task>> GetDefaultValuesForDimensionsAsync(UnconfiguredProject project) + { + Requires.NotNull(project); - return Array.Empty(); + ImmutableArray values = await GetOrderedPropertyValuesAsync(project); + if (values.IsEmpty) + { + return Enumerable.Empty>(); } - - public async Task>> GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProject project) + else { - string? defaultValue = await FindDefaultValueFromDimensionPropertyAsync(project) ?? DimensionDefaultValue; - if (defaultValue is not null) - return new[] { new KeyValuePair(DimensionName, defaultValue) }; + // First value is the default one. + return new[] { new KeyValuePair(DimensionName, values[0]) }; + } + } - return Enumerable.Empty>(); + /// + /// Gets the project configuration dimension and values represented by this provider for the given unconfigured project. + /// + /// Unconfigured project. + /// Collection of key/value pairs for the current values for the configuration dimensions of this provider for given project. + /// + /// From . + /// The interface expects a collection of key/value pairs containing one or more dimensions along with the values for each + /// dimension. In this implementation each provider is representing a single dimension with one or more values. + /// + public virtual async Task>>> GetProjectConfigurationDimensionsAsync(UnconfiguredProject project) + { + Requires.NotNull(project); + + ImmutableArray values = await GetOrderedPropertyValuesAsync(project); + if (values.IsEmpty) + { + return Enumerable.Empty>>(); } + else + { + return new[] { new KeyValuePair>(DimensionName, values) }; + } + } + + public IEnumerable GetBestGuessDimensionNames(ImmutableArray properties) + { + if (FindDimensionProperty(properties) is not null) + return new string[] { DimensionName }; + + return Array.Empty(); + } + + public async Task>> GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProject project) + { + string? defaultValue = await FindDefaultValueFromDimensionPropertyAsync(project) ?? DimensionDefaultValue; + if (defaultValue is not null) + return new[] { new KeyValuePair(DimensionName, defaultValue) }; - /// - /// Modifies the project when there's a configuration change. - /// - /// Information about the configuration dimension value change. - /// A task for the async operation. - public Task OnDimensionValueChangedAsync(ProjectConfigurationDimensionValueChangedEventArgs args) + return Enumerable.Empty>(); + } + + /// + /// Modifies the project when there's a configuration change. + /// + /// Information about the configuration dimension value change. + /// A task for the async operation. + public Task OnDimensionValueChangedAsync(ProjectConfigurationDimensionValueChangedEventArgs args) + { + if (StringComparers.ConfigurationDimensionNames.Equals(args.DimensionName, DimensionName)) { - if (StringComparers.ConfigurationDimensionNames.Equals(args.DimensionName, DimensionName)) + if (args.Stage == ChangeEventStage.Before) { - if (args.Stage == ChangeEventStage.Before) - { - switch (args.Change) - { - case ConfigurationDimensionChange.Add: - return UpdateUnderLockAsync(args.Project, (msbuildProject, evaluatedPropertyValue) => - BuildUtilities.AppendPropertyValue(msbuildProject, evaluatedPropertyValue, PropertyName, args.DimensionValue)); - - case ConfigurationDimensionChange.Delete: - return UpdateUnderLockAsync(args.Project, (msbuildProject, evaluatedPropertyValue) => - BuildUtilities.RemovePropertyValue(msbuildProject, evaluatedPropertyValue, PropertyName, args.DimensionValue)); - - case ConfigurationDimensionChange.Rename: - // Need to wait until the core rename changes happen before renaming the property. - break; - } - } - else if (args.Stage == ChangeEventStage.After) + switch (args.Change) { - // Only change that needs to be handled here is renaming configurations which needs to happen after all - // of the core changes to rename existing conditions have executed. - if (args.Change == ConfigurationDimensionChange.Rename) - { + case ConfigurationDimensionChange.Add: return UpdateUnderLockAsync(args.Project, (msbuildProject, evaluatedPropertyValue) => - BuildUtilities.RenamePropertyValue(msbuildProject, evaluatedPropertyValue, PropertyName, args.OldDimensionValue, args.DimensionValue)); - } - } - } + BuildUtilities.AppendPropertyValue(msbuildProject, evaluatedPropertyValue, PropertyName, args.DimensionValue)); - return Task.CompletedTask; + case ConfigurationDimensionChange.Delete: + return UpdateUnderLockAsync(args.Project, (msbuildProject, evaluatedPropertyValue) => + BuildUtilities.RemovePropertyValue(msbuildProject, evaluatedPropertyValue, PropertyName, args.DimensionValue)); - async Task UpdateUnderLockAsync(UnconfiguredProject project, Action action) + case ConfigurationDimensionChange.Rename: + // Need to wait until the core rename changes happen before renaming the property. + break; + } + } + else if (args.Stage == ChangeEventStage.After) { - ConfiguredProject? configuredProject = await project.GetSuggestedConfiguredProjectAsync(); - - Assumes.NotNull(configuredProject); - - await ProjectAccessor.OpenProjectForUpgradeableReadAsync(configuredProject, evaluatedProject => + // Only change that needs to be handled here is renaming configurations which needs to happen after all + // of the core changes to rename existing conditions have executed. + if (args.Change == ConfigurationDimensionChange.Rename) { - string evaluatedPropertyValue = evaluatedProject.GetPropertyValue(PropertyName); - - return ProjectAccessor.OpenProjectXmlForWriteAsync(project, msbuildProject => - { - action(msbuildProject, evaluatedPropertyValue); - }); - }); + return UpdateUnderLockAsync(args.Project, (msbuildProject, evaluatedPropertyValue) => + BuildUtilities.RenamePropertyValue(msbuildProject, evaluatedPropertyValue, PropertyName, args.OldDimensionValue, args.DimensionValue)); + } } } - public virtual Task>>> GetProjectConfigurationDimensionsAsync(Project project) + return Task.CompletedTask; + + async Task UpdateUnderLockAsync(UnconfiguredProject project, Action action) { - Requires.NotNull(project); + ConfiguredProject? configuredProject = await project.GetSuggestedConfiguredProjectAsync(); - ImmutableArray values = GetOrderedPropertyValues(project); - if (values.IsEmpty) - { - return TaskResult.EmptyEnumerable>>(); - } - else + Assumes.NotNull(configuredProject); + + await ProjectAccessor.OpenProjectForUpgradeableReadAsync(configuredProject, evaluatedProject => { - IEnumerable>> dimensionValues = new[] { new KeyValuePair>(DimensionName, values) }; - return Task.FromResult(dimensionValues); - } + string evaluatedPropertyValue = evaluatedProject.GetPropertyValue(PropertyName); + + return ProjectAccessor.OpenProjectXmlForWriteAsync(project, msbuildProject => + { + action(msbuildProject, evaluatedPropertyValue); + }); + }); } + } - public Task>> GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProject project, string solutionConfiguration) + public virtual Task>>> GetProjectConfigurationDimensionsAsync(Project project) + { + Requires.NotNull(project); + + ImmutableArray values = GetOrderedPropertyValues(project); + if (values.IsEmpty) { - return GetBestGuessDefaultValuesForDimensionsAsync(project); + return TaskResult.EmptyEnumerable>>(); } - - private async Task FindDefaultValueFromDimensionPropertyAsync(UnconfiguredProject project) + else { - string? values = await FindDimensionPropertyAsync(project); - if (Strings.IsNullOrEmpty(values)) - return null; + IEnumerable>> dimensionValues = new[] { new KeyValuePair>(DimensionName, values) }; + return Task.FromResult(dimensionValues); + } + } - foreach (string defaultValue in BuildUtilities.GetPropertyValues(values)) - { - // If this property is derived from another property, skip it and just - // pull default from next known values. This is better than picking a - // default that is not actually one of the known configs. - if (defaultValue.IndexOf("$(", StringComparisons.PropertyValues) == -1) - return defaultValue; - } + public Task>> GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProject project, string solutionConfiguration) + { + return GetBestGuessDefaultValuesForDimensionsAsync(project); + } + private async Task FindDefaultValueFromDimensionPropertyAsync(UnconfiguredProject project) + { + string? values = await FindDimensionPropertyAsync(project); + if (Strings.IsNullOrEmpty(values)) return null; - } - private Task FindDimensionPropertyAsync(UnconfiguredProject project) + foreach (string defaultValue in BuildUtilities.GetPropertyValues(values)) { - return ProjectAccessor.OpenProjectXmlForReadAsync( - project, - projectXml => FindDimensionProperty(projectXml)?.GetUnescapedValue()); + // If this property is derived from another property, skip it and just + // pull default from next known values. This is better than picking a + // default that is not actually one of the known configs. + if (defaultValue.IndexOf("$(", StringComparisons.PropertyValues) == -1) + return defaultValue; } - private ProjectPropertyElement? FindDimensionProperty(ProjectRootElement projectXml) - { - IEnumerable properties = projectXml.PropertyGroups - .SelectMany(group => group.Properties); + return null; + } - return FindDimensionProperty(properties); - } + private Task FindDimensionPropertyAsync(UnconfiguredProject project) + { + return ProjectAccessor.OpenProjectXmlForReadAsync( + project, + projectXml => FindDimensionProperty(projectXml)?.GetUnescapedValue()); + } - private ProjectPropertyElement? FindDimensionProperty(IEnumerable properties) - { - // NOTE: We try to somewhat mimic evaluation, but it doesn't have to be exact; its just a guess - // at what "might" be the default configuration, not what it actually is. - return properties.Reverse() - .FirstOrDefault( - p => StringComparers.PropertyNames.Equals(PropertyName, p.Name) && - BuildUtilities.HasWellKnownConditionsThatAlwaysEvaluateToTrue(p)); - } + private ProjectPropertyElement? FindDimensionProperty(ProjectRootElement projectXml) + { + IEnumerable properties = projectXml.PropertyGroups + .SelectMany(group => group.Properties); + + return FindDimensionProperty(properties); + } + + private ProjectPropertyElement? FindDimensionProperty(IEnumerable properties) + { + // NOTE: We try to somewhat mimic evaluation, but it doesn't have to be exact; its just a guess + // at what "might" be the default configuration, not what it actually is. + return properties.Reverse() + .FirstOrDefault( + p => StringComparers.PropertyNames.Equals(PropertyName, p.Name) && + BuildUtilities.HasWellKnownConditionsThatAlwaysEvaluateToTrue(p)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProvider.cs index 23ac7411f2..5337020c81 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProvider.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Provides 'Configuration' project configuration dimension and values. +/// +[Export(typeof(IProjectConfigurationDimensionsProvider))] +[AppliesTo(ProjectCapabilities.ProjectConfigurationsDeclaredDimensions)] +[Order(DimensionProviderOrder.Configuration)] +[ConfigurationDimensionDescription(ConfigurationGeneral.ConfigurationProperty)] +internal class ConfigurationProjectConfigurationDimensionProvider : BaseProjectConfigurationDimensionProvider { - /// - /// Provides 'Configuration' project configuration dimension and values. - /// - [Export(typeof(IProjectConfigurationDimensionsProvider))] - [AppliesTo(ProjectCapabilities.ProjectConfigurationsDeclaredDimensions)] - [Order(DimensionProviderOrder.Configuration)] - [ConfigurationDimensionDescription(ConfigurationGeneral.ConfigurationProperty)] - internal class ConfigurationProjectConfigurationDimensionProvider : BaseProjectConfigurationDimensionProvider + [ImportingConstructor] + public ConfigurationProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor) + : base( + projectAccessor, + dimensionName: ConfigurationGeneral.ConfigurationProperty, + propertyName: "Configurations", + dimensionDefaultValue: "Debug") { - [ImportingConstructor] - public ConfigurationProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor) - : base( - projectAccessor, - dimensionName: ConfigurationGeneral.ConfigurationProperty, - propertyName: "Configurations", - dimensionDefaultValue: "Debug") - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/DimensionProviderOrder.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/DimensionProviderOrder.cs index 3c2b9efc80..698dd69192 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/DimensionProviderOrder.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/DimensionProviderOrder.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Contains constants representing the order precedence for implementations. +/// +internal static class DimensionProviderOrder { - /// - /// Contains constants representing the order precedence for implementations. - /// - internal static class DimensionProviderOrder - { - // These values determine the order of dimensions inside the configuration service. - // We want Configuration|Platform|TargetFramework. + // These values determine the order of dimensions inside the configuration service. + // We want Configuration|Platform|TargetFramework. - public const int Configuration = Order.BeforeBeforeDefault; - public const int Platform = Order.BeforeDefault; - public const int TargetFramework = Order.Default; - } + public const int Configuration = Order.BeforeBeforeDefault; + public const int Platform = Order.BeforeDefault; + public const int TargetFramework = Order.Default; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IActiveConfiguredProjectsDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IActiveConfiguredProjectsDimensionProvider.cs index 37a1086f02..5aa40b244b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IActiveConfiguredProjectsDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IActiveConfiguredProjectsDimensionProvider.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Indicates that a dimension provided by a instance +/// should participate in calculating the active project configurations for . +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface IActiveConfiguredProjectsDimensionProvider { /// - /// Indicates that a dimension provided by a instance - /// should participate in calculating the active project configurations for . + /// Gets the name of the dimension that should participate in calculating the active project configurations. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface IActiveConfiguredProjectsDimensionProvider - { - /// - /// Gets the name of the dimension that should participate in calculating the active project configurations. - /// - string DimensionName { get; } - } + string DimensionName { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IConfigurationDimensionDescriptionMetadataView.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IConfigurationDimensionDescriptionMetadataView.cs index 2142007352..576af4b8a2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IConfigurationDimensionDescriptionMetadataView.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IConfigurationDimensionDescriptionMetadataView.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Provides a customized view to extract configuration dimension description data from . +/// +public interface IConfigurationDimensionDescriptionMetadataView : IOrderPrecedenceMetadataView { - /// - /// Provides a customized view to extract configuration dimension description data from . - /// - public interface IConfigurationDimensionDescriptionMetadataView : IOrderPrecedenceMetadataView - { #pragma warning disable CA1819 // Properties should not return arrays - /// - /// Dimension names. - /// This must match . - /// - string[] DimensionName { get; } + /// + /// Dimension names. + /// This must match . + /// + string[] DimensionName { get; } - /// - /// Whether it is a dimension to calculate configuration groups. - /// This must match . - /// - bool[] IsVariantDimension { get; } + /// + /// Whether it is a dimension to calculate configuration groups. + /// This must match . + /// + bool[] IsVariantDimension { get; } #pragma warning restore CA1819 // Properties should not return arrays - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IImplicitlyActiveDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IImplicitlyActiveDimensionProvider.cs index ca25a80c79..8af3504854 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IImplicitlyActiveDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IImplicitlyActiveDimensionProvider.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Provides the implicitly active dimensions from a list of dimension names. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IImplicitlyActiveDimensionProvider { /// - /// Provides the implicitly active dimensions from a list of dimension names. + /// Returns the implicitly active dimension names from the specified dimension names. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IImplicitlyActiveDimensionProvider - { - /// - /// Returns the implicitly active dimension names from the specified dimension names. - /// - /// - /// is . - /// - /// - /// NOTE: The returned order matches the order in which the dimension names and values - /// should be displayed to the user. - /// - IEnumerable GetImplicitlyActiveDimensions(IEnumerable dimensionNames); - } + /// + /// is . + /// + /// + /// NOTE: The returned order matches the order in which the dimension names and values + /// should be displayed to the user. + /// + IEnumerable GetImplicitlyActiveDimensions(IEnumerable dimensionNames); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IProjectConfigurationDimensionsProviderInternal.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IProjectConfigurationDimensionsProviderInternal.cs index eee6773da4..5ce60ca2ce 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IProjectConfigurationDimensionsProviderInternal.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/IProjectConfigurationDimensionsProviderInternal.cs @@ -2,10 +2,9 @@ using Microsoft.Build.Construction; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal interface IProjectConfigurationDimensionsProviderInternal : IProjectConfigurationDimensionsProvider5 { - internal interface IProjectConfigurationDimensionsProviderInternal : IProjectConfigurationDimensionsProvider5 - { - IEnumerable GetBestGuessDimensionNames(ImmutableArray properties); - } + IEnumerable GetBestGuessDimensionNames(ImmutableArray properties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ImplicitlyActiveDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ImplicitlyActiveDimensionProvider.cs index 14c15974f1..51a69819d8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ImplicitlyActiveDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/ImplicitlyActiveDimensionProvider.cs @@ -2,56 +2,55 @@ using Microsoft.VisualStudio.Buffers.PooledObjects; -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Provides an implementation of that bases +/// itself on instances. +/// +[Export(typeof(IImplicitlyActiveDimensionProvider))] +internal class ImplicitlyActiveDimensionProvider : IImplicitlyActiveDimensionProvider { - /// - /// Provides an implementation of that bases - /// itself on instances. - /// - [Export(typeof(IImplicitlyActiveDimensionProvider))] - internal class ImplicitlyActiveDimensionProvider : IImplicitlyActiveDimensionProvider + private readonly Lazy> _builtInImplicitlyActiveDimensions; + + [ImportingConstructor] + public ImplicitlyActiveDimensionProvider(UnconfiguredProject project) { - private readonly Lazy> _builtInImplicitlyActiveDimensions; + _builtInImplicitlyActiveDimensions = new Lazy>(CalculateBuiltInImplicitlyActiveDimensions); - [ImportingConstructor] - public ImplicitlyActiveDimensionProvider(UnconfiguredProject project) - { - _builtInImplicitlyActiveDimensions = new Lazy>(CalculateBuiltInImplicitlyActiveDimensions); + DimensionProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - DimensionProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + [ImportMany] + internal OrderPrecedenceImportCollection DimensionProviders { get; } - [ImportMany] - internal OrderPrecedenceImportCollection DimensionProviders { get; } + public IEnumerable GetImplicitlyActiveDimensions(IEnumerable dimensionNames) + { + Requires.NotNull(dimensionNames); - public IEnumerable GetImplicitlyActiveDimensions(IEnumerable dimensionNames) - { - Requires.NotNull(dimensionNames); + ImmutableArray builtInDimensions = _builtInImplicitlyActiveDimensions.Value; - ImmutableArray builtInDimensions = _builtInImplicitlyActiveDimensions.Value; + // NOTE: Order matters; this must be in the order in which the providers are + // prioritized in 'builtInDimensions', of which Enumerable.Intersect guarantees. + return builtInDimensions.Intersect(dimensionNames, StringComparers.ConfigurationDimensionNames); + } - // NOTE: Order matters; this must be in the order in which the providers are - // prioritized in 'builtInDimensions', of which Enumerable.Intersect guarantees. - return builtInDimensions.Intersect(dimensionNames, StringComparers.ConfigurationDimensionNames); - } + private ImmutableArray CalculateBuiltInImplicitlyActiveDimensions() + { + var implicitlyActiveDimensions = PooledArray.GetInstance(); - private ImmutableArray CalculateBuiltInImplicitlyActiveDimensions() + foreach (Lazy provider in DimensionProviders) { - var implicitlyActiveDimensions = PooledArray.GetInstance(); - - foreach (Lazy provider in DimensionProviders) + for (int i = 0; i < provider.Metadata.IsVariantDimension.Length; i++) { - for (int i = 0; i < provider.Metadata.IsVariantDimension.Length; i++) + if (provider.Metadata.IsVariantDimension[i]) { - if (provider.Metadata.IsVariantDimension[i]) - { - if (!implicitlyActiveDimensions.Contains(provider.Metadata.DimensionName[i], StringComparers.ConfigurationDimensionNames)) - implicitlyActiveDimensions.Add(provider.Metadata.DimensionName[i]); - } + if (!implicitlyActiveDimensions.Contains(provider.Metadata.DimensionName[i], StringComparers.ConfigurationDimensionNames)) + implicitlyActiveDimensions.Add(provider.Metadata.DimensionName[i]); } } - - return implicitlyActiveDimensions.ToImmutableAndFree(); } + + return implicitlyActiveDimensions.ToImmutableAndFree(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProvider.cs index bf54150a10..f984dca60b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProvider.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Provides 'Platform' project configuration dimension and values. +/// +[Export(typeof(IProjectConfigurationDimensionsProvider))] +[AppliesTo(ProjectCapabilities.ProjectConfigurationsDeclaredDimensions)] +[Order(DimensionProviderOrder.Platform)] +[ConfigurationDimensionDescription(ConfigurationGeneral.PlatformProperty)] +internal class PlatformProjectConfigurationDimensionProvider : BaseProjectConfigurationDimensionProvider { - /// - /// Provides 'Platform' project configuration dimension and values. - /// - [Export(typeof(IProjectConfigurationDimensionsProvider))] - [AppliesTo(ProjectCapabilities.ProjectConfigurationsDeclaredDimensions)] - [Order(DimensionProviderOrder.Platform)] - [ConfigurationDimensionDescription(ConfigurationGeneral.PlatformProperty)] - internal class PlatformProjectConfigurationDimensionProvider : BaseProjectConfigurationDimensionProvider + [ImportingConstructor] + public PlatformProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor) + : base( + projectAccessor, + dimensionName: ConfigurationGeneral.PlatformProperty, + propertyName: "Platforms", + dimensionDefaultValue: "AnyCPU") { - [ImportingConstructor] - public PlatformProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor) - : base( - projectAccessor, - dimensionName: ConfigurationGeneral.PlatformProperty, - propertyName: "Platforms", - dimensionDefaultValue: "AnyCPU") - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProvider.cs index e7d7639c0a..44ff6cba0f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProvider.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +/// +/// Provides "TargetFramework" project configuration dimension and values. +/// +[Export(typeof(IProjectConfigurationDimensionsProvider))] +[Export(typeof(IActiveConfiguredProjectsDimensionProvider))] +[AppliesTo(ProjectCapabilities.ProjectConfigurationsDeclaredDimensions)] +[Order(DimensionProviderOrder.TargetFramework)] +[ConfigurationDimensionDescription(ConfigurationGeneral.TargetFrameworkProperty, isVariantDimension: true)] +internal class TargetFrameworkProjectConfigurationDimensionProvider : BaseProjectConfigurationDimensionProvider, IActiveConfiguredProjectsDimensionProvider { - /// - /// Provides "TargetFramework" project configuration dimension and values. - /// - [Export(typeof(IProjectConfigurationDimensionsProvider))] - [Export(typeof(IActiveConfiguredProjectsDimensionProvider))] - [AppliesTo(ProjectCapabilities.ProjectConfigurationsDeclaredDimensions)] - [Order(DimensionProviderOrder.TargetFramework)] - [ConfigurationDimensionDescription(ConfigurationGeneral.TargetFrameworkProperty, isVariantDimension: true)] - internal class TargetFrameworkProjectConfigurationDimensionProvider : BaseProjectConfigurationDimensionProvider, IActiveConfiguredProjectsDimensionProvider + [ImportingConstructor] + public TargetFrameworkProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor) + : base( + projectAccessor, + dimensionName: ConfigurationGeneral.TargetFrameworkProperty, + propertyName: ConfigurationGeneral.TargetFrameworksProperty) { - [ImportingConstructor] - public TargetFrameworkProjectConfigurationDimensionProvider(IProjectAccessor projectAccessor) - : base( - projectAccessor, - dimensionName: ConfigurationGeneral.TargetFrameworkProperty, - propertyName: ConfigurationGeneral.TargetFrameworksProperty) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.Instance.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.Instance.cs index f07aaab3c4..ebf692633c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.Instance.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.Instance.cs @@ -2,97 +2,96 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal sealed partial class ConfiguredProjectActivationTracking { - internal sealed partial class ConfiguredProjectActivationTracking + internal sealed class ConfiguredProjectActivationTrackingInstance : OnceInitializedOnceDisposedAsync, IMultiLifetimeInstance { - internal sealed class ConfiguredProjectActivationTrackingInstance : OnceInitializedOnceDisposedAsync, IMultiLifetimeInstance + private readonly ConfiguredProject _project; + private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; + private readonly ITargetBlock> _targetBlock; + private readonly OrderPrecedenceImportCollection _components; + + private IReadOnlyCollection _activeComponents = Array.Empty(); + private IDisposable? _subscription; + + public ConfiguredProjectActivationTrackingInstance( + IProjectThreadingService threadingService, + ConfiguredProject project, + IActiveConfiguredProjectProvider activeConfiguredProjectProvider, + OrderPrecedenceImportCollection components) + : base(threadingService.JoinableTaskContext) + { + _project = project; + _activeConfiguredProjectProvider = activeConfiguredProjectProvider; + _targetBlock = DataflowBlockFactory.CreateActionBlock>(OnChangeAsync, project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality); + _components = components; + } + + public Task InitializeAsync() + { + return InitializeAsync(CancellationToken.None); + } + + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _subscription = ProjectDataSources.SyncLinkTo( + _project.Capabilities.SourceBlock.SyncLinkOptions(), + _activeConfiguredProjectProvider.ActiveConfiguredProjectBlock.SyncLinkOptions(), + linkOptions: DataflowOption.PropagateCompletion, + target: _targetBlock, + cancellationToken: cancellationToken); + + return Task.CompletedTask; + } + + protected override Task DisposeCoreAsync(bool initialized) + { + _subscription?.Dispose(); + _targetBlock.Complete(); + + return DeactivateAsync(_activeComponents); + } + + private async Task OnChangeAsync(IProjectVersionedValue<(IProjectCapabilitiesSnapshot, ConfiguredProject)> e) + { + // We'll get called back in main two situations (notwithstanding version-only updates): + // + // - The active configuration changed, and our configuration might + // be now active or might no longer be active. + // + // - The capabilities changed in our configuration. + // + // In both situations, we may need to activate or deactivate + // IActiveConfigurationComponent instances. + + IProjectCapabilitiesSnapshot snapshot = e.Value.Item1; + bool isActive = e.Value.Item2.ProjectConfiguration.Equals(_project.ProjectConfiguration); + + using var capabilitiesContext = ProjectCapabilitiesContext.CreateIsolatedContext(_project, snapshot); + + // If we're not active, there are no future services to activate + IReadOnlyCollection futureComponents = isActive + ? _components.Select(s => s.Value).ToList() + : Array.Empty(); + + var diff = new SetDiff(_activeComponents, futureComponents); + + await DeactivateAsync(diff.Removed); + await ActivateAsync(diff.Added); + + _activeComponents = futureComponents; + } + + private static Task DeactivateAsync(IEnumerable services) + { + return Task.WhenAll(services.Select(c => c.DeactivateAsync())); + } + + private static Task ActivateAsync(IEnumerable services) { - private readonly ConfiguredProject _project; - private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; - private readonly ITargetBlock> _targetBlock; - private readonly OrderPrecedenceImportCollection _components; - - private IReadOnlyCollection _activeComponents = Array.Empty(); - private IDisposable? _subscription; - - public ConfiguredProjectActivationTrackingInstance( - IProjectThreadingService threadingService, - ConfiguredProject project, - IActiveConfiguredProjectProvider activeConfiguredProjectProvider, - OrderPrecedenceImportCollection components) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _activeConfiguredProjectProvider = activeConfiguredProjectProvider; - _targetBlock = DataflowBlockFactory.CreateActionBlock>(OnChangeAsync, project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality); - _components = components; - } - - public Task InitializeAsync() - { - return InitializeAsync(CancellationToken.None); - } - - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _subscription = ProjectDataSources.SyncLinkTo( - _project.Capabilities.SourceBlock.SyncLinkOptions(), - _activeConfiguredProjectProvider.ActiveConfiguredProjectBlock.SyncLinkOptions(), - linkOptions: DataflowOption.PropagateCompletion, - target: _targetBlock, - cancellationToken: cancellationToken); - - return Task.CompletedTask; - } - - protected override Task DisposeCoreAsync(bool initialized) - { - _subscription?.Dispose(); - _targetBlock.Complete(); - - return DeactivateAsync(_activeComponents); - } - - private async Task OnChangeAsync(IProjectVersionedValue<(IProjectCapabilitiesSnapshot, ConfiguredProject)> e) - { - // We'll get called back in main two situations (notwithstanding version-only updates): - // - // - The active configuration changed, and our configuration might - // be now active or might no longer be active. - // - // - The capabilities changed in our configuration. - // - // In both situations, we may need to activate or deactivate - // IActiveConfigurationComponent instances. - - IProjectCapabilitiesSnapshot snapshot = e.Value.Item1; - bool isActive = e.Value.Item2.ProjectConfiguration.Equals(_project.ProjectConfiguration); - - using var capabilitiesContext = ProjectCapabilitiesContext.CreateIsolatedContext(_project, snapshot); - - // If we're not active, there are no future services to activate - IReadOnlyCollection futureComponents = isActive - ? _components.Select(s => s.Value).ToList() - : Array.Empty(); - - var diff = new SetDiff(_activeComponents, futureComponents); - - await DeactivateAsync(diff.Removed); - await ActivateAsync(diff.Added); - - _activeComponents = futureComponents; - } - - private static Task DeactivateAsync(IEnumerable services) - { - return Task.WhenAll(services.Select(c => c.DeactivateAsync())); - } - - private static Task ActivateAsync(IEnumerable services) - { - return Task.WhenAll(services.Select(c => c.ActivateAsync())); - } + return Task.WhenAll(services.Select(c => c.ActivateAsync())); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.cs index 1bcd2313fe..2ca7f5d602 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectActivationTracking.cs @@ -1,55 +1,54 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Responsible for activating and deactivating instances in +/// response to changes in either project capabilities or the active project configuration. +/// +/// +/// Similar to and , +/// however those types apply to implicitly active configurations (of which there may be multiple). +/// By contrast, this type applies to the singular active configuration. +/// +[Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))] +[AppliesTo(LoadCapabilities)] +internal partial class ConfiguredProjectActivationTracking : AbstractMultiLifetimeComponent, IProjectDynamicLoadComponent { - /// - /// Responsible for activating and deactivating instances in - /// response to changes in either project capabilities or the active project configuration. - /// - /// - /// Similar to and , - /// however those types apply to implicitly active configurations (of which there may be multiple). - /// By contrast, this type applies to the singular active configuration. - /// - [Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))] - [AppliesTo(LoadCapabilities)] - internal partial class ConfiguredProjectActivationTracking : AbstractMultiLifetimeComponent, IProjectDynamicLoadComponent - { - // NOTE: Ideally this component would be marked with 'AlwaysApplicable' so that we always load - // IActiveConfigurationComponent instances in all project types regardless of exported capabilities, - // but doing so would cause the .NET Project System's assemblies to be loaded in lots of - // situations even when not needed. Instead, we explicitly hard code the set of capabilities of - // all our IActiveConfigurationComponent services. - private const string LoadCapabilities = "(" + ProjectCapability.DotNet + "+ !" + ProjectCapabilities.SharedAssetsProject + ")"; + // NOTE: Ideally this component would be marked with 'AlwaysApplicable' so that we always load + // IActiveConfigurationComponent instances in all project types regardless of exported capabilities, + // but doing so would cause the .NET Project System's assemblies to be loaded in lots of + // situations even when not needed. Instead, we explicitly hard code the set of capabilities of + // all our IActiveConfigurationComponent services. + private const string LoadCapabilities = "(" + ProjectCapability.DotNet + "+ !" + ProjectCapabilities.SharedAssetsProject + ")"; - private readonly IProjectThreadingService _threadingService; - private readonly ConfiguredProject _project; - private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; + private readonly IProjectThreadingService _threadingService; + private readonly ConfiguredProject _project; + private readonly IActiveConfiguredProjectProvider _activeConfiguredProjectProvider; - [ImportingConstructor] - public ConfiguredProjectActivationTracking( - IProjectThreadingService threadingService, - ConfiguredProject project, - IActiveConfiguredProjectProvider activeConfiguredProjectProvider) - : base(threadingService.JoinableTaskContext) - { - _threadingService = threadingService; - _project = project; - _activeConfiguredProjectProvider = activeConfiguredProjectProvider; + [ImportingConstructor] + public ConfiguredProjectActivationTracking( + IProjectThreadingService threadingService, + ConfiguredProject project, + IActiveConfiguredProjectProvider activeConfiguredProjectProvider) + : base(threadingService.JoinableTaskContext) + { + _threadingService = threadingService; + _project = project; + _activeConfiguredProjectProvider = activeConfiguredProjectProvider; - Components = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + Components = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - public OrderPrecedenceImportCollection Components { get; } + [ImportMany] + public OrderPrecedenceImportCollection Components { get; } - protected override ConfiguredProjectActivationTrackingInstance CreateInstance() - { - return new( - _threadingService, - _project, - _activeConfiguredProjectProvider, - Components); - } + protected override ConfiguredProjectActivationTrackingInstance CreateInstance() + { + return new( + _threadingService, + _project, + _activeConfiguredProjectProvider, + Components); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.Instance.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.Instance.cs index 2954aaed7a..7fa99ad9ec 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.Instance.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.Instance.cs @@ -3,106 +3,105 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.OperationProgress; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal sealed partial class ConfiguredProjectImplicitActivationTracking { - internal sealed partial class ConfiguredProjectImplicitActivationTracking + internal sealed class ConfiguredProjectImplicitActivationTrackingInstance : OnceInitializedOnceDisposedAsync, IMultiLifetimeInstance { - internal sealed class ConfiguredProjectImplicitActivationTrackingInstance : OnceInitializedOnceDisposedAsync, IMultiLifetimeInstance + private readonly ConfiguredProject _project; + private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; + private readonly ITargetBlock)>> _targetBlock; + private readonly IDataProgressTrackerService _dataProgressTrackerService; + private readonly OrderPrecedenceImportCollection _components; + + private IReadOnlyCollection _activeComponents = Array.Empty(); + private IDisposable? _subscription; + private IDataProgressTrackerServiceRegistration? _dataProgressTrackerRegistration; + + public ConfiguredProjectImplicitActivationTrackingInstance( + IProjectThreadingService threadingService, + ConfiguredProject project, + IActiveConfigurationGroupService activeConfigurationGroupService, + IDataProgressTrackerService dataProgressTrackerService, + OrderPrecedenceImportCollection components) + : base(threadingService.JoinableTaskContext) + { + _project = project; + _activeConfigurationGroupService = activeConfigurationGroupService; + _targetBlock = DataflowBlockFactory.CreateActionBlock)>>(OnChangeAsync, project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality); + _dataProgressTrackerService = dataProgressTrackerService; + _components = components; + } + + public Task InitializeAsync() + { + return InitializeAsync(CancellationToken.None); + } + + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _dataProgressTrackerRegistration = _dataProgressTrackerService.RegisterForIntelliSense(this, _project, nameof(ConfiguredProjectImplicitActivationTrackingInstance)); + + _subscription = ProjectDataSources.SyncLinkTo( + _project.Capabilities.SourceBlock.SyncLinkOptions(), + _activeConfigurationGroupService.ActiveConfigurationGroupSource.SourceBlock.SyncLinkOptions(), + linkOptions: DataflowOption.PropagateCompletion, + target: _targetBlock, + cancellationToken: cancellationToken); + + return Task.CompletedTask; + } + + protected override Task DisposeCoreAsync(bool initialized) + { + _dataProgressTrackerRegistration?.Dispose(); + _subscription?.Dispose(); + _targetBlock.Complete(); + + return DeactivateAsync(_activeComponents); + } + + private async Task OnChangeAsync(IProjectVersionedValue<(IProjectCapabilitiesSnapshot, IConfigurationGroup)> e) + { + // We'll get called back in main two situations (notwithstanding version-only updates): + // + // - The active configuration changed, and our configuration might + // be now implicitly active or might no longer be implicitly active. + // + // - The capabilities changed in our configuration. + // + // In both situations, we may need to activate or deactivate + // IImplicitlyActiveConfigurationComponent instances. + + IProjectCapabilitiesSnapshot snapshot = e.Value.Item1; + bool isActive = e.Value.Item2.Contains(_project.ProjectConfiguration); + + using var capabilitiesContext = ProjectCapabilitiesContext.CreateIsolatedContext(_project, snapshot); + + // If we're not active, there are no future services to activate + IReadOnlyCollection futureComponents = isActive + ? _components.Select(s => s.Value).ToList() + : Array.Empty(); + + var diff = new SetDiff(_activeComponents, futureComponents); + + await DeactivateAsync(diff.Removed); + await ActivateAsync(diff.Added); + + _activeComponents = futureComponents; + + _dataProgressTrackerRegistration?.NotifyOutputDataCalculated(e.DataSourceVersions); + } + + private static Task DeactivateAsync(IEnumerable services) + { + return Task.WhenAll(services.Select(c => c.DeactivateAsync())); + } + + private static Task ActivateAsync(IEnumerable services) { - private readonly ConfiguredProject _project; - private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; - private readonly ITargetBlock)>> _targetBlock; - private readonly IDataProgressTrackerService _dataProgressTrackerService; - private readonly OrderPrecedenceImportCollection _components; - - private IReadOnlyCollection _activeComponents = Array.Empty(); - private IDisposable? _subscription; - private IDataProgressTrackerServiceRegistration? _dataProgressTrackerRegistration; - - public ConfiguredProjectImplicitActivationTrackingInstance( - IProjectThreadingService threadingService, - ConfiguredProject project, - IActiveConfigurationGroupService activeConfigurationGroupService, - IDataProgressTrackerService dataProgressTrackerService, - OrderPrecedenceImportCollection components) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _activeConfigurationGroupService = activeConfigurationGroupService; - _targetBlock = DataflowBlockFactory.CreateActionBlock)>>(OnChangeAsync, project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality); - _dataProgressTrackerService = dataProgressTrackerService; - _components = components; - } - - public Task InitializeAsync() - { - return InitializeAsync(CancellationToken.None); - } - - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _dataProgressTrackerRegistration = _dataProgressTrackerService.RegisterForIntelliSense(this, _project, nameof(ConfiguredProjectImplicitActivationTrackingInstance)); - - _subscription = ProjectDataSources.SyncLinkTo( - _project.Capabilities.SourceBlock.SyncLinkOptions(), - _activeConfigurationGroupService.ActiveConfigurationGroupSource.SourceBlock.SyncLinkOptions(), - linkOptions: DataflowOption.PropagateCompletion, - target: _targetBlock, - cancellationToken: cancellationToken); - - return Task.CompletedTask; - } - - protected override Task DisposeCoreAsync(bool initialized) - { - _dataProgressTrackerRegistration?.Dispose(); - _subscription?.Dispose(); - _targetBlock.Complete(); - - return DeactivateAsync(_activeComponents); - } - - private async Task OnChangeAsync(IProjectVersionedValue<(IProjectCapabilitiesSnapshot, IConfigurationGroup)> e) - { - // We'll get called back in main two situations (notwithstanding version-only updates): - // - // - The active configuration changed, and our configuration might - // be now implicitly active or might no longer be implicitly active. - // - // - The capabilities changed in our configuration. - // - // In both situations, we may need to activate or deactivate - // IImplicitlyActiveConfigurationComponent instances. - - IProjectCapabilitiesSnapshot snapshot = e.Value.Item1; - bool isActive = e.Value.Item2.Contains(_project.ProjectConfiguration); - - using var capabilitiesContext = ProjectCapabilitiesContext.CreateIsolatedContext(_project, snapshot); - - // If we're not active, there are no future services to activate - IReadOnlyCollection futureComponents = isActive - ? _components.Select(s => s.Value).ToList() - : Array.Empty(); - - var diff = new SetDiff(_activeComponents, futureComponents); - - await DeactivateAsync(diff.Removed); - await ActivateAsync(diff.Added); - - _activeComponents = futureComponents; - - _dataProgressTrackerRegistration?.NotifyOutputDataCalculated(e.DataSourceVersions); - } - - private static Task DeactivateAsync(IEnumerable services) - { - return Task.WhenAll(services.Select(c => c.DeactivateAsync())); - } - - private static Task ActivateAsync(IEnumerable services) - { - return Task.WhenAll(services.Select(c => c.ActivateAsync())); - } + return Task.WhenAll(services.Select(c => c.ActivateAsync())); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.cs index b03d9094eb..6fc464287a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ConfiguredProjectImplicitActivationTracking.cs @@ -1,54 +1,53 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Responsible for activating and deactivating instances. +/// +[Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))] +[AppliesTo(LoadCapabilities)] +internal partial class ConfiguredProjectImplicitActivationTracking : AbstractMultiLifetimeComponent, IProjectDynamicLoadComponent { - /// - /// Responsible for activating and deactivating instances. - /// - [Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))] - [AppliesTo(LoadCapabilities)] - internal partial class ConfiguredProjectImplicitActivationTracking : AbstractMultiLifetimeComponent, IProjectDynamicLoadComponent - { - // NOTE: Ideally this component would be marked with 'AlwaysApplicable' so that we always load - // IImplicitlyActiveConfigurationComponent instances in all project types regardless of exported capabilities, - // but doing so would cause the .NET Project System's assemblies to be loaded in lots of - // situations even when not needed. Instead, we explicitly hard code the set of capabilities of - // all our IImplicitlyActiveConfigurationComponent services. - private const string LoadCapabilities = ProjectCapability.DotNetLanguageService + " | " + - ProjectCapability.PackageReferences; + // NOTE: Ideally this component would be marked with 'AlwaysApplicable' so that we always load + // IImplicitlyActiveConfigurationComponent instances in all project types regardless of exported capabilities, + // but doing so would cause the .NET Project System's assemblies to be loaded in lots of + // situations even when not needed. Instead, we explicitly hard code the set of capabilities of + // all our IImplicitlyActiveConfigurationComponent services. + private const string LoadCapabilities = ProjectCapability.DotNetLanguageService + " | " + + ProjectCapability.PackageReferences; - private readonly IProjectThreadingService _threadingService; - private readonly ConfiguredProject _project; - private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; - private readonly IDataProgressTrackerService _dataProgressTrackerService; + private readonly IProjectThreadingService _threadingService; + private readonly ConfiguredProject _project; + private readonly IActiveConfigurationGroupService _activeConfigurationGroupService; + private readonly IDataProgressTrackerService _dataProgressTrackerService; - [ImportingConstructor] - public ConfiguredProjectImplicitActivationTracking( - IProjectThreadingService threadingService, - ConfiguredProject project, - IActiveConfigurationGroupService activeConfigurationGroupService, - IDataProgressTrackerService dataProgressTrackerService) - : base(threadingService.JoinableTaskContext) - { - _threadingService = threadingService; - _project = project; - _activeConfigurationGroupService = activeConfigurationGroupService; - _dataProgressTrackerService = dataProgressTrackerService; + [ImportingConstructor] + public ConfiguredProjectImplicitActivationTracking( + IProjectThreadingService threadingService, + ConfiguredProject project, + IActiveConfigurationGroupService activeConfigurationGroupService, + IDataProgressTrackerService dataProgressTrackerService) + : base(threadingService.JoinableTaskContext) + { + _threadingService = threadingService; + _project = project; + _activeConfigurationGroupService = activeConfigurationGroupService; + _dataProgressTrackerService = dataProgressTrackerService; - Components = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + Components = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - public OrderPrecedenceImportCollection Components { get; } + [ImportMany] + public OrderPrecedenceImportCollection Components { get; } - protected override ConfiguredProjectImplicitActivationTrackingInstance CreateInstance() - { - return new( - _threadingService, - _project, - _activeConfigurationGroupService, - _dataProgressTrackerService, - Components); - } + protected override ConfiguredProjectImplicitActivationTrackingInstance CreateInstance() + { + return new( + _threadingService, + _project, + _activeConfigurationGroupService, + _dataProgressTrackerService, + Components); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ContractNames.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ContractNames.cs index e3c02c1114..a4bb01f877 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ContractNames.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ContractNames.cs @@ -1,71 +1,70 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Contract names to use for CPS exports/imports. (Ported over from +/// Microsoft.VisualStudio.ProjectSystem.ExportContractNames) +/// +internal static class ContractNames { /// - /// Contract names to use for CPS exports/imports. (Ported over from - /// Microsoft.VisualStudio.ProjectSystem.ExportContractNames) + /// The substring to prepend to all CPS-defined contract names. /// - internal static class ContractNames + private const string Prefix = "Microsoft.VisualStudio.ProjectSystem."; + + /// + /// Contract names for IProjectPropertiesProvider implementations. + /// + internal static class ProjectPropertyProviders { /// - /// The substring to prepend to all CPS-defined contract names. + /// Contract name for the property provider that reads MSBuild intrinsic properties. /// - private const string Prefix = "Microsoft.VisualStudio.ProjectSystem."; + internal const string Intrinsic = Prefix + "Intrinsic"; /// - /// Contract names for IProjectPropertiesProvider implementations. + /// Contract name for the property provider that reads/writes properties from the project file. /// - internal static class ProjectPropertyProviders - { - /// - /// Contract name for the property provider that reads MSBuild intrinsic properties. - /// - internal const string Intrinsic = Prefix + "Intrinsic"; - - /// - /// Contract name for the property provider that reads/writes properties from the project file. - /// - internal const string ProjectFile = Prefix + "ProjectFile"; + internal const string ProjectFile = Prefix + "ProjectFile"; - /// - /// Contract name for the property provider that reads/writes properties from the user file. - /// - internal const string UserFile = Prefix + "UserFile"; + /// + /// Contract name for the property provider that reads/writes properties from the user file. + /// + internal const string UserFile = Prefix + "UserFile"; - /// - /// Contract name for the property provider that reads/writes properties from the user file - /// and when properties are not defined in context falls back to defaults as specified - /// in the XAML file rather than from elsewhere in the project (e.g. such as .props files). - /// - internal const string UserFileWithXamlDefaults = UserFile + "WithXamlDefaults"; + /// + /// Contract name for the property provider that reads/writes properties from the user file + /// and when properties are not defined in context falls back to defaults as specified + /// in the XAML file rather than from elsewhere in the project (e.g. such as .props files). + /// + internal const string UserFileWithXamlDefaults = UserFile + "WithXamlDefaults"; - /// - /// Contract name for the property provider that reads/writes special properties from the project file for assembly references. - /// - internal const string AssemblyReference = Prefix + "AssemblyReference"; - } + /// + /// Contract name for the property provider that reads/writes special properties from the project file for assembly references. + /// + internal const string AssemblyReference = Prefix + "AssemblyReference"; + } + /// + /// Contracts used by CPS exports of MSBuild objects. + /// + internal static class MSBuild + { /// - /// Contracts used by CPS exports of MSBuild objects. + /// The contract name on the IProjectGlobalPropertiesProvider export that publishes all the global properties on the GlobalProjectCollection. /// - internal static class MSBuild - { - /// - /// The contract name on the IProjectGlobalPropertiesProvider export that publishes all the global properties on the GlobalProjectCollection. - /// - internal const string GlobalProjectCollectionGlobalProperties = Prefix + "GlobalProjectCollection.GlobalProperties"; - } + internal const string GlobalProjectCollectionGlobalProperties = Prefix + "GlobalProjectCollection.GlobalProperties"; + } + /// + /// Contracts used by tree providers. + /// + internal static class ProjectTreeProviders + { /// - /// Contracts used by tree providers. + /// The tree of the exact contents of the project directory. /// - internal static class ProjectTreeProviders - { - /// - /// The tree of the exact contents of the project directory. - /// - internal const string FileSystemDirectoryTree = Prefix + "FileSystemDirectory"; - } + internal const string FileSystemDirectoryTree = Prefix + "FileSystemDirectory"; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/ClipboardFormat.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/ClipboardFormat.cs index a8b630a166..7b5db787ac 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/ClipboardFormat.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/ClipboardFormat.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.CopyPaste +namespace Microsoft.VisualStudio.ProjectSystem.CopyPaste; + +internal static class ClipboardFormat { - internal static class ClipboardFormat - { - /// - /// A ushort used by data objects to get and set the text format. Constant defined in WinUser.h. - /// - internal const ushort CF_TEXT = 1; + /// + /// A ushort used by data objects to get and set the text format. Constant defined in WinUser.h. + /// + internal const ushort CF_TEXT = 1; - /// - /// A ushort used by data objects to get and set the unicode text format. Constant defined in WinUser.h. - /// - internal const ushort CF_UNICODETEXT = 13; - } + /// + /// A ushort used by data objects to get and set the unicode text format. Constant defined in WinUser.h. + /// + internal const ushort CF_UNICODETEXT = 13; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/DependencyTextPackager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/DependencyTextPackager.cs index f50b2366f1..7661f54497 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/DependencyTextPackager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/CopyPaste/DependencyTextPackager.cs @@ -4,77 +4,76 @@ using System.Text; using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies; -namespace Microsoft.VisualStudio.ProjectSystem.CopyPaste +namespace Microsoft.VisualStudio.ProjectSystem.CopyPaste; + +/// +/// Packages dependency nodes as text for the "Copy Full Path" command. +/// +[Export(typeof(ICopyPackager))] +[AppliesTo(ProjectCapability.DependenciesTree)] +[Order(Order.Default)] +internal class DependencyTextPackager : ICopyPackager { - /// - /// Packages dependency nodes as text for the "Copy Full Path" command. - /// - [Export(typeof(ICopyPackager))] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order.Default)] - internal class DependencyTextPackager : ICopyPackager - { - private static readonly ImmutableHashSet s_formats = ImmutableHashSet.Create(ClipboardFormat.CF_TEXT, ClipboardFormat.CF_UNICODETEXT); - - private readonly UnconfiguredProject _project; - - [ImportingConstructor] - public DependencyTextPackager(UnconfiguredProject project) - { - _project = project; - } + private static readonly ImmutableHashSet s_formats = ImmutableHashSet.Create(ClipboardFormat.CF_TEXT, ClipboardFormat.CF_UNICODETEXT); - public IImmutableSet ClipboardDataFormats - { - get { return s_formats; } - } + private readonly UnconfiguredProject _project; - public CopyPasteOperations GetAllowedOperations(IEnumerable selectedNodes, IProjectTreeProvider currentProvider) - { - return IsValidSetOfNodes(selectedNodes) ? CopyPasteOperations.Copy : CopyPasteOperations.None; - } - - public async Task>> GetPointerToDataAsync(IReadOnlyCollection types, IEnumerable selectedNodes, IProjectTreeProvider currentProvider) - { - var paths = new StringBuilder(); - var data = new List>(); + [ImportingConstructor] + public DependencyTextPackager(UnconfiguredProject project) + { + _project = project; + } - foreach (IProjectTree node in selectedNodes) - { - string? path = await DependencyServices.GetBrowsePathAsync(_project, node); - if (path is null) - continue; + public IImmutableSet ClipboardDataFormats + { + get { return s_formats; } + } - // Note we leave trailing slashes to mimic what happens with normal folders - if (node.Flags.Contains(DependencyTreeFlags.SupportsFolderBrowse)) - path = PathHelper.EnsureTrailingSlash(path); + public CopyPasteOperations GetAllowedOperations(IEnumerable selectedNodes, IProjectTreeProvider currentProvider) + { + return IsValidSetOfNodes(selectedNodes) ? CopyPasteOperations.Copy : CopyPasteOperations.None; + } - if (paths.Length > 0) - { - paths.AppendLine(); - } + public async Task>> GetPointerToDataAsync(IReadOnlyCollection types, IEnumerable selectedNodes, IProjectTreeProvider currentProvider) + { + var paths = new StringBuilder(); + var data = new List>(); - paths.Append(path); - } + foreach (IProjectTree node in selectedNodes) + { + string? path = await DependencyServices.GetBrowsePathAsync(_project, node); + if (path is null) + continue; - if (types.Contains(ClipboardFormat.CF_TEXT)) - { - data.Add(new Tuple(ClipboardFormat.CF_TEXT, Marshal.StringToHGlobalAnsi(paths.ToString()))); - } + // Note we leave trailing slashes to mimic what happens with normal folders + if (node.Flags.Contains(DependencyTreeFlags.SupportsFolderBrowse)) + path = PathHelper.EnsureTrailingSlash(path); - if (types.Contains(ClipboardFormat.CF_UNICODETEXT)) + if (paths.Length > 0) { - data.Add(new Tuple(ClipboardFormat.CF_UNICODETEXT, Marshal.StringToHGlobalUni(paths.ToString()))); + paths.AppendLine(); } - return data; + paths.Append(path); } - private static bool IsValidSetOfNodes(IEnumerable treeNodes) + if (types.Contains(ClipboardFormat.CF_TEXT)) { - Requires.NotNull(treeNodes); + data.Add(new Tuple(ClipboardFormat.CF_TEXT, Marshal.StringToHGlobalAnsi(paths.ToString()))); + } - return treeNodes.All(node => node.Flags.Contains(DependencyTreeFlags.Dependency | DependencyTreeFlags.SupportsBrowse)); + if (types.Contains(ClipboardFormat.CF_UNICODETEXT)) + { + data.Add(new Tuple(ClipboardFormat.CF_UNICODETEXT, Marshal.StringToHGlobalUni(paths.ToString()))); } + + return data; + } + + private static bool IsValidSetOfNodes(IEnumerable treeNodes) + { + Requires.NotNull(treeNodes); + + return treeNodes.All(node => node.Flags.Contains(DependencyTreeFlags.Dependency | DependencyTreeFlags.SupportsBrowse)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowBlockFactory.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowBlockFactory.cs index 05d75cf694..14d910837b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowBlockFactory.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowBlockFactory.cs @@ -4,51 +4,50 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Produces instances. +/// +internal static class DataflowBlockFactory { /// - /// Produces instances. + /// Provides a dataflow block that invokes a provided delegate for every data element received. /// - internal static class DataflowBlockFactory + public static ITargetBlock CreateActionBlock(Action target, UnconfiguredProject project, ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, string? nameFormat = null, bool skipIntermediateInputData = false) { - /// - /// Provides a dataflow block that invokes a provided delegate for every data element received. - /// - public static ITargetBlock CreateActionBlock(Action target, UnconfiguredProject project, ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, string? nameFormat = null, bool skipIntermediateInputData = false) - { - Requires.NotNull(target); - Requires.NotNull(project); + Requires.NotNull(target); + Requires.NotNull(project); - ITargetBlock block = DataflowBlockSlim.CreateActionBlock(target, nameFormat, skipIntermediateInputData: skipIntermediateInputData); + ITargetBlock block = DataflowBlockSlim.CreateActionBlock(target, nameFormat, skipIntermediateInputData: skipIntermediateInputData); - RegisterFaultHandler(block, project, severity); + RegisterFaultHandler(block, project, severity); - return block; - } + return block; + } - /// - /// Provides a dataflow block that invokes a provided delegate for every data element received. - /// - public static ITargetBlock CreateActionBlock(Func target, UnconfiguredProject project, ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, string? nameFormat = null, bool skipIntermediateInputData = false) - { - Requires.NotNull(target); - Requires.NotNull(project); + /// + /// Provides a dataflow block that invokes a provided delegate for every data element received. + /// + public static ITargetBlock CreateActionBlock(Func target, UnconfiguredProject project, ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, string? nameFormat = null, bool skipIntermediateInputData = false) + { + Requires.NotNull(target); + Requires.NotNull(project); - ITargetBlock block = DataflowBlockSlim.CreateActionBlock(target, nameFormat, skipIntermediateInputData: skipIntermediateInputData); + ITargetBlock block = DataflowBlockSlim.CreateActionBlock(target, nameFormat, skipIntermediateInputData: skipIntermediateInputData); - RegisterFaultHandler(block, project, severity); + RegisterFaultHandler(block, project, severity); - return block; - } + return block; + } - private static void RegisterFaultHandler(IDataflowBlock block, UnconfiguredProject project, ProjectFaultSeverity severity) - { - IProjectFaultHandlerService faultHandlerService = project.Services.FaultHandler; + private static void RegisterFaultHandler(IDataflowBlock block, UnconfiguredProject project, ProjectFaultSeverity severity) + { + IProjectFaultHandlerService faultHandlerService = project.Services.FaultHandler; - Task faultTask = faultHandlerService.RegisterFaultHandlerAsync(block, project, severity); + Task faultTask = faultHandlerService.RegisterFaultHandlerAsync(block, project, severity); - // We don't actually care about the result of reporting the fault if one occurs - faultHandlerService.Forget(faultTask, project); - } + // We don't actually care about the result of reporting the fault if one occurs + faultHandlerService.Forget(faultTask, project); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowOption.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowOption.cs index 641ef36edf..8859aadd1c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowOption.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowOption.cs @@ -5,74 +5,73 @@ using System.Threading.Tasks.Dataflow; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides properties and methods containing common dataflow link and block options. +/// +internal static class DataflowOption { /// - /// Provides properties and methods containing common dataflow link and block options. + /// Returns a new instance of with + /// set to . /// - internal static class DataflowOption + public static DataflowLinkOptions PropagateCompletion { - /// - /// Returns a new instance of with - /// set to . - /// - public static DataflowLinkOptions PropagateCompletion + get => new() { - get => new() - { - // Make sure source block completion and faults flow onto the target block to avoid hangs. - PropagateCompletion = true - }; - } + // Make sure source block completion and faults flow onto the target block to avoid hangs. + PropagateCompletion = true + }; + } - /// - /// Returns a new instance of with - /// set to - /// and set to . - /// - /// - /// is . - /// - public static StandardRuleDataflowLinkOptions WithRuleNames(string ruleName) - { - Requires.NotNull(ruleName); + /// + /// Returns a new instance of with + /// set to + /// and set to . + /// + /// + /// is . + /// + public static StandardRuleDataflowLinkOptions WithRuleNames(string ruleName) + { + Requires.NotNull(ruleName); - return WithRuleNames(ImmutableHashSet.Create(ruleName)); - } + return WithRuleNames(ImmutableHashSet.Create(ruleName)); + } - /// - /// Returns a new instance of with - /// set to - /// and set to . - /// - /// - /// is . - /// - public static StandardRuleDataflowLinkOptions WithRuleNames(IImmutableSet ruleNames) - { - Requires.NotNull(ruleNames); + /// + /// Returns a new instance of with + /// set to + /// and set to . + /// + /// + /// is . + /// + public static StandardRuleDataflowLinkOptions WithRuleNames(IImmutableSet ruleNames) + { + Requires.NotNull(ruleNames); - // This class sets PropagateCompletion by default, so we don't have to set it here again. - return new() - { - RuleNames = ruleNames - }; - } + // This class sets PropagateCompletion by default, so we don't have to set it here again. + return new() + { + RuleNames = ruleNames + }; + } - /// - /// Returns a new instance of with - /// set to , - /// set to - /// and set to . - /// - public static JointRuleDataflowLinkOptions WithJointRuleNames(ImmutableHashSet evaluationRuleNames, ImmutableHashSet buildRuleNames) + /// + /// Returns a new instance of with + /// set to , + /// set to + /// and set to . + /// + public static JointRuleDataflowLinkOptions WithJointRuleNames(ImmutableHashSet evaluationRuleNames, ImmutableHashSet buildRuleNames) + { + // This class sets PropagateCompletion by default, so we don't have to set it here again. + return new() { - // This class sets PropagateCompletion by default, so we don't have to set it here again. - return new() - { - EvaluationRuleNames = evaluationRuleNames, - BuildRuleNames = buildRuleNames - }; - } + EvaluationRuleNames = evaluationRuleNames, + BuildRuleNames = buildRuleNames + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowUtilities.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowUtilities.cs index 6b58315e22..3441eb2ca8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowUtilities.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/DataflowUtilities.cs @@ -2,578 +2,577 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class DataflowUtilities { - internal static class DataflowUtilities + /// + /// Links to the specified to receive a cross-sectional slice of project + /// data, including detailed descriptions of what changed between snapshots, as described by + /// specified rules. + /// + /// + /// The broadcasting block that produces the messages. + /// + /// + /// The to receive the broadcasts. + /// + /// + /// The project related to the failure, if applicable. + /// + /// + /// The severity of any failure that occurs. + /// + /// + /// A value indicating whether to prevent messages from propagating to the target + /// block if no project changes are include other than an incremented version number. + /// + /// + /// The names of the rules that describe the project data the caller is interested in. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static IDisposable LinkToAction( + this ISourceBlock> source, + Action> target, + UnconfiguredProject project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, + bool suppressVersionOnlyUpdates = true, + params string[] ruleNames) { - /// - /// Links to the specified to receive a cross-sectional slice of project - /// data, including detailed descriptions of what changed between snapshots, as described by - /// specified rules. - /// - /// - /// The broadcasting block that produces the messages. - /// - /// - /// The to receive the broadcasts. - /// - /// - /// The project related to the failure, if applicable. - /// - /// - /// The severity of any failure that occurs. - /// - /// - /// A value indicating whether to prevent messages from propagating to the target - /// block if no project changes are include other than an incremented version number. - /// - /// - /// The names of the rules that describe the project data the caller is interested in. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static IDisposable LinkToAction( - this ISourceBlock> source, - Action> target, - UnconfiguredProject project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, - bool suppressVersionOnlyUpdates = true, - params string[] ruleNames) - { - Requires.NotNull(source); - Requires.NotNull(target); - Requires.NotNull(project); - - return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, severity), - DataflowOption.PropagateCompletion, - initialDataAsNew: true, - suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, - ruleNames: ruleNames); - } - - /// - /// Links to the specified to receive a cross-sectional slice of project - /// data, including detailed descriptions of what changed between snapshots, as described by - /// specified rules. - /// - /// - /// The broadcasting block that produces the messages. - /// - /// - /// The to receive the broadcasts. - /// - /// - /// The project related to the failure, if applicable. - /// - /// - /// The severity of any failure that occurs. - /// - /// - /// A value indicating whether to prevent messages from propagating to the target - /// block if no project changes are include other than an incremented version number. - /// - /// - /// The names of the rules that describe the project data the caller is interested in. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static IDisposable LinkToAction( - this ISourceBlock> source, - Action> target, - UnconfiguredProject project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, - bool suppressVersionOnlyUpdates = true, - IEnumerable? ruleNames = null) - { - Requires.NotNull(source); - Requires.NotNull(target); - Requires.NotNull(project); - - return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, severity), - DataflowOption.PropagateCompletion, - initialDataAsNew: true, - suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, - ruleNames: ruleNames); - } - - /// - /// Links to the specified to receive a cross-sectional slice of project - /// data, including detailed descriptions of what changed between snapshots, as described by - /// specified rules. - /// - /// - /// The broadcasting block that produces the messages. - /// - /// - /// The to receive the broadcasts. - /// - /// - /// The project related to the failure, if applicable. - /// - /// - /// A value indicating whether to prevent messages from propagating to the target - /// block if no project changes are include other than an incremented version number. - /// - /// - /// The names of the rules that describe the project data the caller is interested in. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static IDisposable LinkToAsyncAction( - this ISourceBlock> source, - Func, Task> target, - UnconfiguredProject project, - bool suppressVersionOnlyUpdates = true, - params string[] ruleNames) - { - Requires.NotNull(source); - Requires.NotNull(target); - Requires.NotNull(project); - - return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, ProjectFaultSeverity.Recoverable), - DataflowOption.PropagateCompletion, - initialDataAsNew: true, - suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, - ruleNames: ruleNames); - } - - /// - /// Links to the specified to receive a cross-sectional slice of project - /// data, including detailed descriptions of what changed between snapshots, as described by - /// specified rules. - /// - /// - /// The broadcasting block that produces the messages. - /// - /// - /// The to receive the broadcasts. - /// - /// - /// The project related to the failure, if applicable. - /// - /// - /// The severity of any failure that occurs. - /// - /// - /// A value indicating whether to prevent messages from propagating to the target - /// block if no project changes are include other than an incremented version number. - /// - /// - /// The names of the rules that describe the project data the caller is interested in. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static IDisposable LinkToAsyncAction( - this ISourceBlock> source, - Func, Task> target, - UnconfiguredProject project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, - bool suppressVersionOnlyUpdates = true, - IEnumerable? ruleNames = null) - { - Requires.NotNull(source); - Requires.NotNull(target); - Requires.NotNull(project); - - return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, severity), - DataflowOption.PropagateCompletion, - initialDataAsNew: true, - suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, - ruleNames: ruleNames); - } - - /// - /// Links the to the specified - /// that can process messages, propagating completion and faults. - /// - /// - /// The broadcasting block that produces the messages. - /// - /// - /// The to receive the broadcasts. - /// - /// - /// The project related to the failure, if applicable. - /// - /// - /// An that, upon calling Dispose, will unlink the source from the target. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static IDisposable LinkToAction( - this ISourceBlock source, - Action target, - UnconfiguredProject project) - { - Requires.NotNull(source); - Requires.NotNull(target); - Requires.NotNull(project); - - return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, ProjectFaultSeverity.Recoverable), - DataflowOption.PropagateCompletion); - } - - /// - /// Links the to the specified - /// that can process messages, propagating completion and faults. - /// - /// - /// An that, upon calling Dispose, will unlink the source from the target. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static IDisposable LinkToAsyncAction( - this ISourceBlock source, - Func target, - UnconfiguredProject project) - { - Requires.NotNull(source); - Requires.NotNull(target); - Requires.NotNull(project); - - return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, ProjectFaultSeverity.Recoverable), - DataflowOption.PropagateCompletion); - } - - /// - /// Creates a source block that produces a transformed value for each value from original source block. - /// - /// - /// The type of the input value produced by . - /// - /// - /// The type of value produced by . - /// - /// - /// The source block whose values are to be transformed. - /// - /// - /// The function to execute on each value from . - /// - /// - /// The transformed source block and a disposable value that terminates the link. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static DisposableValue> Transform( - this ISourceBlock> source, - Func, TOut> transform) - { - Requires.NotNull(source); - Requires.NotNull(transform); - - IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform); - - IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); - - return new DisposableValue>(transformBlock, link); - } - - /// - /// Creates a source block that produces multiple transformed values for each value from original source block, - /// skipping intermediate input and output states, and hence is not suitable for producing or consuming - /// deltas. - /// - /// - /// The type of the input value produced by . - /// - /// - /// The type of value produced by . - /// - /// - /// The source block whose values are to be transformed. - /// - /// - /// The function to execute on each value from . - /// - /// - /// The transformed source block and a disposable value that terminates the link. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static DisposableValue> TransformManyWithNoDelta( - this ISourceBlock> source, - Func, Task>> transform) - { - Requires.NotNull(source); - Requires.NotNull(transform); - - IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformManyBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); - - IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); - - return new DisposableValue>(transformBlock, link); - } - - /// - /// Creates a source block that produces a transformed value for each value from original source block, - /// skipping intermediate input and output states, and hence is not suitable for producing or consuming - /// deltas. - /// - /// - /// The type of the input value produced by . - /// - /// - /// The type of value produced by . - /// - /// - /// The source block whose values are to be transformed. - /// - /// - /// The function to execute on each value from . - /// - /// - /// The transformed source block and a disposable value that terminates the link. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static DisposableValue> TransformWithNoDelta( - this ISourceBlock> source, - Func, TOut> transform) - { - Requires.NotNull(source); - Requires.NotNull(transform); - - IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); - - IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); - - return new DisposableValue>(transformBlock, link); - } - - /// - /// Creates a source block that produces a transformed value for each value from original source block, - /// skipping intermediate input and output states, and hence is not suitable for producing or consuming - /// deltas. - /// - /// - /// The type of the input value produced by . - /// - /// - /// The type of value produced by . - /// - /// - /// The source block whose values are to be transformed. - /// - /// - /// The function to execute on each value from . - /// - /// - /// The transformed source block and a disposable value that terminates the link. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static DisposableValue> TransformWithNoDelta( - this ISourceBlock> source, - Func, Task> transform) - { - Requires.NotNull(source); - Requires.NotNull(transform); - - IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); - - IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); - - return new DisposableValue>(transformBlock, link); - } - - /// - /// Creates a source block that produces a transformed value for each value from original source block, - /// skipping intermediate input and output states, and hence is not suitable for producing or consuming - /// deltas. - /// - /// - /// The type of value produced by . - /// - /// - /// The source block whose values are to be transformed. - /// - /// - /// The function to execute on each value from . - /// - /// - /// A value indicating whether to prevent messages from propagating to the target - /// block if no project changes are include other than an incremented version number. - /// - /// - /// The names of the rules that describe the project data the caller is interested in. - /// - /// - /// The transformed source block and a disposable value that terminates the link. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static DisposableValue> TransformWithNoDelta( - this ISourceBlock> source, - Func, TOut> transform, - bool suppressVersionOnlyUpdates, - params string[] ruleNames) - { - return TransformWithNoDelta(source, transform, suppressVersionOnlyUpdates, (IEnumerable)ruleNames); - } - - /// - /// Creates a source block that produces a transformed value for each value from original source block, - /// skipping intermediate input and output states, and hence is not suitable for producing or consuming - /// deltas. - /// - /// - /// The type of value produced by . - /// - /// - /// The source block whose values are to be transformed. - /// - /// - /// The function to execute on each value from . - /// - /// - /// A value indicating whether to prevent messages from propagating to the target - /// block if no project changes are include other than an incremented version number. - /// - /// - /// The names of the rules that describe the project data the caller is interested in. - /// - /// - /// The transformed source block and a disposable value that terminates the link. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static DisposableValue> TransformWithNoDelta( - this ISourceBlock> source, - Func, TOut> transform, - bool suppressVersionOnlyUpdates, - IEnumerable? ruleNames = null) - { - Requires.NotNull(source); - Requires.NotNull(transform); - - IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); - - IDisposable link = source.LinkTo(transformBlock, - DataflowOption.PropagateCompletion, - initialDataAsNew: true, - suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, - ruleNames: ruleNames); - - return new DisposableValue>(transformBlock, link); - } - - /// - /// Wraps a delegate in a repeatably executable delegate that runs within an ExecutionContext captured at the time of *this* method call. - /// - /// The type of input parameter that is taken by the delegate. - /// The delegate to invoke when the returned delegate is invoked. - /// The wrapper delegate. - /// - /// This is useful because Dataflow doesn't capture or apply ExecutionContext for its delegates, - /// so the delegate runs in whatever ExecutionContext happened to call ITargetBlock.Post, which is - /// never the behavior we actually want. We've been bitten several times by bugs due to this. - /// Ironically, in Dataflow's early days it *did* have the desired behavior but they removed it - /// when they pulled it out of the Framework so it could be 'security transparent'. - /// By passing block delegates through this wrapper, we can reattain the old behavior. - /// - internal static Func CaptureAndApplyExecutionContext(Func function) + Requires.NotNull(source); + Requires.NotNull(target); + Requires.NotNull(project); + + return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, severity), + DataflowOption.PropagateCompletion, + initialDataAsNew: true, + suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, + ruleNames: ruleNames); + } + + /// + /// Links to the specified to receive a cross-sectional slice of project + /// data, including detailed descriptions of what changed between snapshots, as described by + /// specified rules. + /// + /// + /// The broadcasting block that produces the messages. + /// + /// + /// The to receive the broadcasts. + /// + /// + /// The project related to the failure, if applicable. + /// + /// + /// The severity of any failure that occurs. + /// + /// + /// A value indicating whether to prevent messages from propagating to the target + /// block if no project changes are include other than an incremented version number. + /// + /// + /// The names of the rules that describe the project data the caller is interested in. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static IDisposable LinkToAction( + this ISourceBlock> source, + Action> target, + UnconfiguredProject project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, + bool suppressVersionOnlyUpdates = true, + IEnumerable? ruleNames = null) + { + Requires.NotNull(source); + Requires.NotNull(target); + Requires.NotNull(project); + + return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, severity), + DataflowOption.PropagateCompletion, + initialDataAsNew: true, + suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, + ruleNames: ruleNames); + } + + /// + /// Links to the specified to receive a cross-sectional slice of project + /// data, including detailed descriptions of what changed between snapshots, as described by + /// specified rules. + /// + /// + /// The broadcasting block that produces the messages. + /// + /// + /// The to receive the broadcasts. + /// + /// + /// The project related to the failure, if applicable. + /// + /// + /// A value indicating whether to prevent messages from propagating to the target + /// block if no project changes are include other than an incremented version number. + /// + /// + /// The names of the rules that describe the project data the caller is interested in. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static IDisposable LinkToAsyncAction( + this ISourceBlock> source, + Func, Task> target, + UnconfiguredProject project, + bool suppressVersionOnlyUpdates = true, + params string[] ruleNames) + { + Requires.NotNull(source); + Requires.NotNull(target); + Requires.NotNull(project); + + return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, ProjectFaultSeverity.Recoverable), + DataflowOption.PropagateCompletion, + initialDataAsNew: true, + suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, + ruleNames: ruleNames); + } + + /// + /// Links to the specified to receive a cross-sectional slice of project + /// data, including detailed descriptions of what changed between snapshots, as described by + /// specified rules. + /// + /// + /// The broadcasting block that produces the messages. + /// + /// + /// The to receive the broadcasts. + /// + /// + /// The project related to the failure, if applicable. + /// + /// + /// The severity of any failure that occurs. + /// + /// + /// A value indicating whether to prevent messages from propagating to the target + /// block if no project changes are include other than an incremented version number. + /// + /// + /// The names of the rules that describe the project data the caller is interested in. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static IDisposable LinkToAsyncAction( + this ISourceBlock> source, + Func, Task> target, + UnconfiguredProject project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, + bool suppressVersionOnlyUpdates = true, + IEnumerable? ruleNames = null) + { + Requires.NotNull(source); + Requires.NotNull(target); + Requires.NotNull(project); + + return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, severity), + DataflowOption.PropagateCompletion, + initialDataAsNew: true, + suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, + ruleNames: ruleNames); + } + + /// + /// Links the to the specified + /// that can process messages, propagating completion and faults. + /// + /// + /// The broadcasting block that produces the messages. + /// + /// + /// The to receive the broadcasts. + /// + /// + /// The project related to the failure, if applicable. + /// + /// + /// An that, upon calling Dispose, will unlink the source from the target. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static IDisposable LinkToAction( + this ISourceBlock source, + Action target, + UnconfiguredProject project) + { + Requires.NotNull(source); + Requires.NotNull(target); + Requires.NotNull(project); + + return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, ProjectFaultSeverity.Recoverable), + DataflowOption.PropagateCompletion); + } + + /// + /// Links the to the specified + /// that can process messages, propagating completion and faults. + /// + /// + /// An that, upon calling Dispose, will unlink the source from the target. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static IDisposable LinkToAsyncAction( + this ISourceBlock source, + Func target, + UnconfiguredProject project) + { + Requires.NotNull(source); + Requires.NotNull(target); + Requires.NotNull(project); + + return source.LinkTo(DataflowBlockFactory.CreateActionBlock(target, project, ProjectFaultSeverity.Recoverable), + DataflowOption.PropagateCompletion); + } + + /// + /// Creates a source block that produces a transformed value for each value from original source block. + /// + /// + /// The type of the input value produced by . + /// + /// + /// The type of value produced by . + /// + /// + /// The source block whose values are to be transformed. + /// + /// + /// The function to execute on each value from . + /// + /// + /// The transformed source block and a disposable value that terminates the link. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static DisposableValue> Transform( + this ISourceBlock> source, + Func, TOut> transform) + { + Requires.NotNull(source); + Requires.NotNull(transform); + + IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform); + + IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); + + return new DisposableValue>(transformBlock, link); + } + + /// + /// Creates a source block that produces multiple transformed values for each value from original source block, + /// skipping intermediate input and output states, and hence is not suitable for producing or consuming + /// deltas. + /// + /// + /// The type of the input value produced by . + /// + /// + /// The type of value produced by . + /// + /// + /// The source block whose values are to be transformed. + /// + /// + /// The function to execute on each value from . + /// + /// + /// The transformed source block and a disposable value that terminates the link. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static DisposableValue> TransformManyWithNoDelta( + this ISourceBlock> source, + Func, Task>> transform) + { + Requires.NotNull(source); + Requires.NotNull(transform); + + IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformManyBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); + + IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); + + return new DisposableValue>(transformBlock, link); + } + + /// + /// Creates a source block that produces a transformed value for each value from original source block, + /// skipping intermediate input and output states, and hence is not suitable for producing or consuming + /// deltas. + /// + /// + /// The type of the input value produced by . + /// + /// + /// The type of value produced by . + /// + /// + /// The source block whose values are to be transformed. + /// + /// + /// The function to execute on each value from . + /// + /// + /// The transformed source block and a disposable value that terminates the link. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static DisposableValue> TransformWithNoDelta( + this ISourceBlock> source, + Func, TOut> transform) + { + Requires.NotNull(source); + Requires.NotNull(transform); + + IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); + + IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); + + return new DisposableValue>(transformBlock, link); + } + + /// + /// Creates a source block that produces a transformed value for each value from original source block, + /// skipping intermediate input and output states, and hence is not suitable for producing or consuming + /// deltas. + /// + /// + /// The type of the input value produced by . + /// + /// + /// The type of value produced by . + /// + /// + /// The source block whose values are to be transformed. + /// + /// + /// The function to execute on each value from . + /// + /// + /// The transformed source block and a disposable value that terminates the link. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static DisposableValue> TransformWithNoDelta( + this ISourceBlock> source, + Func, Task> transform) + { + Requires.NotNull(source); + Requires.NotNull(transform); + + IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); + + IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion); + + return new DisposableValue>(transformBlock, link); + } + + /// + /// Creates a source block that produces a transformed value for each value from original source block, + /// skipping intermediate input and output states, and hence is not suitable for producing or consuming + /// deltas. + /// + /// + /// The type of value produced by . + /// + /// + /// The source block whose values are to be transformed. + /// + /// + /// The function to execute on each value from . + /// + /// + /// A value indicating whether to prevent messages from propagating to the target + /// block if no project changes are include other than an incremented version number. + /// + /// + /// The names of the rules that describe the project data the caller is interested in. + /// + /// + /// The transformed source block and a disposable value that terminates the link. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static DisposableValue> TransformWithNoDelta( + this ISourceBlock> source, + Func, TOut> transform, + bool suppressVersionOnlyUpdates, + params string[] ruleNames) + { + return TransformWithNoDelta(source, transform, suppressVersionOnlyUpdates, (IEnumerable)ruleNames); + } + + /// + /// Creates a source block that produces a transformed value for each value from original source block, + /// skipping intermediate input and output states, and hence is not suitable for producing or consuming + /// deltas. + /// + /// + /// The type of value produced by . + /// + /// + /// The source block whose values are to be transformed. + /// + /// + /// The function to execute on each value from . + /// + /// + /// A value indicating whether to prevent messages from propagating to the target + /// block if no project changes are include other than an incremented version number. + /// + /// + /// The names of the rules that describe the project data the caller is interested in. + /// + /// + /// The transformed source block and a disposable value that terminates the link. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static DisposableValue> TransformWithNoDelta( + this ISourceBlock> source, + Func, TOut> transform, + bool suppressVersionOnlyUpdates, + IEnumerable? ruleNames = null) + { + Requires.NotNull(source); + Requires.NotNull(transform); + + IPropagatorBlock, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); + + IDisposable link = source.LinkTo(transformBlock, + DataflowOption.PropagateCompletion, + initialDataAsNew: true, + suppressVersionOnlyUpdates: suppressVersionOnlyUpdates, + ruleNames: ruleNames); + + return new DisposableValue>(transformBlock, link); + } + + /// + /// Wraps a delegate in a repeatably executable delegate that runs within an ExecutionContext captured at the time of *this* method call. + /// + /// The type of input parameter that is taken by the delegate. + /// The delegate to invoke when the returned delegate is invoked. + /// The wrapper delegate. + /// + /// This is useful because Dataflow doesn't capture or apply ExecutionContext for its delegates, + /// so the delegate runs in whatever ExecutionContext happened to call ITargetBlock.Post, which is + /// never the behavior we actually want. We've been bitten several times by bugs due to this. + /// Ironically, in Dataflow's early days it *did* have the desired behavior but they removed it + /// when they pulled it out of the Framework so it could be 'security transparent'. + /// By passing block delegates through this wrapper, we can reattain the old behavior. + /// + internal static Func CaptureAndApplyExecutionContext(Func function) + { + var context = ExecutionContext.Capture(); + return input => { - var context = ExecutionContext.Capture(); - return input => - { - SynchronizationContext currentSynchronizationContext = SynchronizationContext.Current; - using ExecutionContext copy = context.CreateCopy(); - Task? result = null; - ExecutionContext.Run( - copy, - state => - { - SynchronizationContext.SetSynchronizationContext(currentSynchronizationContext); - result = function(input); - }, - null); - return result!; - }; - } + SynchronizationContext currentSynchronizationContext = SynchronizationContext.Current; + using ExecutionContext copy = context.CreateCopy(); + Task? result = null; + ExecutionContext.Run( + copy, + state => + { + SynchronizationContext.SetSynchronizationContext(currentSynchronizationContext); + result = function(input); + }, + null); + return result!; + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ActiveDebugFrameworkServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ActiveDebugFrameworkServices.cs index 0a50d3bc76..9112c57384 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ActiveDebugFrameworkServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ActiveDebugFrameworkServices.cs @@ -2,107 +2,106 @@ using Microsoft.VisualStudio.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Providers a wrapper around the what is considered the active debugging framework. +/// +[Export(typeof(IActiveDebugFrameworkServices))] +[AppliesTo(ProjectCapability.DotNet)] +internal class ActiveDebugFrameworkServices : IActiveDebugFrameworkServices { + private readonly IActiveConfiguredProjectsProvider _activeConfiguredProjectsProvider; + private readonly IUnconfiguredProjectCommonServices _commonProjectServices; + + [ImportingConstructor] + public ActiveDebugFrameworkServices(IActiveConfiguredProjectsProvider activeConfiguredProjectsProvider, IUnconfiguredProjectCommonServices commonProjectServices) + { + _activeConfiguredProjectsProvider = activeConfiguredProjectsProvider; + _commonProjectServices = commonProjectServices; + } + /// - /// Providers a wrapper around the what is considered the active debugging framework. + /// /// - [Export(typeof(IActiveDebugFrameworkServices))] - [AppliesTo(ProjectCapability.DotNet)] - internal class ActiveDebugFrameworkServices : IActiveDebugFrameworkServices + public async Task?> GetProjectFrameworksAsync() { - private readonly IActiveConfiguredProjectsProvider _activeConfiguredProjectsProvider; - private readonly IUnconfiguredProjectCommonServices _commonProjectServices; + // It is important that we return the frameworks in the order they are specified in the project to ensure the default is set + // correctly. + ConfigurationGeneral props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetConfigurationGeneralPropertiesAsync(); + + string? targetFrameworks = (string?)await props.TargetFrameworks.GetValueAsync(); - [ImportingConstructor] - public ActiveDebugFrameworkServices(IActiveConfiguredProjectsProvider activeConfiguredProjectsProvider, IUnconfiguredProjectCommonServices commonProjectServices) + if (Strings.IsNullOrWhiteSpace(targetFrameworks)) { - _activeConfiguredProjectsProvider = activeConfiguredProjectsProvider; - _commonProjectServices = commonProjectServices; + return null; } - /// - /// - /// - public async Task?> GetProjectFrameworksAsync() - { - // It is important that we return the frameworks in the order they are specified in the project to ensure the default is set - // correctly. - ConfigurationGeneral props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetConfigurationGeneralPropertiesAsync(); + return BuildUtilities.GetPropertyValues(targetFrameworks).ToList(); + } - string? targetFrameworks = (string?)await props.TargetFrameworks.GetValueAsync(); + /// + /// + /// + public async Task SetActiveDebuggingFrameworkPropertyAsync(string activeFramework) + { + ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); + await props.ActiveDebugFramework.SetValueAsync(activeFramework); + } - if (Strings.IsNullOrWhiteSpace(targetFrameworks)) - { - return null; - } + /// + /// + /// + public async Task GetActiveDebuggingFrameworkPropertyAsync() + { + ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); + string? activeValue = await props.ActiveDebugFramework.GetValueAsync() as string; + return activeValue; + } - return BuildUtilities.GetPropertyValues(targetFrameworks).ToList(); - } + /// + /// + /// + public async Task GetConfiguredProjectForActiveFrameworkAsync() + { +#pragma warning disable CS0618 // Type or member is obsolete + ImmutableDictionary? configProjects = await _activeConfiguredProjectsProvider.GetActiveConfiguredProjectsMapAsync(); +#pragma warning restore CS0618 // Type or member is obsolete - /// - /// - /// - public async Task SetActiveDebuggingFrameworkPropertyAsync(string activeFramework) + if (configProjects is null) { - ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); - await props.ActiveDebugFramework.SetValueAsync(activeFramework); + return null; } - /// - /// - /// - public async Task GetActiveDebuggingFrameworkPropertyAsync() + // If there is only one we are done + if (configProjects.Count == 1) { - ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); - string? activeValue = await props.ActiveDebugFramework.GetValueAsync() as string; - return activeValue; + return configProjects.First().Value; } - /// - /// - /// - public async Task GetConfiguredProjectForActiveFrameworkAsync() - { -#pragma warning disable CS0618 // Type or member is obsolete - ImmutableDictionary? configProjects = await _activeConfiguredProjectsProvider.GetActiveConfiguredProjectsMapAsync(); -#pragma warning restore CS0618 // Type or member is obsolete - - if (configProjects is null) - { - return null; - } - - // If there is only one we are done - if (configProjects.Count == 1) - { - return configProjects.First().Value; - } + string? activeFramework = await GetActiveDebuggingFrameworkPropertyAsync(); - string? activeFramework = await GetActiveDebuggingFrameworkPropertyAsync(); - - if (!Strings.IsNullOrWhiteSpace(activeFramework)) + if (!Strings.IsNullOrWhiteSpace(activeFramework)) + { + if (configProjects.TryGetValue(activeFramework, out ConfiguredProject? configuredProject)) { - if (configProjects.TryGetValue(activeFramework, out ConfiguredProject? configuredProject)) - { - return configuredProject; - } + return configuredProject; } + } - // We can't just select the first one. If activeFramework is not set we must pick the first one as defined by the - // targetFrameworks property. So we need the order as returned by GetProjectFrameworks() - List? frameworks = await GetProjectFrameworksAsync(); + // We can't just select the first one. If activeFramework is not set we must pick the first one as defined by the + // targetFrameworks property. So we need the order as returned by GetProjectFrameworks() + List? frameworks = await GetProjectFrameworksAsync(); - if (frameworks?.Count > 0) + if (frameworks?.Count > 0) + { + if (configProjects.TryGetValue(frameworks[0], out ConfiguredProject? configuredProject)) { - if (configProjects.TryGetValue(frameworks[0], out ConfiguredProject? configuredProject)) - { - return configuredProject; - } + return configuredProject; } - - // All that is left is to return the first one. - return configProjects.First().Value; } + + // All that is left is to return the first one. + return configProjects.First().Value; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileDebugTargetGenerator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileDebugTargetGenerator.cs index 0dd562b75e..3e0c0c849c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileDebugTargetGenerator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileDebugTargetGenerator.cs @@ -5,60 +5,59 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Provides the set of debug profiles to populate the debugger dropdown. The Property associated +/// with this is the ActiveDebugProfile which contains the currently selected profile, and the DebugProfiles which +/// is the name of the enumerator provider +/// +[ExportDynamicEnumValuesProvider("DebugProfileProvider")] +[AppliesTo(ProjectCapability.LaunchProfiles)] +[Export(typeof(IDynamicDebugTargetsGenerator))] +[ExportMetadata("Name", "DebugProfileProvider")] +internal class DebugProfileDebugTargetGenerator : ChainedProjectValueDataSourceBase>, IDynamicEnumValuesProvider, IDynamicDebugTargetsGenerator { - /// - /// Provides the set of debug profiles to populate the debugger dropdown. The Property associated - /// with this is the ActiveDebugProfile which contains the currently selected profile, and the DebugProfiles which - /// is the name of the enumerator provider - /// - [ExportDynamicEnumValuesProvider("DebugProfileProvider")] - [AppliesTo(ProjectCapability.LaunchProfiles)] - [Export(typeof(IDynamicDebugTargetsGenerator))] - [ExportMetadata("Name", "DebugProfileProvider")] - internal class DebugProfileDebugTargetGenerator : ChainedProjectValueDataSourceBase>, IDynamicEnumValuesProvider, IDynamicDebugTargetsGenerator - { - private readonly IVersionedLaunchSettingsProvider _launchSettingProvider; - private readonly IProjectThreadingService _projectThreadingService; + private readonly IVersionedLaunchSettingsProvider _launchSettingProvider; + private readonly IProjectThreadingService _projectThreadingService; - [ImportingConstructor] - public DebugProfileDebugTargetGenerator( - UnconfiguredProject project, - IVersionedLaunchSettingsProvider launchSettingProvider, - IProjectThreadingService threadingService) - : base(project, synchronousDisposal: false, registerDataSource: false) - { - _launchSettingProvider = launchSettingProvider; - _projectThreadingService = threadingService; - } + [ImportingConstructor] + public DebugProfileDebugTargetGenerator( + UnconfiguredProject project, + IVersionedLaunchSettingsProvider launchSettingProvider, + IProjectThreadingService threadingService) + : base(project, synchronousDisposal: false, registerDataSource: false) + { + _launchSettingProvider = launchSettingProvider; + _projectThreadingService = threadingService; + } - public Task GetProviderAsync(IList? options) - { - return Task.FromResult( - new DebugProfileEnumValuesGenerator(_launchSettingProvider, _projectThreadingService)); - } + public Task GetProviderAsync(IList? options) + { + return Task.FromResult( + new DebugProfileEnumValuesGenerator(_launchSettingProvider, _projectThreadingService)); + } - protected override IDisposable? LinkExternalInput(ITargetBlock>> targetBlock) - { - var transformBlock = DataflowBlockSlim.CreateTransformBlock, IProjectVersionedValue>>( - update => update.Derive(Transform)); + protected override IDisposable? LinkExternalInput(ITargetBlock>> targetBlock) + { + var transformBlock = DataflowBlockSlim.CreateTransformBlock, IProjectVersionedValue>>( + update => update.Derive(Transform)); - // IVersionedLaunchSettingsProvider implements "SourceBlock" in both ILaunchSettingsProvider and IProjectValueDataSource. Cast to the one we need. - IProjectValueDataSource launchSettingsSource = _launchSettingProvider; + // IVersionedLaunchSettingsProvider implements "SourceBlock" in both ILaunchSettingsProvider and IProjectValueDataSource. Cast to the one we need. + IProjectValueDataSource launchSettingsSource = _launchSettingProvider; - return new DisposableBag - { - launchSettingsSource.SourceBlock.LinkTo(transformBlock, linkOptions: DataflowOption.PropagateCompletion), + return new DisposableBag + { + launchSettingsSource.SourceBlock.LinkTo(transformBlock, linkOptions: DataflowOption.PropagateCompletion), - transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion), + transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion), - JoinUpstreamDataSources(_launchSettingProvider) - }; + JoinUpstreamDataSources(_launchSettingProvider) + }; - static IReadOnlyList Transform(ILaunchSettings launchSettings) - { - return DebugProfileEnumValuesGenerator.GetEnumeratorEnumValues(launchSettings); - } + static IReadOnlyList Transform(ILaunchSettings launchSettings) + { + return DebugProfileEnumValuesGenerator.GetEnumeratorEnumValues(launchSettings); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileEnumValuesGenerator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileEnumValuesGenerator.cs index dca7f7c5ca..df7017b83c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileEnumValuesGenerator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugProfileEnumValuesGenerator.cs @@ -5,74 +5,73 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Provides the IEnumValue's for the ActiveDebugProfile property. This is what is used to drive +/// the debug target selection dropdown. +/// +internal class DebugProfileEnumValuesGenerator : IDynamicEnumValuesGenerator { - /// - /// Provides the IEnumValue's for the ActiveDebugProfile property. This is what is used to drive - /// the debug target selection dropdown. - /// - internal class DebugProfileEnumValuesGenerator : IDynamicEnumValuesGenerator - { - private readonly AsyncLazy> _listedValues; + private readonly AsyncLazy> _listedValues; - internal DebugProfileEnumValuesGenerator( - ILaunchSettingsProvider profileProvider, - IProjectThreadingService threadingService) - { - Requires.NotNull(profileProvider); - Requires.NotNull(threadingService); + internal DebugProfileEnumValuesGenerator( + ILaunchSettingsProvider profileProvider, + IProjectThreadingService threadingService) + { + Requires.NotNull(profileProvider); + Requires.NotNull(threadingService); - _listedValues = new AsyncLazy>( - () => - { - ILaunchSettings? snapshot = profileProvider.CurrentSnapshot; + _listedValues = new AsyncLazy>( + () => + { + ILaunchSettings? snapshot = profileProvider.CurrentSnapshot; - ICollection values = snapshot is null - ? Array.Empty() - : GetEnumeratorEnumValues(snapshot); + ICollection values = snapshot is null + ? Array.Empty() + : GetEnumeratorEnumValues(snapshot); - return Task.FromResult(values); - }, - threadingService.JoinableTaskFactory); - } + return Task.FromResult(values); + }, + threadingService.JoinableTaskFactory); + } - public Task> GetListedValuesAsync() - { - return _listedValues.GetValueAsync(); - } + public Task> GetListedValuesAsync() + { + return _listedValues.GetValueAsync(); + } - public bool AllowCustomValues - { - get { return false; } - } + public bool AllowCustomValues + { + get { return false; } + } - public async Task TryCreateEnumValueAsync(string userSuppliedValue) - { - ICollection enumValues = await _listedValues.GetValueAsync(); + public async Task TryCreateEnumValueAsync(string userSuppliedValue) + { + ICollection enumValues = await _listedValues.GetValueAsync(); - return enumValues.FirstOrDefault(v => LaunchProfile.IsSameProfileName(v.Name, userSuppliedValue)); - } + return enumValues.FirstOrDefault(v => LaunchProfile.IsSameProfileName(v.Name, userSuppliedValue)); + } - internal static ImmutableArray GetEnumeratorEnumValues(ILaunchSettings launchSettings) - { - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: launchSettings.Profiles.Count); + internal static ImmutableArray GetEnumeratorEnumValues(ILaunchSettings launchSettings) + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: launchSettings.Profiles.Count); - builder.AddRange(launchSettings.Profiles.Select(ToEnumValue)); + builder.AddRange(launchSettings.Profiles.Select(ToEnumValue)); - return builder.MoveToImmutable(); + return builder.MoveToImmutable(); - static IEnumValue ToEnumValue(ILaunchProfile profile) - { - var enumValue = new EnumValue { Name = profile.Name, DisplayName = EscapeMnemonics(profile.Name) }; + static IEnumValue ToEnumValue(ILaunchProfile profile) + { + var enumValue = new EnumValue { Name = profile.Name, DisplayName = EscapeMnemonics(profile.Name) }; - return new PageEnumValue(enumValue); - } + return new PageEnumValue(enumValue); } + } - [return: NotNullIfNotNull(nameof(text))] - private static string? EscapeMnemonics(string? text) - { - return text?.Replace("&", "&&"); - } + [return: NotNullIfNotNull(nameof(text))] + private static string? EscapeMnemonics(string? text) + { + return text?.Replace("&", "&&"); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugTokenReplacer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugTokenReplacer.cs index cc648d9e0d..2d10fb4762 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugTokenReplacer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DebugTokenReplacer.cs @@ -3,58 +3,57 @@ using System.Text.RegularExpressions; using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +[Export(typeof(IDebugTokenReplacer))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal class DebugTokenReplacer : IDebugTokenReplacer { - [Export(typeof(IDebugTokenReplacer))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal class DebugTokenReplacer : IDebugTokenReplacer + [ImportingConstructor] + public DebugTokenReplacer(IEnvironmentHelper environmentHelper, IActiveDebugFrameworkServices activeDebugFrameworkService, IProjectAccessor projectAccessor) { - [ImportingConstructor] - public DebugTokenReplacer(IEnvironmentHelper environmentHelper, IActiveDebugFrameworkServices activeDebugFrameworkService, IProjectAccessor projectAccessor) - { - EnvironmentHelper = environmentHelper; - ActiveDebugFrameworkService = activeDebugFrameworkService; - ProjectAccessor = projectAccessor; - } + EnvironmentHelper = environmentHelper; + ActiveDebugFrameworkService = activeDebugFrameworkService; + ProjectAccessor = projectAccessor; + } - private IEnvironmentHelper EnvironmentHelper { get; } - private IActiveDebugFrameworkServices ActiveDebugFrameworkService { get; } - private IProjectAccessor ProjectAccessor { get; } + private IEnvironmentHelper EnvironmentHelper { get; } + private IActiveDebugFrameworkServices ActiveDebugFrameworkService { get; } + private IProjectAccessor ProjectAccessor { get; } - // Regular expression string to extract $(sometoken) elements from a string - private static readonly Regex s_matchTokenRegex = new(@"\$\((?[^\)]+)\)", RegexOptions.IgnoreCase); + // Regular expression string to extract $(sometoken) elements from a string + private static readonly Regex s_matchTokenRegex = new(@"\$\((?[^\)]+)\)", RegexOptions.IgnoreCase); - public async Task ReplaceTokensInProfileAsync(ILaunchProfile profile) - { - return await LaunchProfile.ReplaceTokensAsync( - profile, - str => ReplaceTokensInStringAsync(str, expandEnvironmentVars: true)); - } + public async Task ReplaceTokensInProfileAsync(ILaunchProfile profile) + { + return await LaunchProfile.ReplaceTokensAsync( + profile, + str => ReplaceTokensInStringAsync(str, expandEnvironmentVars: true)); + } - public Task ReplaceTokensInStringAsync(string rawString, bool expandEnvironmentVars) - { - if (string.IsNullOrWhiteSpace(rawString)) - return Task.FromResult(rawString); + public Task ReplaceTokensInStringAsync(string rawString, bool expandEnvironmentVars) + { + if (string.IsNullOrWhiteSpace(rawString)) + return Task.FromResult(rawString); - string expandedString = expandEnvironmentVars - ? EnvironmentHelper.ExpandEnvironmentVariables(rawString) - : rawString; + string expandedString = expandEnvironmentVars + ? EnvironmentHelper.ExpandEnvironmentVariables(rawString) + : rawString; - if (!s_matchTokenRegex.IsMatch(expandedString)) - return Task.FromResult(expandedString); + if (!s_matchTokenRegex.IsMatch(expandedString)) + return Task.FromResult(expandedString); - return ReplaceMSBuildTokensAsync(); + return ReplaceMSBuildTokensAsync(); - async Task ReplaceMSBuildTokensAsync() - { - ConfiguredProject? configuredProject = await ActiveDebugFrameworkService.GetConfiguredProjectForActiveFrameworkAsync(); + async Task ReplaceMSBuildTokensAsync() + { + ConfiguredProject? configuredProject = await ActiveDebugFrameworkService.GetConfiguredProjectForActiveFrameworkAsync(); - Assumes.NotNull(configuredProject); + Assumes.NotNull(configuredProject); - return await ProjectAccessor.OpenProjectForReadAsync( - configuredProject, - project => s_matchTokenRegex.Replace(expandedString, m => project.ExpandString(m.Value))); - } + return await ProjectAccessor.OpenProjectForReadAsync( + configuredProject, + project => s_matchTokenRegex.Replace(expandedString, m => project.ExpandString(m.Value))); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DefaultLaunchProfileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DefaultLaunchProfileProvider.cs index e6fddc29ea..94c834563c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DefaultLaunchProfileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/DefaultLaunchProfileProvider.cs @@ -2,26 +2,25 @@ using ExportOrder = Microsoft.VisualStudio.ProjectSystem.OrderAttribute; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +[Export(typeof(IDefaultLaunchProfileProvider))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +[ExportOrder(Order.Default)] +internal class DefaultLaunchProfileProvider : IDefaultLaunchProfileProvider { - [Export(typeof(IDefaultLaunchProfileProvider))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - [ExportOrder(Order.Default)] - internal class DefaultLaunchProfileProvider : IDefaultLaunchProfileProvider - { - private readonly UnconfiguredProject _project; + private readonly UnconfiguredProject _project; - [ImportingConstructor] - public DefaultLaunchProfileProvider(UnconfiguredProject project) - { - _project = project; - } + [ImportingConstructor] + public DefaultLaunchProfileProvider(UnconfiguredProject project) + { + _project = project; + } - public ILaunchProfile? CreateDefaultProfile() - { - return new LaunchProfile( - name: Path.GetFileNameWithoutExtension(_project.FullPath), - commandName: LaunchSettingsProvider.RunProjectCommandName); - } + public ILaunchProfile? CreateDefaultProfile() + { + return new LaunchProfile( + name: Path.GetFileNameWithoutExtension(_project.FullPath), + commandName: LaunchSettingsProvider.RunProjectCommandName); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IActiveDebugFrameworkServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IActiveDebugFrameworkServices.cs index 9845f5d94e..cd450463bc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IActiveDebugFrameworkServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IActiveDebugFrameworkServices.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Wrapper around the active debug framework to provide a single implementation of what is considered the active framework. If there is +/// only one framework. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface IActiveDebugFrameworkServices { /// - /// Wrapper around the active debug framework to provide a single implementation of what is considered the active framework. If there is - /// only one framework. + /// Returns the set of frameworks in the order defined in msbuild. If not multi-targeting it returns null. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface IActiveDebugFrameworkServices - { - /// - /// Returns the set of frameworks in the order defined in msbuild. If not multi-targeting it returns null. - /// - Task?> GetProjectFrameworksAsync(); + Task?> GetProjectFrameworksAsync(); - /// - /// Sets the value of the active debugging target framework property. - /// - Task SetActiveDebuggingFrameworkPropertyAsync(string activeFramework); + /// + /// Sets the value of the active debugging target framework property. + /// + Task SetActiveDebuggingFrameworkPropertyAsync(string activeFramework); - /// - /// Returns the value of the property, or empty string/null if the property has never been set. - /// - Task GetActiveDebuggingFrameworkPropertyAsync(); + /// + /// Returns the value of the property, or empty string/null if the property has never been set. + /// + Task GetActiveDebuggingFrameworkPropertyAsync(); - /// - /// Returns the configured project which represents the active framework. This is valid whether multi-targeting or not. - /// - Task GetConfiguredProjectForActiveFrameworkAsync(); - } + /// + /// Returns the configured project which represents the active framework. This is valid whether multi-targeting or not. + /// + Task GetConfiguredProjectForActiveFrameworkAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDebugTokenReplacer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDebugTokenReplacer.cs index 3666e55455..8233d45205 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDebugTokenReplacer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDebugTokenReplacer.cs @@ -1,38 +1,37 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Replaces tokens of form %VAR% with their environment variable value, and of form +/// $(MSBuildExpression) with the evaluated expression via the active configured project. +/// +/// +/// Intended for token substitution in data. Custom launch profile +/// providers may import this component to perform such substitutions. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface IDebugTokenReplacer { /// - /// Replaces tokens of form %VAR% with their environment variable value, and of form - /// $(MSBuildExpression) with the evaluated expression via the active configured project. + /// Returns a copy of where all tokens have been replaced. + /// Tokens can consist of environment variables (%VAR%), or MSBuild expressions ($(Property)). /// /// - /// Intended for token substitution in data. Custom launch profile - /// providers may import this component to perform such substitutions. + /// Environment variables are replaced first, followed by MSBuild properties. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface IDebugTokenReplacer - { - /// - /// Returns a copy of where all tokens have been replaced. - /// Tokens can consist of environment variables (%VAR%), or MSBuild expressions ($(Property)). - /// - /// - /// Environment variables are replaced first, followed by MSBuild properties. - /// - Task ReplaceTokensInProfileAsync(ILaunchProfile profile); + Task ReplaceTokensInProfileAsync(ILaunchProfile profile); - /// - /// Replaces the MSBuild expressions and (optionally) environment variables in . - /// - /// - /// - /// If is , environment variables are substituted. Environment variables are substituted before MSBuild expressions. - /// - /// - /// If is or empty, it is returned as is. - /// - /// - Task ReplaceTokensInStringAsync(string rawString, bool expandEnvironmentVars); - } + /// + /// Replaces the MSBuild expressions and (optionally) environment variables in . + /// + /// + /// + /// If is , environment variables are substituted. Environment variables are substituted before MSBuild expressions. + /// + /// + /// If is or empty, it is returned as is. + /// + /// + Task ReplaceTokensInStringAsync(string rawString, bool expandEnvironmentVars); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDefaultLaunchProfileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDefaultLaunchProfileProvider.cs index ef7b3bc354..62056b8f80 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDefaultLaunchProfileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IDefaultLaunchProfileProvider.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Provides a default to be displayed when no profiles are provided. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IDefaultLaunchProfileProvider { /// - /// Provides a default to be displayed when no profiles are provided. + /// Gets the default . Return null to remove the default profile. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IDefaultLaunchProfileProvider - { - /// - /// Gets the default . Return null to remove the default profile. - /// - ILaunchProfile? CreateDefaultProfile(); - } + ILaunchProfile? CreateDefaultProfile(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IJsonSection.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IJsonSection.cs index 3061f10dad..abb2f9dcaf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IJsonSection.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IJsonSection.cs @@ -2,17 +2,16 @@ using System.ComponentModel; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Used to get to the JsonString exported attribute by importers of . +/// +public interface IJsonSection : IOrderPrecedenceMetadataView { - /// - /// Used to get to the JsonString exported attribute by importers of . - /// - public interface IJsonSection : IOrderPrecedenceMetadataView - { - [DefaultValue(null)] - string JsonSection { get; } + [DefaultValue(null)] + string JsonSection { get; } - [DefaultValue(null)] - Type SerializationType { get; } - } + [DefaultValue(null)] + Type SerializationType { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile.cs index 4bd9ac3b06..2766e5d69c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Models immutable data about a launch profile. +/// +public interface ILaunchProfile { - /// - /// Models immutable data about a launch profile. - /// - public interface ILaunchProfile - { - string? Name { get; } - string? CommandName { get; } - string? ExecutablePath { get; } - string? CommandLineArgs { get; } - string? WorkingDirectory { get; } - bool LaunchBrowser { get; } - string? LaunchUrl { get; } - ImmutableDictionary? EnvironmentVariables { get; } - ImmutableDictionary? OtherSettings { get; } - } + string? Name { get; } + string? CommandName { get; } + string? ExecutablePath { get; } + string? CommandLineArgs { get; } + string? WorkingDirectory { get; } + bool LaunchBrowser { get; } + string? LaunchUrl { get; } + ImmutableDictionary? EnvironmentVariables { get; } + ImmutableDictionary? OtherSettings { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile2.cs index 1a61a753e2..03ef5cdbb5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchProfile2.cs @@ -1,14 +1,13 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Extends the default profile with properties for environment variables and settings +/// that preserve the order of these collections. +/// +public interface ILaunchProfile2 : ILaunchProfile { - /// - /// Extends the default profile with properties for environment variables and settings - /// that preserve the order of these collections. - /// - public interface ILaunchProfile2 : ILaunchProfile - { - new ImmutableArray<(string Key, string Value)> EnvironmentVariables { get; } - new ImmutableArray<(string Key, object Value)> OtherSettings { get; } - } + new ImmutableArray<(string Key, string Value)> EnvironmentVariables { get; } + new ImmutableArray<(string Key, object Value)> OtherSettings { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettings.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettings.cs index 97581824b3..aef5bd7d9b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettings.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettings.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Interface definition for an immutable launch settings snapshot. +/// +public interface ILaunchSettings { + ILaunchProfile? ActiveProfile { get; } + /// - /// Interface definition for an immutable launch settings snapshot. + /// Access to the current set of launch profiles. /// - public interface ILaunchSettings - { - ILaunchProfile? ActiveProfile { get; } + ImmutableList Profiles { get; } - /// - /// Access to the current set of launch profiles. - /// - ImmutableList Profiles { get; } - - /// - /// Provides access to custom global launch settings data. The returned value depends - /// on the section being retrieved. The settingsName matches the section in the - /// settings file. - /// - object? GetGlobalSetting(string settingsName); + /// + /// Provides access to custom global launch settings data. The returned value depends + /// on the section being retrieved. The settingsName matches the section in the + /// settings file. + /// + object? GetGlobalSetting(string settingsName); - /// - /// Provides access to all the global settings. - /// - ImmutableDictionary GlobalSettings { get; } - } + /// + /// Provides access to all the global settings. + /// + ImmutableDictionary GlobalSettings { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider.cs index 02cf0425d3..973cbb1752 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider.cs @@ -2,83 +2,82 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Interface definition for the LaunchSettingsProvider. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface ILaunchSettingsProvider { /// - /// Interface definition for the LaunchSettingsProvider. + /// Link to this source block to be notified when the snapshot is changed. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface ILaunchSettingsProvider - { - /// - /// Link to this source block to be notified when the snapshot is changed. - /// - /// - /// If the provided by this block are going to feed - /// into another data flow block, strongly consider using - /// instead as that provides a joinable variant. - /// - IReceivableSourceBlock SourceBlock { get; } + /// + /// If the provided by this block are going to feed + /// into another data flow block, strongly consider using + /// instead as that provides a joinable variant. + /// + IReceivableSourceBlock SourceBlock { get; } - /// - /// Gets the current launch settings snapshot, or if it is not yet available. - /// - ILaunchSettings? CurrentSnapshot { get; } + /// + /// Gets the current launch settings snapshot, or if it is not yet available. + /// + ILaunchSettings? CurrentSnapshot { get; } - [Obsolete("Use ILaunchSettingsProvider2.GetLaunchSettingsFilePathAsync instead.")] - string LaunchSettingsFile { get; } + [Obsolete("Use ILaunchSettingsProvider2.GetLaunchSettingsFilePathAsync instead.")] + string LaunchSettingsFile { get; } - /// - /// Returns the active profile. Equivalent to CurrentSnapshot?.ActiveProfile. - /// - ILaunchProfile? ActiveProfile { get; } + /// + /// Returns the active profile. Equivalent to CurrentSnapshot?.ActiveProfile. + /// + ILaunchProfile? ActiveProfile { get; } - /// - /// Replaces the current set of profiles with the contents of . - /// If changes were made, the file will be checked out and updated. Note that the - /// active profile in is ignored; to change the active - /// profile use instead. - /// - Task UpdateAndSaveSettingsAsync(ILaunchSettings profiles); + /// + /// Replaces the current set of profiles with the contents of . + /// If changes were made, the file will be checked out and updated. Note that the + /// active profile in is ignored; to change the active + /// profile use instead. + /// + Task UpdateAndSaveSettingsAsync(ILaunchSettings profiles); - /// - /// Blocks until at least one snapshot has been generated. - /// - /// The timeout in milliseconds. - /// - /// The current snapshot, or if the - /// timeout expires before the snapshot become available. - /// - Task WaitForFirstSnapshot(int timeout); + /// + /// Blocks until at least one snapshot has been generated. + /// + /// The timeout in milliseconds. + /// + /// The current snapshot, or if the + /// timeout expires before the snapshot become available. + /// + Task WaitForFirstSnapshot(int timeout); - /// - /// Adds the given profile to the list and saves to disk. If a profile with the same - /// name exists (case sensitive), it will be replaced with the new profile. If is - /// the profile will be the first one in the list. This is useful since quite often callers want - /// their just added profile to be listed first in the start menu. - /// - Task AddOrUpdateProfileAsync(ILaunchProfile profile, bool addToFront); + /// + /// Adds the given profile to the list and saves to disk. If a profile with the same + /// name exists (case sensitive), it will be replaced with the new profile. If is + /// the profile will be the first one in the list. This is useful since quite often callers want + /// their just added profile to be listed first in the start menu. + /// + Task AddOrUpdateProfileAsync(ILaunchProfile profile, bool addToFront); - /// - /// Removes the specified profile from the list and saves to disk. - /// - Task RemoveProfileAsync(string profileName); + /// + /// Removes the specified profile from the list and saves to disk. + /// + Task RemoveProfileAsync(string profileName); - /// - /// Adds or updates the global settings represented by . Saves the - /// updated settings to disk. Note that the settings object must be serializable. - /// - Task AddOrUpdateGlobalSettingAsync(string settingName, object settingContent); + /// + /// Adds or updates the global settings represented by . Saves the + /// updated settings to disk. Note that the settings object must be serializable. + /// + Task AddOrUpdateGlobalSettingAsync(string settingName, object settingContent); - /// - /// Removes the specified global setting and saves the settings to disk. - /// - Task RemoveGlobalSettingAsync(string settingName); + /// + /// Removes the specified global setting and saves the settings to disk. + /// + Task RemoveGlobalSettingAsync(string settingName); - /// - /// Sets the active profile. This just sets the property; it does not validate that the setting matches an - /// existing profile. - /// - Task SetActiveProfileAsync(string profileName); - } + /// + /// Sets the active profile. This just sets the property; it does not validate that the setting matches an + /// existing profile. + /// + Task SetActiveProfileAsync(string profileName); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider2.cs index 120fe86e83..10b1802185 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider2.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Provides an implementation of with an +/// additional method for retrieving +/// the launch settings file. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface ILaunchSettingsProvider2 : ILaunchSettingsProvider { /// - /// Provides an implementation of with an - /// additional method for retrieving - /// the launch settings file. + /// Gets the full path to the launch settings file, typically located under + /// "Properties\launchSettings.json" or "My Project\launchSettings.json" of + /// the project directory. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface ILaunchSettingsProvider2 : ILaunchSettingsProvider - { - /// - /// Gets the full path to the launch settings file, typically located under - /// "Properties\launchSettings.json" or "My Project\launchSettings.json" of - /// the project directory. - /// - Task GetLaunchSettingsFilePathAsync(); - } + Task GetLaunchSettingsFilePathAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider3.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider3.cs index fd1399e8b0..31cf107acd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider3.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsProvider3.cs @@ -1,49 +1,48 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Provides an implementation of with +/// additional methods for atomically updating a launch profile or global setting. +/// +/// +/// +/// The existing method +/// does not work well in situations where multiple update operations may run +/// concurrently because the retrieval of the current set of values is separated from +/// the later update. If operations A and B each retrieve the current profile, make +/// their changes, and then apply them there is a very good chance that A's changes +/// will overwrite B's or the other way around. The new method introduced here makes +/// retrieving and updating a profile an single operation. +/// +/// +/// The method has +/// similar problems. +/// +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface ILaunchSettingsProvider3 : ILaunchSettingsProvider2 { /// - /// Provides an implementation of with - /// additional methods for atomically updating a launch profile or global setting. + /// Supports the retrieval and update of a given launch profile as a single + /// operation. When is called it is given an + /// with the current state of the + /// profile. It will update the profile as + /// appropriate and when it is done the profile is applied to the settings. /// - /// - /// - /// The existing method - /// does not work well in situations where multiple update operations may run - /// concurrently because the retrieval of the current set of values is separated from - /// the later update. If operations A and B each retrieve the current profile, make - /// their changes, and then apply them there is a very good chance that A's changes - /// will overwrite B's or the other way around. The new method introduced here makes - /// retrieving and updating a profile an single operation. - /// - /// - /// The method has - /// similar problems. - /// - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface ILaunchSettingsProvider3 : ILaunchSettingsProvider2 - { - /// - /// Supports the retrieval and update of a given launch profile as a single - /// operation. When is called it is given an - /// with the current state of the - /// profile. It will update the profile as - /// appropriate and when it is done the profile is applied to the settings. - /// - /// - /// if was found and - /// executed; if was not found. - /// - Task TryUpdateProfileAsync(string profileName, Action updateAction); + /// + /// if was found and + /// executed; if was not found. + /// + Task TryUpdateProfileAsync(string profileName, Action updateAction); - /// - /// Supports the retrieval and update of the global settings as a single operation. - /// When is called it is given the current global - /// settings and is expected to return a set of the values that have changed. If the - /// value for a given key is the corresponding global - /// setting will be removed, otherwise the setting will be updated. - /// - Task UpdateGlobalSettingsAsync(Func, ImmutableDictionary> updateFunction); - } + /// + /// Supports the retrieval and update of the global settings as a single operation. + /// When is called it is given the current global + /// settings and is expected to return a set of the values that have changed. If the + /// value for a given key is the corresponding global + /// setting will be removed, otherwise the setting will be updated. + /// + Task UpdateGlobalSettingsAsync(Func, ImmutableDictionary> updateFunction); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsSerializationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsSerializationProvider.cs index 231f21df2a..e49adb4f24 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsSerializationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsSerializationProvider.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Interface implemented by providers of custom data. When the launch settings file is read the top level token matching the attribute +/// "JsonSection" is invoked to deserialize the json to an object. The export needs the attribute +/// [ExportMetadata("JsonSection", "nameofjsonsection")] +/// [ExportMetadata("SerializationProvider", typeof(objectToSerialize))] +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +public interface ILaunchSettingsSerializationProvider { - /// - /// Interface implemented by providers of custom data. When the launch settings file is read the top level token matching the attribute - /// "JsonSection" is invoked to deserialize the json to an object. The export needs the attribute - /// [ExportMetadata("JsonSection", "nameofjsonsection")] - /// [ExportMetadata("SerializationProvider", typeof(objectToSerialize))] - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - public interface ILaunchSettingsSerializationProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsUIProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsUIProvider.cs index 805a6bbe8a..c6331b02a2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsUIProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/ILaunchSettingsUIProvider.cs @@ -4,57 +4,56 @@ using System.Windows.Controls; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Allows a launch settings provider to modify and extend the debug property page. +/// +/// +/// Implementations of this interface each contribute an entry to the drop-down list of +/// launch profiles in the "Debug" project property page. +/// +[Obsolete("This interface is obsolete and no longer used for the launch profiles UI. See https://github.com/dotnet/project-system/blob/main/docs/repo/property-pages/how-to-add-a-new-launch-profile-kind.md for more information.")] +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface ILaunchSettingsUIProvider { /// - /// Allows a launch settings provider to modify and extend the debug property page. + /// The value of the commandName property written to the launchSettings.json file. + /// + string CommandName { get; } + + /// + /// The user-friendly name of this launch provider, to show in the drop down. + /// + string FriendlyName { get; } + + /// + /// Allows a launch provider to suppress default properties from the UI. /// /// - /// Implementations of this interface each contribute an entry to the drop-down list of - /// launch profiles in the "Debug" project property page. + /// Currently supports the following default properties: + /// + /// Executable + /// Arguments + /// LaunchUrl + /// EnvironmentVariables + /// WorkingDirectory + /// + /// Names should be treated case-insensitively. Constants for these names exist in . /// - [Obsolete("This interface is obsolete and no longer used for the launch profiles UI. See https://github.com/dotnet/project-system/blob/main/docs/repo/property-pages/how-to-add-a-new-launch-profile-kind.md for more information.")] - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface ILaunchSettingsUIProvider - { - /// - /// The value of the commandName property written to the launchSettings.json file. - /// - string CommandName { get; } - - /// - /// The user-friendly name of this launch provider, to show in the drop down. - /// - string FriendlyName { get; } - - /// - /// Allows a launch provider to suppress default properties from the UI. - /// - /// - /// Currently supports the following default properties: - /// - /// Executable - /// Arguments - /// LaunchUrl - /// EnvironmentVariables - /// WorkingDirectory - /// - /// Names should be treated case-insensitively. Constants for these names exist in . - /// - bool ShouldEnableProperty(string propertyName); - - /// - /// Provides an optional UI control for this launch provider to be displayed below other controls on the dialog. - /// May be if the launch provider does not have any dedicated UI. - /// - UserControl? CustomUI { get; } - - /// - /// Called when the selected profile changes to a profile which matches this command. - /// - /// The page's current values. - void ProfileSelected(IWritableLaunchSettings curSettings); - } + bool ShouldEnableProperty(string propertyName); + + /// + /// Provides an optional UI control for this launch provider to be displayed below other controls on the dialog. + /// May be if the launch provider does not have any dedicated UI. + /// + UserControl? CustomUI { get; } + + /// + /// Called when the selected profile changes to a profile which matches this command. + /// + /// The page's current values. + void ProfileSelected(IWritableLaunchSettings curSettings); } #endif diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IPersistOption.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IPersistOption.cs index 19bce4b332..bc194f2d55 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IPersistOption.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IPersistOption.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Extends to support in-memory (not persisted) profiles. +/// +public interface IPersistOption { - /// - /// Extends to support in-memory (not persisted) profiles. - /// - public interface IPersistOption - { - bool DoNotPersist { get; } - } + bool DoNotPersist { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteAuthenticationProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteAuthenticationProvider.cs index 3af986f2a8..c731ab2398 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteAuthenticationProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteAuthenticationProvider.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface IRemoteAuthenticationProvider { - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface IRemoteAuthenticationProvider - { - /// - /// Name displayed in the App Designer. Should be localized - /// - string DisplayName { get; } - /// - /// Name that is serialized to the launchSettings.json file. Shouldn't be localized - /// - string Name { get; } - /// - /// Guid passed to the debugger - /// - Guid PortSupplierGuid { get; } - /// - /// Guid used for to specify and read the authentication mode for the Remote Discovery Dialog - /// - Guid AuthenticationModeGuid { get; } - /// - /// Allows the authentication provider to influence the Remote Discovery Dialog - /// - uint AdditionalRemoteDiscoveryDialogFlags { get; } - } + /// + /// Name displayed in the App Designer. Should be localized + /// + string DisplayName { get; } + /// + /// Name that is serialized to the launchSettings.json file. Shouldn't be localized + /// + string Name { get; } + /// + /// Guid passed to the debugger + /// + Guid PortSupplierGuid { get; } + /// + /// Guid used for to specify and read the authentication mode for the Remote Discovery Dialog + /// + Guid AuthenticationModeGuid { get; } + /// + /// Allows the authentication provider to influence the Remote Discovery Dialog + /// + uint AdditionalRemoteDiscoveryDialogFlags { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteDebuggerAuthenticationService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteDebuggerAuthenticationService.cs index b394c32ec0..9ccf244d14 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteDebuggerAuthenticationService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IRemoteDebuggerAuthenticationService.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] +internal interface IRemoteDebuggerAuthenticationService { - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private)] - internal interface IRemoteDebuggerAuthenticationService - { - IEnumerable GetRemoteAuthenticationModes(); - IRemoteAuthenticationProvider? FindProviderForAuthenticationMode(string remoteAuthenticationMode); - bool ShowRemoteDiscoveryDialog(ref string remoteDebugMachine, ref IRemoteAuthenticationProvider? remoteAuthenticationProvider); - } + IEnumerable GetRemoteAuthenticationModes(); + IRemoteAuthenticationProvider? FindProviderForAuthenticationMode(string remoteAuthenticationMode); + bool ShowRemoteDiscoveryDialog(ref string remoteDebugMachine, ref IRemoteAuthenticationProvider? remoteAuthenticationProvider); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IStartupProjectHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IStartupProjectHelper.cs index 1426bfb214..6735da7ffa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IStartupProjectHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IStartupProjectHelper.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Provides a set of helper methods for interacting with designated start up projects. +/// +internal interface IStartupProjectHelper { /// - /// Provides a set of helper methods for interacting with designated start up projects. + /// Provides a mechanism to get an export from DotNet projects designated as startup projects. The is + /// used to refine the projects that are considered. /// - internal interface IStartupProjectHelper - { - /// - /// Provides a mechanism to get an export from DotNet projects designated as startup projects. The is - /// used to refine the projects that are considered. - /// - ImmutableArray GetExportFromDotNetStartupProjects(string capabilityMatch) where T : class; + ImmutableArray GetExportFromDotNetStartupProjects(string capabilityMatch) where T : class; - /// - /// Retrieves the full paths to the designated startup projects. - /// - ImmutableArray GetFullPathsOfStartupProjects(); - } + /// + /// Retrieves the full paths to the designated startup projects. + /// + ImmutableArray GetFullPathsOfStartupProjects(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettings.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettings.cs index 7604d699bc..54e838bbfd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettings.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettings.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Exposes the version number of an . +/// +internal interface IVersionedLaunchSettings { - /// - /// Exposes the version number of an . - /// - internal interface IVersionedLaunchSettings - { - long Version { get; } - } + long Version { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettingsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettingsProvider.cs index 709207dabf..2c9ca13f84 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettingsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IVersionedLaunchSettingsProvider.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// An update of that provides versioned data +/// and is joinable. Ideally, everywhere +/// provides data that feeds into another data flow block we should use this instead, +/// as joinable data flow blocks can coordinate their work in such a way as to avoid +/// deadlocks. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IVersionedLaunchSettingsProvider : IProjectValueDataSource, ILaunchSettingsProvider3 { - /// - /// An update of that provides versioned data - /// and is joinable. Ideally, everywhere - /// provides data that feeds into another data flow block we should use this instead, - /// as joinable data flow blocks can coordinate their work in such a way as to avoid - /// deadlocks. - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IVersionedLaunchSettingsProvider : IProjectValueDataSource, ILaunchSettingsProvider3 - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchProfile.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchProfile.cs index 029952b01f..fea02ddc7b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchProfile.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchProfile.cs @@ -1,25 +1,24 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Interface definition for a writable launch profile +/// +public interface IWritableLaunchProfile { + string? Name { get; set; } + string? CommandName { get; set; } + string? ExecutablePath { get; set; } + string? CommandLineArgs { get; set; } + string? WorkingDirectory { get; set; } + bool LaunchBrowser { get; set; } + string? LaunchUrl { get; set; } + Dictionary EnvironmentVariables { get; } + Dictionary OtherSettings { get; } + /// - /// Interface definition for a writable launch profile + /// Convert back to the immutable form. /// - public interface IWritableLaunchProfile - { - string? Name { get; set; } - string? CommandName { get; set; } - string? ExecutablePath { get; set; } - string? CommandLineArgs { get; set; } - string? WorkingDirectory { get; set; } - bool LaunchBrowser { get; set; } - string? LaunchUrl { get; set; } - Dictionary EnvironmentVariables { get; } - Dictionary OtherSettings { get; } - - /// - /// Convert back to the immutable form. - /// - ILaunchProfile ToLaunchProfile(); - } + ILaunchProfile ToLaunchProfile(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchSettings.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchSettings.cs index 6fc5647a44..3f5704b86f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchSettings.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritableLaunchSettings.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Interface definition for a modifiable launch settings +/// +public interface IWritableLaunchSettings { - /// - /// Interface definition for a modifiable launch settings - /// - public interface IWritableLaunchSettings - { - IWritableLaunchProfile? ActiveProfile { get; set; } + IWritableLaunchProfile? ActiveProfile { get; set; } - List Profiles { get; } + List Profiles { get; } - Dictionary GlobalSettings { get; } + Dictionary GlobalSettings { get; } - /// - /// Converts back to the immutable form. - /// - ILaunchSettings ToLaunchSettings(); - } + /// + /// Converts back to the immutable form. + /// + ILaunchSettings ToLaunchSettings(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritablePersistOption.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritablePersistOption.cs index 18ac7fa087..7142149b4f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritablePersistOption.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/IWritablePersistOption.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Extends IWritableLaunchProfile to handle in-memory only profiles +/// +public interface IWritablePersistOption : IPersistOption { - /// - /// Extends IWritableLaunchProfile to handle in-memory only profiles - /// - public interface IWritablePersistOption : IPersistOption - { - new bool DoNotPersist { get; set; } - } + new bool DoNotPersist { get; set; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfile.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfile.cs index 174c059fdb..958c53fa25 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfile.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfile.cs @@ -2,186 +2,185 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Represents one launch profile read from the launchSettings file. +/// +internal class LaunchProfile : ILaunchProfile2, IPersistOption { + public static LaunchProfile Clone(ILaunchProfile profile) + { + // LaunchProfile is immutable and doesn't need to be cloned. + if (profile is LaunchProfile lp) + { + return lp; + } + + // Unknown implementation. Make a defensive copy to a new immutable instance. + return new LaunchProfile( + name: profile.Name, + executablePath: profile.ExecutablePath, + commandName: profile.CommandName, + commandLineArgs: profile.CommandLineArgs, + workingDirectory: profile.WorkingDirectory, + launchBrowser: profile.LaunchBrowser, + launchUrl: profile.LaunchUrl, + environmentVariables: profile.FlattenEnvironmentVariables(), + otherSettings: profile.FlattenOtherSettings(), + doNotPersist: profile.IsInMemoryObject()); + } + /// - /// Represents one launch profile read from the launchSettings file. + /// Creates a copy of in which tokens are replaced via . /// - internal class LaunchProfile : ILaunchProfile2, IPersistOption + /// + /// Intended to replace tokens such as environment variables and MSBuild properties. + /// + /// The source profile to copy from. + /// A function that performs token substitution. + /// A profile with tokens substituted. + internal static async Task ReplaceTokensAsync(ILaunchProfile profile, Func> replaceAsync) { - public static LaunchProfile Clone(ILaunchProfile profile) + return new( + name: profile.Name, + commandName: profile.CommandName, + executablePath: await ReplaceOrNullAsync(profile.ExecutablePath), + commandLineArgs: await ReplaceOrNullAsync(profile.CommandLineArgs), + workingDirectory: await ReplaceOrNullAsync(profile.WorkingDirectory), + launchBrowser: profile.LaunchBrowser, + launchUrl: await ReplaceOrNullAsync(profile.LaunchUrl), + doNotPersist: profile.IsInMemoryObject(), + environmentVariables: await GetEnvironmentVariablesAsync(), + otherSettings: await GetOtherSettingsAsync()); + + Task ReplaceOrNullAsync(string? s) { - // LaunchProfile is immutable and doesn't need to be cloned. - if (profile is LaunchProfile lp) + if (Strings.IsNullOrWhiteSpace(s)) { - return lp; + return TaskResult.Null(); } - // Unknown implementation. Make a defensive copy to a new immutable instance. - return new LaunchProfile( - name: profile.Name, - executablePath: profile.ExecutablePath, - commandName: profile.CommandName, - commandLineArgs: profile.CommandLineArgs, - workingDirectory: profile.WorkingDirectory, - launchBrowser: profile.LaunchBrowser, - launchUrl: profile.LaunchUrl, - environmentVariables: profile.FlattenEnvironmentVariables(), - otherSettings: profile.FlattenOtherSettings(), - doNotPersist: profile.IsInMemoryObject()); + return replaceAsync(s)!; } - /// - /// Creates a copy of in which tokens are replaced via . - /// - /// - /// Intended to replace tokens such as environment variables and MSBuild properties. - /// - /// The source profile to copy from. - /// A function that performs token substitution. - /// A profile with tokens substituted. - internal static async Task ReplaceTokensAsync(ILaunchProfile profile, Func> replaceAsync) + Task> GetEnvironmentVariablesAsync() { - return new( - name: profile.Name, - commandName: profile.CommandName, - executablePath: await ReplaceOrNullAsync(profile.ExecutablePath), - commandLineArgs: await ReplaceOrNullAsync(profile.CommandLineArgs), - workingDirectory: await ReplaceOrNullAsync(profile.WorkingDirectory), - launchBrowser: profile.LaunchBrowser, - launchUrl: await ReplaceOrNullAsync(profile.LaunchUrl), - doNotPersist: profile.IsInMemoryObject(), - environmentVariables: await GetEnvironmentVariablesAsync(), - otherSettings: await GetOtherSettingsAsync()); - - Task ReplaceOrNullAsync(string? s) + return profile switch { - if (Strings.IsNullOrWhiteSpace(s)) - { - return TaskResult.Null(); - } - - return replaceAsync(s)!; - } + ILaunchProfile2 profile2 => ReplaceValuesAsync(profile2.EnvironmentVariables, replaceAsync), + _ => ReplaceValuesAsync(profile.FlattenEnvironmentVariables(), replaceAsync) + }; + } - Task> GetEnvironmentVariablesAsync() + Task> GetOtherSettingsAsync() + { + return profile switch { - return profile switch - { - ILaunchProfile2 profile2 => ReplaceValuesAsync(profile2.EnvironmentVariables, replaceAsync), - _ => ReplaceValuesAsync(profile.FlattenEnvironmentVariables(), replaceAsync) - }; - } + ILaunchProfile2 profile2 => ReplaceValuesAsync(profile2.OtherSettings, ReplaceIfStringAsync), + _ => ReplaceValuesAsync(profile.FlattenOtherSettings(), ReplaceIfStringAsync) + }; - Task> GetOtherSettingsAsync() + async Task ReplaceIfStringAsync(object o) { - return profile switch + return o switch { - ILaunchProfile2 profile2 => ReplaceValuesAsync(profile2.OtherSettings, ReplaceIfStringAsync), - _ => ReplaceValuesAsync(profile.FlattenOtherSettings(), ReplaceIfStringAsync) + string s => await replaceAsync(s), + _ => o }; - - async Task ReplaceIfStringAsync(object o) - { - return o switch - { - string s => await replaceAsync(s), - _ => o - }; - } } + } - static async Task> ReplaceValuesAsync(ImmutableArray<(string Key, T Value)> source, Func> replaceAsync) - where T : class - { - // We will only allocate a new array if a substituion is made - ImmutableArray<(string, T)>.Builder? builder = null; + static async Task> ReplaceValuesAsync(ImmutableArray<(string Key, T Value)> source, Func> replaceAsync) + where T : class + { + // We will only allocate a new array if a substituion is made + ImmutableArray<(string, T)>.Builder? builder = null; - for (int index = 0; index < source.Length; index++) - { - (string key, T value) = source[index]; + for (int index = 0; index < source.Length; index++) + { + (string key, T value) = source[index]; - T replaced = await replaceAsync(value); + T replaced = await replaceAsync(value); - if (!ReferenceEquals(value, replaced)) + if (!ReferenceEquals(value, replaced)) + { + // The value had at least one token substitution. + if (builder is null) { - // The value had at least one token substitution. - if (builder is null) + // Init the builder. + builder = ImmutableArray.CreateBuilder<(string, T)>(source.Length); + + if (index != 0) { - // Init the builder. - builder = ImmutableArray.CreateBuilder<(string, T)>(source.Length); - - if (index != 0) - { - // Copy any unsubstituted values up until this point. - builder.AddRange(source, index); - } + // Copy any unsubstituted values up until this point. + builder.AddRange(source, index); } } - - builder?.Add((key, (T)replaced)); } - // Return the source unchanged if there were no substitutions made. - return builder?.MoveToImmutable() ?? source; + builder?.Add((key, (T)replaced)); } - } - public LaunchProfile( - string? name, - string? commandName, - string? executablePath = null, - string? commandLineArgs = null, - string? workingDirectory = null, - bool launchBrowser = false, - string? launchUrl = null, - bool doNotPersist = false, - ImmutableArray<(string Key, string Value)> environmentVariables = default, - ImmutableArray<(string Key, object Value)> otherSettings = default) - { - Name = name; - CommandName = commandName; - ExecutablePath = executablePath; - CommandLineArgs = commandLineArgs; - WorkingDirectory = workingDirectory; - LaunchBrowser = launchBrowser; - LaunchUrl = launchUrl; - DoNotPersist = doNotPersist; - - EnvironmentVariables = environmentVariables.IsDefault - ? ImmutableArray<(string Key, string Value)>.Empty - : environmentVariables; - OtherSettings = otherSettings.IsDefault - ? ImmutableArray<(string Key, object Value)>.Empty - : otherSettings; + // Return the source unchanged if there were no substitutions made. + return builder?.MoveToImmutable() ?? source; } + } - public string? Name { get; } - public string? CommandName { get; } - public string? ExecutablePath { get; } - public string? CommandLineArgs { get; } - public string? WorkingDirectory { get; } - public bool LaunchBrowser { get; } - public string? LaunchUrl { get; } - public bool DoNotPersist { get; } - - public ImmutableArray<(string Key, string Value)> EnvironmentVariables { get; } - public ImmutableArray<(string Key, object Value)> OtherSettings { get; } - - ImmutableDictionary? ILaunchProfile.EnvironmentVariables => EnvironmentVariables.ToImmutableDictionary(pairs => pairs.Key, pairs => pairs.Value, StringComparers.EnvironmentVariableNames); - ImmutableDictionary? ILaunchProfile.OtherSettings => OtherSettings.ToImmutableDictionary(pairs => pairs.Key, pairs => pairs.Value, StringComparers.LaunchProfileProperties); - - /// - /// Compares two profile names. Using this function ensures case comparison consistency - /// - public static bool IsSameProfileName(string? name1, string? name2) - { - return string.Equals(name1, name2, StringComparisons.LaunchProfileNames); - } + public LaunchProfile( + string? name, + string? commandName, + string? executablePath = null, + string? commandLineArgs = null, + string? workingDirectory = null, + bool launchBrowser = false, + string? launchUrl = null, + bool doNotPersist = false, + ImmutableArray<(string Key, string Value)> environmentVariables = default, + ImmutableArray<(string Key, object Value)> otherSettings = default) + { + Name = name; + CommandName = commandName; + ExecutablePath = executablePath; + CommandLineArgs = commandLineArgs; + WorkingDirectory = workingDirectory; + LaunchBrowser = launchBrowser; + LaunchUrl = launchUrl; + DoNotPersist = doNotPersist; + + EnvironmentVariables = environmentVariables.IsDefault + ? ImmutableArray<(string Key, string Value)>.Empty + : environmentVariables; + OtherSettings = otherSettings.IsDefault + ? ImmutableArray<(string Key, object Value)>.Empty + : otherSettings; + } - public override string ToString() - { - return $"Name={Name ?? ""}, Command={CommandName ?? ""}"; - } + public string? Name { get; } + public string? CommandName { get; } + public string? ExecutablePath { get; } + public string? CommandLineArgs { get; } + public string? WorkingDirectory { get; } + public bool LaunchBrowser { get; } + public string? LaunchUrl { get; } + public bool DoNotPersist { get; } + + public ImmutableArray<(string Key, string Value)> EnvironmentVariables { get; } + public ImmutableArray<(string Key, object Value)> OtherSettings { get; } + + ImmutableDictionary? ILaunchProfile.EnvironmentVariables => EnvironmentVariables.ToImmutableDictionary(pairs => pairs.Key, pairs => pairs.Value, StringComparers.EnvironmentVariableNames); + ImmutableDictionary? ILaunchProfile.OtherSettings => OtherSettings.ToImmutableDictionary(pairs => pairs.Key, pairs => pairs.Value, StringComparers.LaunchProfileProperties); + + /// + /// Compares two profile names. Using this function ensures case comparison consistency + /// + public static bool IsSameProfileName(string? name1, string? name2) + { + return string.Equals(name1, name2, StringComparisons.LaunchProfileNames); + } + + public override string ToString() + { + return $"Name={Name ?? ""}, Command={CommandName ?? ""}"; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileEnvironmentVariableEncoding.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileEnvironmentVariableEncoding.cs index d0121e6262..5490c88813 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileEnvironmentVariableEncoding.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileEnvironmentVariableEncoding.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Formats and parses the single-string representation of multiple name/value pairs +/// used for environment variables in launch profiles. +/// +/// +/// We store potentially many environment variables, each having a name and value, in a +/// single string property value on each launch profile. This class owns the formatting +/// and parsing of such strings. +/// +internal static class LaunchProfileEnvironmentVariableEncoding { - /// - /// Formats and parses the single-string representation of multiple name/value pairs - /// used for environment variables in launch profiles. - /// - /// - /// We store potentially many environment variables, each having a name and value, in a - /// single string property value on each launch profile. This class owns the formatting - /// and parsing of such strings. - /// - internal static class LaunchProfileEnvironmentVariableEncoding + public static string Format(ILaunchProfile? profile) { - public static string Format(ILaunchProfile? profile) - { - if (profile is null) - return ""; + if (profile is null) + return ""; - return KeyValuePairListEncoding.Format(profile.EnumerateEnvironmentVariables()); - } + return KeyValuePairListEncoding.Format(profile.EnumerateEnvironmentVariables()); + } - public static void ParseIntoDictionary(string value, Dictionary dictionary) - { - dictionary.Clear(); + public static void ParseIntoDictionary(string value, Dictionary dictionary) + { + dictionary.Clear(); - foreach ((string entryKey, string entryValue) in KeyValuePairListEncoding.Parse(value)) + foreach ((string entryKey, string entryValue) in KeyValuePairListEncoding.Parse(value)) + { + if (!string.IsNullOrEmpty(entryKey)) { - if (!string.IsNullOrEmpty(entryKey)) - { - dictionary[entryKey] = entryValue; - } + dictionary[entryKey] = entryValue; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileExtensions.cs index 248cbe0fda..87d485eb0d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchProfileExtensions.cs @@ -2,275 +2,274 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal static class LaunchProfileExtensions { - internal static class LaunchProfileExtensions - { - public const string HotReloadEnabledProperty = "hotReloadEnabled"; - public const string NativeDebuggingProperty = "nativeDebugging"; - public const string SqlDebuggingProperty = "sqlDebugging"; - public const string JSWebView2DebuggingProperty = "jsWebView2Debugging"; - public const string RemoteDebugEnabledProperty = "remoteDebugEnabled"; - public const string RemoteDebugMachineProperty = "remoteDebugMachine"; - public const string RemoteAuthenticationModeProperty = "authenticationMode"; + public const string HotReloadEnabledProperty = "hotReloadEnabled"; + public const string NativeDebuggingProperty = "nativeDebugging"; + public const string SqlDebuggingProperty = "sqlDebugging"; + public const string JSWebView2DebuggingProperty = "jsWebView2Debugging"; + public const string RemoteDebugEnabledProperty = "remoteDebugEnabled"; + public const string RemoteDebugMachineProperty = "remoteDebugMachine"; + public const string RemoteAuthenticationModeProperty = "authenticationMode"; - public static bool IsInMemoryObject(this object persistObject) - { - return persistObject is IPersistOption profile2 && profile2.DoNotPersist; - } + public static bool IsInMemoryObject(this object persistObject) + { + return persistObject is IPersistOption profile2 && profile2.DoNotPersist; + } - public static bool TryGetSetting(this ILaunchProfile profile, string key, [NotNullWhen(returnValue: true)] out object? value) + public static bool TryGetSetting(this ILaunchProfile profile, string key, [NotNullWhen(returnValue: true)] out object? value) + { + if (profile is ILaunchProfile2 lp2) { - if (profile is ILaunchProfile2 lp2) + foreach ((string k, object v) in lp2.OtherSettings) { - foreach ((string k, object v) in lp2.OtherSettings) + if (StringComparers.LaunchSettingsPropertyNames.Equals(key, k)) { - if (StringComparers.LaunchSettingsPropertyNames.Equals(key, k)) - { - value = v; - return true; - } + value = v; + return true; } } - - if (profile.OtherSettings?.TryGetValue(key, out value) is true) - { - return true; - } - - value = null; - return false; } - /// - /// Returns true if nativeDebugging property is set to true - /// - public static bool IsNativeDebuggingEnabled(this ILaunchProfile profile) + if (profile.OtherSettings?.TryGetValue(key, out value) is true) { - if (profile.TryGetSetting(NativeDebuggingProperty, out object? value) - && value is bool b) - { - return b; - } - - return false; + return true; } - /// - /// Returns true if sqlDebugging property is set to true - /// - public static bool IsSqlDebuggingEnabled(this ILaunchProfile profile) - { - if (profile.TryGetSetting(SqlDebuggingProperty, out object? value) - && value is bool b) - { - return b; - } + value = null; + return false; + } - return false; + /// + /// Returns true if nativeDebugging property is set to true + /// + public static bool IsNativeDebuggingEnabled(this ILaunchProfile profile) + { + if (profile.TryGetSetting(NativeDebuggingProperty, out object? value) + && value is bool b) + { + return b; } - /// - /// Returns true if jsWebView2Debugging property is set to true - /// - public static bool IsJSWebView2DebuggingEnabled(this ILaunchProfile profile) - { - if (profile.TryGetSetting(JSWebView2DebuggingProperty, out object? value) - && value is bool b) - { - return b; - } + return false; + } - return false; + /// + /// Returns true if sqlDebugging property is set to true + /// + public static bool IsSqlDebuggingEnabled(this ILaunchProfile profile) + { + if (profile.TryGetSetting(SqlDebuggingProperty, out object? value) + && value is bool b) + { + return b; } - public static bool IsRemoteDebugEnabled(this ILaunchProfile profile) - { - if (profile.TryGetSetting(RemoteDebugEnabledProperty, out object? value) - && value is bool b) - { - return b; - } + return false; + } - return false; + /// + /// Returns true if jsWebView2Debugging property is set to true + /// + public static bool IsJSWebView2DebuggingEnabled(this ILaunchProfile profile) + { + if (profile.TryGetSetting(JSWebView2DebuggingProperty, out object? value) + && value is bool b) + { + return b; } - public static string? RemoteDebugMachine(this ILaunchProfile profile) - { - if (profile.TryGetSetting(RemoteDebugMachineProperty, out object? value) - && value is string s) - { - return s; - } + return false; + } - return null; + public static bool IsRemoteDebugEnabled(this ILaunchProfile profile) + { + if (profile.TryGetSetting(RemoteDebugEnabledProperty, out object? value) + && value is bool b) + { + return b; } - public static string? RemoteAuthenticationMode(this ILaunchProfile profile) - { - if (profile.TryGetSetting(RemoteAuthenticationModeProperty, out object? value) - && value is string s) - { - return s; - } + return false; + } - return null; + public static string? RemoteDebugMachine(this ILaunchProfile profile) + { + if (profile.TryGetSetting(RemoteDebugMachineProperty, out object? value) + && value is string s) + { + return s; } - public static bool IsHotReloadEnabled(this ILaunchProfile profile) - { - if (profile.TryGetSetting(HotReloadEnabledProperty, out object? value) - && value is bool b) - { - return b; - } + return null; + } - return true; + public static string? RemoteAuthenticationMode(this ILaunchProfile profile) + { + if (profile.TryGetSetting(RemoteAuthenticationModeProperty, out object? value) + && value is string s) + { + return s; } - /// - /// Enumerates the profile's environment variables, preserving order if possible. - /// - /// - /// - /// If is , we enumerate - /// directly which has an - /// explicit order. - /// - /// - /// If is , - /// is unordered (hash ordered), - /// so we order by key. - /// - /// - /// The profile to read from. - /// An ordered enumeration of environment variable name/value pairs. - public static IEnumerable<(string Key, string Value)> EnumerateEnvironmentVariables(this ILaunchProfile profile) + return null; + } + + public static bool IsHotReloadEnabled(this ILaunchProfile profile) + { + if (profile.TryGetSetting(HotReloadEnabledProperty, out object? value) + && value is bool b) { - return profile switch - { - ILaunchProfile2 launchProfile => launchProfile.EnvironmentVariables, - ILaunchProfile { EnvironmentVariables: null or { Count: 0 } } => Enumerable.Empty<(string Key, string Value)>(), - ILaunchProfile { EnvironmentVariables: { } vars } => vars.OrderBy(pair => pair.Key, StringComparers.EnvironmentVariableNames).Select(pair => (pair.Key, pair.Value)) - }; + return b; } - /// - /// Enumerates the profile's other settings, preserving order if possible. - /// - /// - /// - /// If is , we enumerate - /// directly which has an - /// explicit order. - /// - /// - /// If is , - /// is unordered (hash ordered), - /// so we order by key. - /// - /// - /// The profile to read from. - /// An ordered enumeration of other setting name/value pairs. - public static IEnumerable<(string Key, object Value)> EnumerateOtherSettings(this ILaunchProfile profile) + return true; + } + + /// + /// Enumerates the profile's environment variables, preserving order if possible. + /// + /// + /// + /// If is , we enumerate + /// directly which has an + /// explicit order. + /// + /// + /// If is , + /// is unordered (hash ordered), + /// so we order by key. + /// + /// + /// The profile to read from. + /// An ordered enumeration of environment variable name/value pairs. + public static IEnumerable<(string Key, string Value)> EnumerateEnvironmentVariables(this ILaunchProfile profile) + { + return profile switch { - return profile switch - { - ILaunchProfile2 launchProfile => launchProfile.OtherSettings, - ILaunchProfile { OtherSettings: null or { Count: 0 } } => Enumerable.Empty<(string Key, object Value)>(), - ILaunchProfile { OtherSettings: { } settings } => settings.OrderBy(pair => pair.Key, StringComparers.LaunchProfileProperties).Select(pair => (pair.Key, pair.Value)) - }; - } + ILaunchProfile2 launchProfile => launchProfile.EnvironmentVariables, + ILaunchProfile { EnvironmentVariables: null or { Count: 0 } } => Enumerable.Empty<(string Key, string Value)>(), + ILaunchProfile { EnvironmentVariables: { } vars } => vars.OrderBy(pair => pair.Key, StringComparers.EnvironmentVariableNames).Select(pair => (pair.Key, pair.Value)) + }; + } - public static Dictionary? GetEnvironmentVariablesDictionary(this ILaunchProfile profile) + /// + /// Enumerates the profile's other settings, preserving order if possible. + /// + /// + /// + /// If is , we enumerate + /// directly which has an + /// explicit order. + /// + /// + /// If is , + /// is unordered (hash ordered), + /// so we order by key. + /// + /// + /// The profile to read from. + /// An ordered enumeration of other setting name/value pairs. + public static IEnumerable<(string Key, object Value)> EnumerateOtherSettings(this ILaunchProfile profile) + { + return profile switch { - return profile switch - { - ILaunchProfile2 launchProfile => ToDictionary(launchProfile.EnvironmentVariables, StringComparers.EnvironmentVariableNames), - ILaunchProfile { EnvironmentVariables: null or { Count: 0 } } => null, - ILaunchProfile { EnvironmentVariables: { } vars } => vars.ToDictionary(pair => pair.Key, pair => pair.Value, StringComparers.EnvironmentVariableNames) - }; - } + ILaunchProfile2 launchProfile => launchProfile.OtherSettings, + ILaunchProfile { OtherSettings: null or { Count: 0 } } => Enumerable.Empty<(string Key, object Value)>(), + ILaunchProfile { OtherSettings: { } settings } => settings.OrderBy(pair => pair.Key, StringComparers.LaunchProfileProperties).Select(pair => (pair.Key, pair.Value)) + }; + } - public static Dictionary? GetOtherSettingsDictionary(this ILaunchProfile profile) + public static Dictionary? GetEnvironmentVariablesDictionary(this ILaunchProfile profile) + { + return profile switch { - return profile switch - { - ILaunchProfile2 launchProfile => ToDictionary(launchProfile.OtherSettings, StringComparers.LaunchProfileProperties), - ILaunchProfile { OtherSettings: null or { Count: 0 } } => null, - ILaunchProfile { OtherSettings: { } vars } => vars.ToDictionary(pair => pair.Key, pair => pair.Value, StringComparers.LaunchProfileProperties) - }; - } + ILaunchProfile2 launchProfile => ToDictionary(launchProfile.EnvironmentVariables, StringComparers.EnvironmentVariableNames), + ILaunchProfile { EnvironmentVariables: null or { Count: 0 } } => null, + ILaunchProfile { EnvironmentVariables: { } vars } => vars.ToDictionary(pair => pair.Key, pair => pair.Value, StringComparers.EnvironmentVariableNames) + }; + } - private static Dictionary? ToDictionary(ImmutableArray<(string, T)> source, IEqualityComparer comparer) + public static Dictionary? GetOtherSettingsDictionary(this ILaunchProfile profile) + { + return profile switch { - if (source.IsEmpty) - { - return null; - } + ILaunchProfile2 launchProfile => ToDictionary(launchProfile.OtherSettings, StringComparers.LaunchProfileProperties), + ILaunchProfile { OtherSettings: null or { Count: 0 } } => null, + ILaunchProfile { OtherSettings: { } vars } => vars.ToDictionary(pair => pair.Key, pair => pair.Value, StringComparers.LaunchProfileProperties) + }; + } - var result = new Dictionary(capacity: source.Length, comparer); + private static Dictionary? ToDictionary(ImmutableArray<(string, T)> source, IEqualityComparer comparer) + { + if (source.IsEmpty) + { + return null; + } - foreach ((string key, T value) in source) - { - result[key] = value; - } + var result = new Dictionary(capacity: source.Length, comparer); - return result; + foreach ((string key, T value) in source) + { + result[key] = value; } - /// - /// Produces an immutable array of environment variable name/value pairs, preserving order if possible. - /// - /// - /// - /// If is , we return - /// directly which has an - /// explicit order. - /// - /// - /// If is , - /// is unordered (hash ordered), - /// We allocate a new immutable array, then populate it with name/value pairs, in name order. - /// - /// - /// The profile to read from. - /// An immutable array of environment variable name/value pairs. - public static ImmutableArray<(string Key, string Value)> FlattenEnvironmentVariables(this ILaunchProfile profile) + return result; + } + + /// + /// Produces an immutable array of environment variable name/value pairs, preserving order if possible. + /// + /// + /// + /// If is , we return + /// directly which has an + /// explicit order. + /// + /// + /// If is , + /// is unordered (hash ordered), + /// We allocate a new immutable array, then populate it with name/value pairs, in name order. + /// + /// + /// The profile to read from. + /// An immutable array of environment variable name/value pairs. + public static ImmutableArray<(string Key, string Value)> FlattenEnvironmentVariables(this ILaunchProfile profile) + { + return profile switch { - return profile switch - { - ILaunchProfile2 launchProfile => launchProfile.EnvironmentVariables, - ILaunchProfile { EnvironmentVariables: null or { Count: 0 } } => ImmutableArray<(string Key, string Value)>.Empty, - ILaunchProfile { EnvironmentVariables: { } vars } => vars.OrderBy(pair => pair.Key, StringComparers.EnvironmentVariableNames).Select(pair => (pair.Key, pair.Value)).ToImmutableArray() - }; - } + ILaunchProfile2 launchProfile => launchProfile.EnvironmentVariables, + ILaunchProfile { EnvironmentVariables: null or { Count: 0 } } => ImmutableArray<(string Key, string Value)>.Empty, + ILaunchProfile { EnvironmentVariables: { } vars } => vars.OrderBy(pair => pair.Key, StringComparers.EnvironmentVariableNames).Select(pair => (pair.Key, pair.Value)).ToImmutableArray() + }; + } - /// - /// Produces an immutable array of other setting name/value pairs, preserving order if possible. - /// - /// - /// - /// If is , we return - /// directly which has an - /// explicit order. - /// - /// - /// If is , - /// is unordered (hash ordered), - /// We allocate a new immutable array, then populate it with name/value pairs, in name order. - /// - /// - /// The profile to read from. - /// An immutable array of other setting name/value pairs. - public static ImmutableArray<(string Key, object Value)> FlattenOtherSettings(this ILaunchProfile profile) + /// + /// Produces an immutable array of other setting name/value pairs, preserving order if possible. + /// + /// + /// + /// If is , we return + /// directly which has an + /// explicit order. + /// + /// + /// If is , + /// is unordered (hash ordered), + /// We allocate a new immutable array, then populate it with name/value pairs, in name order. + /// + /// + /// The profile to read from. + /// An immutable array of other setting name/value pairs. + public static ImmutableArray<(string Key, object Value)> FlattenOtherSettings(this ILaunchProfile profile) + { + return profile switch { - return profile switch - { - ILaunchProfile2 launchProfile => launchProfile.OtherSettings, - ILaunchProfile { OtherSettings: null or { Count: 0 } } => ImmutableArray<(string Key, object Value)>.Empty, - ILaunchProfile { OtherSettings: { } vars } => vars.OrderBy(pair => pair.Key, StringComparers.EnvironmentVariableNames).Select(pair => (pair.Key, pair.Value)).ToImmutableArray() - }; - } + ILaunchProfile2 launchProfile => launchProfile.OtherSettings, + ILaunchProfile { OtherSettings: null or { Count: 0 } } => ImmutableArray<(string Key, object Value)>.Empty, + ILaunchProfile { OtherSettings: { } vars } => vars.OrderBy(pair => pair.Key, StringComparers.EnvironmentVariableNames).Select(pair => (pair.Key, pair.Value)).ToImmutableArray() + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettings.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettings.cs index 370d787793..1a912fbd38 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettings.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettings.cs @@ -2,116 +2,115 @@ using Newtonsoft.Json; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Immutable snapshot of data from the launchSettings.json file. +/// +internal class LaunchSettings : ILaunchSettings, IVersionedLaunchSettings { - /// - /// Immutable snapshot of data from the launchSettings.json file. - /// - internal class LaunchSettings : ILaunchSettings, IVersionedLaunchSettings + public static LaunchSettings Empty { get; } = new(); + + public LaunchSettings( + IEnumerable? profiles = null, + ImmutableDictionary? globalSettings = null, + string? activeProfileName = null, + ILaunchProfile? launchProfile = null, + long version = 0) { - public static LaunchSettings Empty { get; } = new(); - - public LaunchSettings( - IEnumerable? profiles = null, - ImmutableDictionary? globalSettings = null, - string? activeProfileName = null, - ILaunchProfile? launchProfile = null, - long version = 0) + Profiles = profiles is null + ? ImmutableList.Empty + : ImmutableList.CreateRange(profiles.Select(LaunchProfile.Clone)); + GlobalSettings = globalSettings ?? ImmutableStringDictionary.EmptyOrdinal; + ActiveProfile = launchProfile ?? FindActiveProfile(); + Version = version; + + ILaunchProfile? FindActiveProfile() { - Profiles = profiles is null - ? ImmutableList.Empty - : ImmutableList.CreateRange(profiles.Select(LaunchProfile.Clone)); - GlobalSettings = globalSettings ?? ImmutableStringDictionary.EmptyOrdinal; - ActiveProfile = launchProfile ?? FindActiveProfile(); - Version = version; - - ILaunchProfile? FindActiveProfile() - { - ILaunchProfile? profile = null; + ILaunchProfile? profile = null; - if (!Profiles.IsEmpty) + if (!Profiles.IsEmpty) + { + if (!Strings.IsNullOrWhiteSpace(activeProfileName)) { - if (!Strings.IsNullOrWhiteSpace(activeProfileName)) - { - // Find the first profile having the required name - profile = Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, activeProfileName)); - } - - // If no active profile specified, or the active one is no longer valid, assume the first one - profile ??= Profiles[0]; + // Find the first profile having the required name + profile = Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, activeProfileName)); } - return profile; + // If no active profile specified, or the active one is no longer valid, assume the first one + profile ??= Profiles[0]; } + + return profile; } + } - public ImmutableList Profiles { get; } + public ImmutableList Profiles { get; } - public ImmutableDictionary GlobalSettings { get; } + public ImmutableDictionary GlobalSettings { get; } - public ILaunchProfile? ActiveProfile { get; } + public ILaunchProfile? ActiveProfile { get; } - public long Version { get; } + public long Version { get; } - public object? GetGlobalSetting(string settingName) - { - GlobalSettings.TryGetValue(settingName, out object? o); - return o; - } + public object? GetGlobalSetting(string settingName) + { + GlobalSettings.TryGetValue(settingName, out object? o); + return o; + } - /// - /// Produces a sequence of values cloned from . Used to keep snapshots of - /// launch setting data immutable. values are excluded from the output sequence. - /// - /// - /// The following approach is taken: - /// - /// Common known immutable types (, , ) are not cloned. - /// If the type supports , that is used. - /// Otherwise the object is round tripped to JSON and back to create a clone. - /// - /// - internal static IEnumerable> CloneGlobalSettingsValues(IEnumerable> keyValues) - { - JsonSerializerSettings? jsonSerializerSettings = null; + /// + /// Produces a sequence of values cloned from . Used to keep snapshots of + /// launch setting data immutable. values are excluded from the output sequence. + /// + /// + /// The following approach is taken: + /// + /// Common known immutable types (, , ) are not cloned. + /// If the type supports , that is used. + /// Otherwise the object is round tripped to JSON and back to create a clone. + /// + /// + internal static IEnumerable> CloneGlobalSettingsValues(IEnumerable> keyValues) + { + JsonSerializerSettings? jsonSerializerSettings = null; - foreach ((string key, object value) in keyValues) + foreach ((string key, object value) in keyValues) + { + if (value is int or string or bool) { - if (value is int or string or bool) - { - // These common types do not need cloning. - yield return new(key, value); - } - else if (value is ICloneable cloneableObject) - { - // Type supports cloning. - yield return new(key, cloneableObject.Clone()); - } - else if (value is not null) - { - // Custom type. The best way we have to clone it is to round trip it to JSON and back. - jsonSerializerSettings ??= new() { NullValueHandling = NullValueHandling.Ignore }; + // These common types do not need cloning. + yield return new(key, value); + } + else if (value is ICloneable cloneableObject) + { + // Type supports cloning. + yield return new(key, cloneableObject.Clone()); + } + else if (value is not null) + { + // Custom type. The best way we have to clone it is to round trip it to JSON and back. + jsonSerializerSettings ??= new() { NullValueHandling = NullValueHandling.Ignore }; - string jsonString = JsonConvert.SerializeObject(value, Formatting.Indented, jsonSerializerSettings); + string jsonString = JsonConvert.SerializeObject(value, Formatting.Indented, jsonSerializerSettings); - object? clonedObject = JsonConvert.DeserializeObject(jsonString, value.GetType()); + object? clonedObject = JsonConvert.DeserializeObject(jsonString, value.GetType()); - if (clonedObject is not null) - { - yield return new(key, clonedObject); - } + if (clonedObject is not null) + { + yield return new(key, clonedObject); } - - // Null values are skipped. } + + // Null values are skipped. } } +} - internal static class LaunchSettingsExtension +internal static class LaunchSettingsExtension +{ + public static IWritableLaunchSettings ToWritableLaunchSettings(this ILaunchSettings curSettings) { - public static IWritableLaunchSettings ToWritableLaunchSettings(this ILaunchSettings curSettings) - { - return new WritableLaunchSettings(curSettings); - } + return new WritableLaunchSettings(curSettings); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProvider.cs index a6bb5c15c1..413c8a0daf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProvider.cs @@ -6,940 +6,939 @@ using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Threading.Tasks; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Manages the set of Debug profiles and web server settings and provides these as a dataflow source. Note +/// that many of the methods are protected so that unit tests can derive from this class and poke them as +/// needed w/o making them public +/// +[Export(typeof(ILaunchSettingsProvider))] +[Export(typeof(ILaunchSettingsProvider2))] +[Export(typeof(ILaunchSettingsProvider3))] +[Export(typeof(IVersionedLaunchSettingsProvider))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal class LaunchSettingsProvider : ProjectValueDataSourceBase, ILaunchSettingsProvider3, IVersionedLaunchSettingsProvider { - /// - /// Manages the set of Debug profiles and web server settings and provides these as a dataflow source. Note - /// that many of the methods are protected so that unit tests can derive from this class and poke them as - /// needed w/o making them public - /// - [Export(typeof(ILaunchSettingsProvider))] - [Export(typeof(ILaunchSettingsProvider2))] - [Export(typeof(ILaunchSettingsProvider3))] - [Export(typeof(IVersionedLaunchSettingsProvider))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal class LaunchSettingsProvider : ProjectValueDataSourceBase, ILaunchSettingsProvider3, IVersionedLaunchSettingsProvider - { - public const string LaunchSettingsFilename = "launchSettings.json"; - - // Command that means run this project - public const string RunProjectCommandName = "Project"; - - // Command that means run an executable - public const string RunExecutableCommandName = "Executable"; - - // These are used internally to loop in debuggers to handle F5 when there are errors in - // the launch settings file or when there are no profiles specified (like class libraries) - public const string ErrorProfileCommandName = "ErrorProfile"; - private const string ErrorProfileErrorMessageSettingsKey = "ErrorString"; - - private readonly UnconfiguredProject _project; - private readonly IActiveConfiguredValue _projectProperties; - private readonly IProjectFaultHandlerService _projectFaultHandler; - private readonly AsyncLazy _launchSettingsFilePath; - private readonly IUnconfiguredProjectServices _projectServices; - private readonly IUnconfiguredProjectCommonServices _commonProjectServices; - private readonly IActiveConfiguredProjectSubscriptionService? _projectSubscriptionService; - private readonly IFileSystem _fileSystem; - private readonly TaskCompletionSource _firstSnapshotCompletionSource = new(); - private readonly SequentialTaskExecutor _sequentialTaskQueue; - private readonly Lazy _defaultLaunchProfile; - private IReceivableSourceBlock? _changedSourceBlock; - private IBroadcastBlock? _broadcastBlock; - private IReceivableSourceBlock>? _versionedChangedSourceBlock; - private IBroadcastBlock>? _versionedBroadcastBlock; - private ILaunchSettings? _currentSnapshot; - private IDisposable? _projectRuleSubscriptionLink; - private long _nextVersion; - - [ImportingConstructor] - public LaunchSettingsProvider( - UnconfiguredProject project, - IUnconfiguredProjectServices projectServices, - IFileSystem fileSystem, - IUnconfiguredProjectCommonServices commonProjectServices, - IActiveConfiguredProjectSubscriptionService? projectSubscriptionService, - IActiveConfiguredValue projectProperties, - IProjectFaultHandlerService projectFaultHandler, - JoinableTaskContext joinableTaskContext) - : base(projectServices, synchronousDisposal: false, registerDataSource: false) - { - _project = project; - _projectServices = projectServices; - _fileSystem = fileSystem; - _commonProjectServices = commonProjectServices; - - _sequentialTaskQueue = new SequentialTaskExecutor(new JoinableTaskContextNode(joinableTaskContext), nameof(LaunchSettingsProvider)); - - JsonSerializationProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, - project); - SourceControlIntegrations = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - - DefaultLaunchProfileProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, project); - - _projectSubscriptionService = projectSubscriptionService; - _projectProperties = projectProperties; - _projectFaultHandler = projectFaultHandler; - _launchSettingsFilePath = new AsyncLazy(GetLaunchSettingsFilePathNoCacheAsync, commonProjectServices.ThreadingService.JoinableTaskFactory); - - _defaultLaunchProfile = new Lazy(() => - { - ILaunchProfile? profile = DefaultLaunchProfileProviders?.FirstOrDefault()?.Value?.CreateDefaultProfile(); - - return profile is null - ? null - : LaunchProfile.Clone(profile); - }); - - _nextVersion = 1; - } + public const string LaunchSettingsFilename = "launchSettings.json"; + + // Command that means run this project + public const string RunProjectCommandName = "Project"; + + // Command that means run an executable + public const string RunExecutableCommandName = "Executable"; + + // These are used internally to loop in debuggers to handle F5 when there are errors in + // the launch settings file or when there are no profiles specified (like class libraries) + public const string ErrorProfileCommandName = "ErrorProfile"; + private const string ErrorProfileErrorMessageSettingsKey = "ErrorString"; + + private readonly UnconfiguredProject _project; + private readonly IActiveConfiguredValue _projectProperties; + private readonly IProjectFaultHandlerService _projectFaultHandler; + private readonly AsyncLazy _launchSettingsFilePath; + private readonly IUnconfiguredProjectServices _projectServices; + private readonly IUnconfiguredProjectCommonServices _commonProjectServices; + private readonly IActiveConfiguredProjectSubscriptionService? _projectSubscriptionService; + private readonly IFileSystem _fileSystem; + private readonly TaskCompletionSource _firstSnapshotCompletionSource = new(); + private readonly SequentialTaskExecutor _sequentialTaskQueue; + private readonly Lazy _defaultLaunchProfile; + private IReceivableSourceBlock? _changedSourceBlock; + private IBroadcastBlock? _broadcastBlock; + private IReceivableSourceBlock>? _versionedChangedSourceBlock; + private IBroadcastBlock>? _versionedBroadcastBlock; + private ILaunchSettings? _currentSnapshot; + private IDisposable? _projectRuleSubscriptionLink; + private long _nextVersion; + + [ImportingConstructor] + public LaunchSettingsProvider( + UnconfiguredProject project, + IUnconfiguredProjectServices projectServices, + IFileSystem fileSystem, + IUnconfiguredProjectCommonServices commonProjectServices, + IActiveConfiguredProjectSubscriptionService? projectSubscriptionService, + IActiveConfiguredValue projectProperties, + IProjectFaultHandlerService projectFaultHandler, + JoinableTaskContext joinableTaskContext) + : base(projectServices, synchronousDisposal: false, registerDataSource: false) + { + _project = project; + _projectServices = projectServices; + _fileSystem = fileSystem; + _commonProjectServices = commonProjectServices; + + _sequentialTaskQueue = new SequentialTaskExecutor(new JoinableTaskContextNode(joinableTaskContext), nameof(LaunchSettingsProvider)); - [ImportMany] - protected OrderPrecedenceImportCollection JsonSerializationProviders { get; set; } + JsonSerializationProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, + project); + SourceControlIntegrations = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - [ImportMany] - protected OrderPrecedenceImportCollection SourceControlIntegrations { get; set; } + DefaultLaunchProfileProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, project); + + _projectSubscriptionService = projectSubscriptionService; + _projectProperties = projectProperties; + _projectFaultHandler = projectFaultHandler; + _launchSettingsFilePath = new AsyncLazy(GetLaunchSettingsFilePathNoCacheAsync, commonProjectServices.ThreadingService.JoinableTaskFactory); + + _defaultLaunchProfile = new Lazy(() => + { + ILaunchProfile? profile = DefaultLaunchProfileProviders?.FirstOrDefault()?.Value?.CreateDefaultProfile(); + + return profile is null + ? null + : LaunchProfile.Clone(profile); + }); + + _nextVersion = 1; + } - [ImportMany] - protected OrderPrecedenceImportCollection DefaultLaunchProfileProviders { get; set; } + [ImportMany] + protected OrderPrecedenceImportCollection JsonSerializationProviders { get; set; } - protected SimpleFileWatcher? FileWatcher { get; set; } + [ImportMany] + protected OrderPrecedenceImportCollection SourceControlIntegrations { get; set; } - // When we are saving the file we set this to minimize noise from the file change - protected bool IgnoreFileChanges { get; set; } + [ImportMany] + protected OrderPrecedenceImportCollection DefaultLaunchProfileProviders { get; set; } - protected TimeSpan FileChangeProcessingDelay = TimeSpan.FromMilliseconds(500); + protected SimpleFileWatcher? FileWatcher { get; set; } - public ITaskDelayScheduler? FileChangeScheduler { get; protected set; } + // When we are saving the file we set this to minimize noise from the file change + protected bool IgnoreFileChanges { get; set; } - // Tracks when we last read or wrote to the file. Prevents picking up needless changes - protected DateTime LastSettingsFileSyncTimeUtc { get; set; } + protected TimeSpan FileChangeProcessingDelay = TimeSpan.FromMilliseconds(500); - [Obsolete("Use GetLaunchSettingsFilePathAsync instead.")] - public string LaunchSettingsFile => _commonProjectServices.ThreadingService.ExecuteSynchronously(GetLaunchSettingsFilePathAsync); + public ITaskDelayScheduler? FileChangeScheduler { get; protected set; } - public ILaunchProfile? ActiveProfile => CurrentSnapshot?.ActiveProfile; + // Tracks when we last read or wrote to the file. Prevents picking up needless changes + protected DateTime LastSettingsFileSyncTimeUtc { get; set; } - IReceivableSourceBlock ILaunchSettingsProvider.SourceBlock + [Obsolete("Use GetLaunchSettingsFilePathAsync instead.")] + public string LaunchSettingsFile => _commonProjectServices.ThreadingService.ExecuteSynchronously(GetLaunchSettingsFilePathAsync); + + public ILaunchProfile? ActiveProfile => CurrentSnapshot?.ActiveProfile; + + IReceivableSourceBlock ILaunchSettingsProvider.SourceBlock + { + get { - get - { - EnsureInitialized(); - return _changedSourceBlock!; - } + EnsureInitialized(); + return _changedSourceBlock!; } + } - public ILaunchSettings CurrentSnapshot + public ILaunchSettings CurrentSnapshot + { + get { - get - { - EnsureInitialized(); - return _currentSnapshot!; - } - protected set - { - ILaunchSettings? prior = _currentSnapshot; - _currentSnapshot = value; + EnsureInitialized(); + return _currentSnapshot!; + } + protected set + { + ILaunchSettings? prior = _currentSnapshot; + _currentSnapshot = value; - // If this is the first snapshot, complete the taskCompletionSource - if (prior is null) - { - _firstSnapshotCompletionSource.TrySetResult(); - } + // If this is the first snapshot, complete the taskCompletionSource + if (prior is null) + { + _firstSnapshotCompletionSource.TrySetResult(); } } + } - public override NamedIdentity DataSourceKey { get; } = new NamedIdentity(nameof(LaunchSettingsProvider)); + public override NamedIdentity DataSourceKey { get; } = new NamedIdentity(nameof(LaunchSettingsProvider)); - // _nextVersion represents the version we will use in the future, so we need to - // subtract 1 to get the current version. - public override IComparable DataSourceVersion => _nextVersion - 1; + // _nextVersion represents the version we will use in the future, so we need to + // subtract 1 to get the current version. + public override IComparable DataSourceVersion => _nextVersion - 1; - public override IReceivableSourceBlock> SourceBlock + public override IReceivableSourceBlock> SourceBlock + { + get { - get - { - EnsureInitialized(); - return _versionedChangedSourceBlock!; - } + EnsureInitialized(); + return _versionedChangedSourceBlock!; } + } - /// - /// The LaunchSettingsProvider sinks 2 sets of information: - /// 1. Changes to the launchSettings.json file on disk - /// 2. Changes to the ActiveDebugProfile property in the .user file - /// - protected override void Initialize() - { - base.Initialize(); + /// + /// The LaunchSettingsProvider sinks 2 sets of information: + /// 1. Changes to the launchSettings.json file on disk + /// 2. Changes to the ActiveDebugProfile property in the .user file + /// + protected override void Initialize() + { + base.Initialize(); - // Create our broadcast block for subscribers to ILaunchSettingsProvider to get new ILaunchSettings information - _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock(nameFormat: "Launch Settings Broadcast: {1}"); - _changedSourceBlock = _broadcastBlock.SafePublicize(); + // Create our broadcast block for subscribers to ILaunchSettingsProvider to get new ILaunchSettings information + _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock(nameFormat: "Launch Settings Broadcast: {1}"); + _changedSourceBlock = _broadcastBlock.SafePublicize(); - // Create our broadcast block for subscribers to IVersionedLaunchSettingsProvider to get new ILaunchSettings information - _versionedBroadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(nameFormat: "Versioned Launch Settings Broadcast: {1}"); - _versionedChangedSourceBlock = _versionedBroadcastBlock.SafePublicize(); + // Create our broadcast block for subscribers to IVersionedLaunchSettingsProvider to get new ILaunchSettings information + _versionedBroadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(nameFormat: "Versioned Launch Settings Broadcast: {1}"); + _versionedChangedSourceBlock = _versionedBroadcastBlock.SafePublicize(); - // Subscribe to changes to the broadcast block using the idle scheduler. This should filter out a lot of the intermediate - // states that files can be in. - if (_projectSubscriptionService is not null) - { - // The use of AsyncLazy with dataflow can allow state stored in the execution context to leak through. The downstream affect is - // calls to say, get properties, may fail. To avoid this, we capture the execution context here, and it will be reapplied when - // we get new subscription data from the dataflow. - ITargetBlock>> projectChangesBlock = DataflowBlockFactory.CreateActionBlock( - DataflowUtilities.CaptureAndApplyExecutionContext>>(ProjectRuleBlock_ChangedAsync), _project, ProjectFaultSeverity.LimitedFunctionality); - StandardRuleDataflowLinkOptions evaluationLinkOptions = DataflowOption.WithRuleNames(ProjectDebugger.SchemaName); - - _projectRuleSubscriptionLink = ProjectDataSources.SyncLinkTo( - _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(evaluationLinkOptions), - _commonProjectServices.Project.Capabilities.SourceBlock.SyncLinkOptions(), - projectChangesBlock, - linkOptions: DataflowOption.PropagateCompletion); - - JoinUpstreamDataSources(_projectSubscriptionService.ProjectRuleSource, _commonProjectServices.Project.Capabilities); - } + // Subscribe to changes to the broadcast block using the idle scheduler. This should filter out a lot of the intermediate + // states that files can be in. + if (_projectSubscriptionService is not null) + { + // The use of AsyncLazy with dataflow can allow state stored in the execution context to leak through. The downstream affect is + // calls to say, get properties, may fail. To avoid this, we capture the execution context here, and it will be reapplied when + // we get new subscription data from the dataflow. + ITargetBlock>> projectChangesBlock = DataflowBlockFactory.CreateActionBlock( + DataflowUtilities.CaptureAndApplyExecutionContext>>(ProjectRuleBlock_ChangedAsync), _project, ProjectFaultSeverity.LimitedFunctionality); + StandardRuleDataflowLinkOptions evaluationLinkOptions = DataflowOption.WithRuleNames(ProjectDebugger.SchemaName); + + _projectRuleSubscriptionLink = ProjectDataSources.SyncLinkTo( + _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(evaluationLinkOptions), + _commonProjectServices.Project.Capabilities.SourceBlock.SyncLinkOptions(), + projectChangesBlock, + linkOptions: DataflowOption.PropagateCompletion); - // Make sure we are watching the file at this point - WatchLaunchSettingsFile(); + JoinUpstreamDataSources(_projectSubscriptionService.ProjectRuleSource, _commonProjectServices.Project.Capabilities); } - /// - /// Handles changes to the ProjectDebugger properties. Gets the active profile and generates a launch settings update if it - /// has changed. The first evaluation generally kicks off the first snapshot - /// - protected async Task ProjectRuleBlock_ChangedAsync(IProjectVersionedValue> projectSnapshot) + // Make sure we are watching the file at this point + WatchLaunchSettingsFile(); + } + + /// + /// Handles changes to the ProjectDebugger properties. Gets the active profile and generates a launch settings update if it + /// has changed. The first evaluation generally kicks off the first snapshot + /// + protected async Task ProjectRuleBlock_ChangedAsync(IProjectVersionedValue> projectSnapshot) + { + // Need to use JTF.RunAsync here to ensure that this task can coordinate with others. + await JoinableFactory.RunAsync(async () => { - // Need to use JTF.RunAsync here to ensure that this task can coordinate with others. - await JoinableFactory.RunAsync(async () => + if (projectSnapshot.Value.Item1.CurrentState.TryGetValue(ProjectDebugger.SchemaName, out IProjectRuleSnapshot? ruleSnapshot)) { - if (projectSnapshot.Value.Item1.CurrentState.TryGetValue(ProjectDebugger.SchemaName, out IProjectRuleSnapshot? ruleSnapshot)) + ruleSnapshot.Properties.TryGetValue(ProjectDebugger.ActiveDebugProfileProperty, out string? activeProfile); + ILaunchSettings snapshot = CurrentSnapshot; + if (snapshot is null || !LaunchProfile.IsSameProfileName(activeProfile, snapshot.ActiveProfile?.Name)) { - ruleSnapshot.Properties.TryGetValue(ProjectDebugger.ActiveDebugProfileProperty, out string? activeProfile); - ILaunchSettings snapshot = CurrentSnapshot; - if (snapshot is null || !LaunchProfile.IsSameProfileName(activeProfile, snapshot.ActiveProfile?.Name)) + // Updates need to be sequenced + await _sequentialTaskQueue.ExecuteTask(async () => { - // Updates need to be sequenced - await _sequentialTaskQueue.ExecuteTask(async () => + using (ProjectCapabilitiesContext.CreateIsolatedContext(_commonProjectServices.Project, projectSnapshot.Value.Item2)) { - using (ProjectCapabilitiesContext.CreateIsolatedContext(_commonProjectServices.Project, projectSnapshot.Value.Item2)) - { - await UpdateActiveProfileInSnapshotAsync(activeProfile); - } - }); - } + await UpdateActiveProfileInSnapshotAsync(activeProfile); + } + }); } - }); - } - - /// - /// Called when the active profile has changed. If there is a current snapshot it just updates that. Otherwise, it creates - /// a new snapshot - /// - protected async Task UpdateActiveProfileInSnapshotAsync(string? updatedActiveProfileName) - { - ILaunchSettings snapshot = CurrentSnapshot; - if (snapshot is null || await SettingsFileHasChangedAsync()) - { - await UpdateProfilesAsync(updatedActiveProfileName); - return; } + }); + } - var newSnapshot = new LaunchSettings(snapshot.Profiles, snapshot.GlobalSettings, updatedActiveProfileName, version: GetNextVersion()); - FinishUpdate(newSnapshot); + /// + /// Called when the active profile has changed. If there is a current snapshot it just updates that. Otherwise, it creates + /// a new snapshot + /// + protected async Task UpdateActiveProfileInSnapshotAsync(string? updatedActiveProfileName) + { + ILaunchSettings snapshot = CurrentSnapshot; + if (snapshot is null || await SettingsFileHasChangedAsync()) + { + await UpdateProfilesAsync(updatedActiveProfileName); + return; } - /// - /// Does the processing to update the profiles when changes have been made to either the file or the active profile name. - /// When merging with the disk, it needs to honor in-memory only profiles that may have been programmatically added. If - /// a profile on disk has the same name as an in-memory profile, the one on disk wins. It tries to add the in-memory profiles - /// in the same order they appeared prior to the disk change. - /// - protected async Task UpdateProfilesAsync(string? updatedActiveProfileName) + var newSnapshot = new LaunchSettings(snapshot.Profiles, snapshot.GlobalSettings, updatedActiveProfileName, version: GetNextVersion()); + FinishUpdate(newSnapshot); + } + + /// + /// Does the processing to update the profiles when changes have been made to either the file or the active profile name. + /// When merging with the disk, it needs to honor in-memory only profiles that may have been programmatically added. If + /// a profile on disk has the same name as an in-memory profile, the one on disk wins. It tries to add the in-memory profiles + /// in the same order they appeared prior to the disk change. + /// + protected async Task UpdateProfilesAsync(string? updatedActiveProfileName) + { + try { - try + // If the name of the new active profile wasn't provided we'll continue to use the + // current one. + if (updatedActiveProfileName is null) { - // If the name of the new active profile wasn't provided we'll continue to use the - // current one. - if (updatedActiveProfileName is null) + ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); + if (await props.ActiveDebugProfile.GetValueAsync() is IEnumValue activeProfileVal) { - ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); - if (await props.ActiveDebugProfile.GetValueAsync() is IEnumValue activeProfileVal) - { - updatedActiveProfileName = activeProfileVal.Name; - } + updatedActiveProfileName = activeProfileVal.Name; } + } - var (profiles, globalSettings) = await ReadSettingsFileFromDiskAsync(); + var (profiles, globalSettings) = await ReadSettingsFileFromDiskAsync(); - // If there are no profiles, we will add a default profile to run the project. W/o it our debugger - // won't be called on F5 and the user will see a poor error message - if (profiles.Length == 0) + // If there are no profiles, we will add a default profile to run the project. W/o it our debugger + // won't be called on F5 and the user will see a poor error message + if (profiles.Length == 0) + { + LaunchProfile? defaultLaunchProfile = _defaultLaunchProfile.Value; + if (defaultLaunchProfile is not null) { - LaunchProfile? defaultLaunchProfile = _defaultLaunchProfile.Value; - if (defaultLaunchProfile is not null) - { - profiles = profiles.Add(defaultLaunchProfile); - } + profiles = profiles.Add(defaultLaunchProfile); } + } - // If we have a previous snapshot, merge in in-memory profiles - ILaunchSettings prevSnapshot = CurrentSnapshot; - if (prevSnapshot is not null) - { - MergeExistingInMemoryProfiles(ref profiles, prevSnapshot.Profiles); - MergeExistingInMemoryGlobalSettings(ref globalSettings, prevSnapshot.GlobalSettings); - } + // If we have a previous snapshot, merge in in-memory profiles + ILaunchSettings prevSnapshot = CurrentSnapshot; + if (prevSnapshot is not null) + { + MergeExistingInMemoryProfiles(ref profiles, prevSnapshot.Profiles); + MergeExistingInMemoryGlobalSettings(ref globalSettings, prevSnapshot.GlobalSettings); + } - var newSnapshot = new LaunchSettings( - profiles, - globalSettings.ToImmutableDictionary(pair => pair.Name, pair => pair.Value, StringComparers.LaunchSettingsPropertyNames), - updatedActiveProfileName, - version: GetNextVersion()); + var newSnapshot = new LaunchSettings( + profiles, + globalSettings.ToImmutableDictionary(pair => pair.Name, pair => pair.Value, StringComparers.LaunchSettingsPropertyNames), + updatedActiveProfileName, + version: GetNextVersion()); - FinishUpdate(newSnapshot); - } - catch (Exception ex) - { - // Errors are added as error list entries. We don't want to throw out of here - // However, if we have never created a snapshot it means there is some error in the file and we want - // to have the user see that, so we add a dummy profile which will bind to an existing debugger which will - // display the error when run - if (CurrentSnapshot is null) - { - var errorProfile = new LaunchProfile( - name: Resources.NoActionProfileName, - commandName: ErrorProfileCommandName, - doNotPersist: true, - otherSettings: ImmutableArray.Create((ErrorProfileErrorMessageSettingsKey, (object)ex.Message))); - var snapshot = new LaunchSettings(new[] { errorProfile }, null, errorProfile.Name, version: GetNextVersion()); - FinishUpdate(snapshot); - } + FinishUpdate(newSnapshot); + } + catch (Exception ex) + { + // Errors are added as error list entries. We don't want to throw out of here + // However, if we have never created a snapshot it means there is some error in the file and we want + // to have the user see that, so we add a dummy profile which will bind to an existing debugger which will + // display the error when run + if (CurrentSnapshot is null) + { + var errorProfile = new LaunchProfile( + name: Resources.NoActionProfileName, + commandName: ErrorProfileCommandName, + doNotPersist: true, + otherSettings: ImmutableArray.Create((ErrorProfileErrorMessageSettingsKey, (object)ex.Message))); + var snapshot = new LaunchSettings(new[] { errorProfile }, null, errorProfile.Name, version: GetNextVersion()); + FinishUpdate(snapshot); } + } - // Re-applies in-memory profiles to the newly created snapshot. Note that we don't want to merge in the error - // profile - static void MergeExistingInMemoryProfiles(ref ImmutableArray newProfiles, ImmutableList previousProfiles) + // Re-applies in-memory profiles to the newly created snapshot. Note that we don't want to merge in the error + // profile + static void MergeExistingInMemoryProfiles(ref ImmutableArray newProfiles, ImmutableList previousProfiles) + { + for (int i = 0; i < previousProfiles.Count; i++) { - for (int i = 0; i < previousProfiles.Count; i++) - { - ILaunchProfile profile = previousProfiles[i]; + ILaunchProfile profile = previousProfiles[i]; - if (profile.IsInMemoryObject() && !string.Equals(profile.CommandName, ErrorProfileCommandName, StringComparisons.LaunchProfileCommandNames)) + if (profile.IsInMemoryObject() && !string.Equals(profile.CommandName, ErrorProfileCommandName, StringComparisons.LaunchProfileCommandNames)) + { + // Does it already have one with this name? + if (newProfiles.FirstOrDefault((p1, p2) => LaunchProfile.IsSameProfileName(p1.Name, p2.Name), profile) is null) { - // Does it already have one with this name? - if (newProfiles.FirstOrDefault((p1, p2) => LaunchProfile.IsSameProfileName(p1.Name, p2.Name), profile) is null) + // Create a new one from the existing in-memory profile and insert it in the same location, or the end if it + // is beyond the end of the list + if (i > newProfiles.Length) + { + newProfiles = newProfiles.Add(LaunchProfile.Clone(profile)); + } + else { - // Create a new one from the existing in-memory profile and insert it in the same location, or the end if it - // is beyond the end of the list - if (i > newProfiles.Length) - { - newProfiles = newProfiles.Add(LaunchProfile.Clone(profile)); - } - else - { - newProfiles = newProfiles.Insert(i, LaunchProfile.Clone(profile)); - } + newProfiles = newProfiles.Insert(i, LaunchProfile.Clone(profile)); } } } } + } - // Re-applies in-memory global options to the newly created snapshot - static void MergeExistingInMemoryGlobalSettings(ref ImmutableArray<(string Name, object Value)> newGlobalSettings, ImmutableDictionary? previousGlobalSettings) + // Re-applies in-memory global options to the newly created snapshot + static void MergeExistingInMemoryGlobalSettings(ref ImmutableArray<(string Name, object Value)> newGlobalSettings, ImmutableDictionary? previousGlobalSettings) + { + if (previousGlobalSettings is not null) { - if (previousGlobalSettings is not null) + foreach ((string key, object value) in previousGlobalSettings) { - foreach ((string key, object value) in previousGlobalSettings) + if (value.IsInMemoryObject() && !newGlobalSettings.Any(pair => StringComparers.LaunchSettingsPropertyNames.Equals(pair.Name, key))) { - if (value.IsInMemoryObject() && !newGlobalSettings.Any(pair => StringComparers.LaunchSettingsPropertyNames.Equals(pair.Name, key))) - { - newGlobalSettings = newGlobalSettings.Add((key, value)); - } + newGlobalSettings = newGlobalSettings.Add((key, value)); } } } } + } - /// - /// Returns true of the file has changed since we last read it. Note that it returns true if the file - /// does not exist - /// - protected async Task SettingsFileHasChangedAsync() - { - string fileName = await GetLaunchSettingsFilePathAsync(); - - if (_fileSystem.TryGetLastFileWriteTimeUtc(fileName, out DateTime? writeTime)) - { - return writeTime != LastSettingsFileSyncTimeUtc; - } + /// + /// Returns true of the file has changed since we last read it. Note that it returns true if the file + /// does not exist + /// + protected async Task SettingsFileHasChangedAsync() + { + string fileName = await GetLaunchSettingsFilePathAsync(); - return true; + if (_fileSystem.TryGetLastFileWriteTimeUtc(fileName, out DateTime? writeTime)) + { + return writeTime != LastSettingsFileSyncTimeUtc; } - /// - /// Helper function to set the new snapshot and post the changes to consumers. - /// - protected void FinishUpdate(ILaunchSettings newSnapshot) - { - CurrentSnapshot = newSnapshot; + return true; + } - // Suppress the project execution context so the data flow doesn't accidentally - // capture it. This prevents us from leaking any active project system locks into - // the data flow, which can lead to asserts, failures, and crashes later if it - // causes code to execute that doesn't expect to run under a lock. Also, if a - // consumer of the data flow needs a lock they should be acquiring it themself - // rather than depending on inheriting it from the data flow. - using (_commonProjectServices.ThreadingService.SuppressProjectExecutionContext()) - { - var versionedLaunchSettings = (IVersionedLaunchSettings)newSnapshot; - _versionedBroadcastBlock?.Post(new ProjectVersionedValue( - newSnapshot, - Empty.ProjectValueVersions.Add(DataSourceKey, versionedLaunchSettings.Version))); - _broadcastBlock?.Post(newSnapshot); - } + /// + /// Helper function to set the new snapshot and post the changes to consumers. + /// + protected void FinishUpdate(ILaunchSettings newSnapshot) + { + CurrentSnapshot = newSnapshot; + + // Suppress the project execution context so the data flow doesn't accidentally + // capture it. This prevents us from leaking any active project system locks into + // the data flow, which can lead to asserts, failures, and crashes later if it + // causes code to execute that doesn't expect to run under a lock. Also, if a + // consumer of the data flow needs a lock they should be acquiring it themself + // rather than depending on inheriting it from the data flow. + using (_commonProjectServices.ThreadingService.SuppressProjectExecutionContext()) + { + var versionedLaunchSettings = (IVersionedLaunchSettings)newSnapshot; + _versionedBroadcastBlock?.Post(new ProjectVersionedValue( + newSnapshot, + Empty.ProjectValueVersions.Add(DataSourceKey, versionedLaunchSettings.Version))); + _broadcastBlock?.Post(newSnapshot); } + } - /// - /// Reads launchSettings.json and returns all data as an API object. - /// - /// - /// Returns empty collections if the file does not exist. - /// - /// JSON data was not of the expected format. - protected async Task<(ImmutableArray Profiles, ImmutableArray<(string Name, object Value)> GlobalSettings)> ReadSettingsFileFromDiskAsync() + /// + /// Reads launchSettings.json and returns all data as an API object. + /// + /// + /// Returns empty collections if the file does not exist. + /// + /// JSON data was not of the expected format. + protected async Task<(ImmutableArray Profiles, ImmutableArray<(string Name, object Value)> GlobalSettings)> ReadSettingsFileFromDiskAsync() + { + string fileName = await GetLaunchSettingsFilePathAsync(); + + if (!_fileSystem.FileExists(fileName)) { - string fileName = await GetLaunchSettingsFilePathAsync(); + return (ImmutableArray.Empty, ImmutableArray<(string Name, object Value)>.Empty); + } - if (!_fileSystem.FileExists(fileName)) - { - return (ImmutableArray.Empty, ImmutableArray<(string Name, object Value)>.Empty); - } + using Stream stream = _fileSystem.OpenTextStream(fileName); - using Stream stream = _fileSystem.OpenTextStream(fileName); + var result = LaunchSettingsJsonEncoding.FromJson(new StreamReader(stream), JsonSerializationProviders); - var result = LaunchSettingsJsonEncoding.FromJson(new StreamReader(stream), JsonSerializationProviders); + // Remember the time we are sync'd to. + // Only do this when we successfully obtain a result. + LastSettingsFileSyncTimeUtc = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(fileName); - // Remember the time we are sync'd to. - // Only do this when we successfully obtain a result. - LastSettingsFileSyncTimeUtc = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(fileName); + return result; + } - return result; - } + public Task GetLaunchSettingsFilePathAsync() + { + return _launchSettingsFilePath.GetValueAsync(); + } - public Task GetLaunchSettingsFilePathAsync() - { - return _launchSettingsFilePath.GetValueAsync(); - } + /// + /// Saves the launch settings to the launch settings file. Adds an error string and throws if an exception. Note + /// that the caller is responsible for checking out the file + /// + protected async Task SaveSettingsToDiskAsync(ILaunchSettings newSettings) + { + string fileName = await GetLaunchSettingsFilePathAsync(); - /// - /// Saves the launch settings to the launch settings file. Adds an error string and throws if an exception. Note - /// that the caller is responsible for checking out the file - /// - protected async Task SaveSettingsToDiskAsync(ILaunchSettings newSettings) + try { - string fileName = await GetLaunchSettingsFilePathAsync(); - - try - { - await EnsureSettingsFolderAsync(); + await EnsureSettingsFolderAsync(); - string json = LaunchSettingsJsonEncoding.ToJson( - profiles: newSettings.Profiles, - globalSettings: newSettings.GlobalSettings.Select(pair => (pair.Key, pair.Value))); + string json = LaunchSettingsJsonEncoding.ToJson( + profiles: newSettings.Profiles, + globalSettings: newSettings.GlobalSettings.Select(pair => (pair.Key, pair.Value))); - // Ignore notifications of edits while our edit is in flight. - IgnoreFileChanges = true; + // Ignore notifications of edits while our edit is in flight. + IgnoreFileChanges = true; - await _fileSystem.WriteAllTextAsync(fileName, json); + await _fileSystem.WriteAllTextAsync(fileName, json); - // Update the last write time - LastSettingsFileSyncTimeUtc = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(fileName); - } - finally - { - IgnoreFileChanges = false; - } + // Update the last write time + LastSettingsFileSyncTimeUtc = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(fileName); } - - /// - /// Helper to check out the launchSettings.json file. - /// - protected async Task CheckoutSettingsFileAsync() + finally { - Lazy? sourceControlIntegration = SourceControlIntegrations.FirstOrDefault(); - if (sourceControlIntegration?.Value is not null) - { - string fileName = await GetLaunchSettingsFilePathAsync(); - - await sourceControlIntegration.Value.CanChangeProjectFilesAsync(new[] { fileName }); - } + IgnoreFileChanges = false; } + } - /// - /// Handler for when the Launch settings file changes. Actually, we watch the project root so any - /// file with the name LaunchSettings.json. We don't need to special case because, if a file with this name - /// changes we will only check if the one we cared about was modified. - /// - private void LaunchSettingsFile_Changed(object sender, FileSystemEventArgs e) + /// + /// Helper to check out the launchSettings.json file. + /// + protected async Task CheckoutSettingsFileAsync() + { + Lazy? sourceControlIntegration = SourceControlIntegrations.FirstOrDefault(); + if (sourceControlIntegration?.Value is not null) { - _projectFaultHandler.Forget(HandleLaunchSettingsFileChangedAsync(), _project); + string fileName = await GetLaunchSettingsFilePathAsync(); + + await sourceControlIntegration.Value.CanChangeProjectFilesAsync(new[] { fileName }); } + } - protected Task HandleLaunchSettingsFileChangedAsync() + /// + /// Handler for when the Launch settings file changes. Actually, we watch the project root so any + /// file with the name LaunchSettings.json. We don't need to special case because, if a file with this name + /// changes we will only check if the one we cared about was modified. + /// + private void LaunchSettingsFile_Changed(object sender, FileSystemEventArgs e) + { + _projectFaultHandler.Forget(HandleLaunchSettingsFileChangedAsync(), _project); + } + + protected Task HandleLaunchSettingsFileChangedAsync() + { + if (!IgnoreFileChanges) { - if (!IgnoreFileChanges) - { #pragma warning disable CS0618 // We're in a synchronous callback - string fileName = LaunchSettingsFile; + string fileName = LaunchSettingsFile; #pragma warning restore CS0618 - // Only do something if the file is truly different than what we synced. Here, we want to - // throttle. - if (_fileSystem.GetLastFileWriteTimeOrMinValueUtc(fileName) != LastSettingsFileSyncTimeUtc) - { - Assumes.NotNull(FileChangeScheduler); + // Only do something if the file is truly different than what we synced. Here, we want to + // throttle. + if (_fileSystem.GetLastFileWriteTimeOrMinValueUtc(fileName) != LastSettingsFileSyncTimeUtc) + { + Assumes.NotNull(FileChangeScheduler); - return FileChangeScheduler.ScheduleAsyncTask(token => + return FileChangeScheduler.ScheduleAsyncTask(token => + { + if (token.IsCancellationRequested) { - if (token.IsCancellationRequested) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - // Updates need to be sequenced - return _sequentialTaskQueue.ExecuteTask(() => UpdateProfilesAsync(null)); - }).Task; - } + // Updates need to be sequenced + return _sequentialTaskQueue.ExecuteTask(() => UpdateProfilesAsync(null)); + }).Task; } - - return Task.CompletedTask; } - /// - /// Makes sure the settings folder exists on disk. Doesn't add the folder to - /// the project. - /// - protected async Task EnsureSettingsFolderAsync() - { - string fileName = await GetLaunchSettingsFilePathAsync(); + return Task.CompletedTask; + } - string parentPath = Path.GetDirectoryName(fileName); + /// + /// Makes sure the settings folder exists on disk. Doesn't add the folder to + /// the project. + /// + protected async Task EnsureSettingsFolderAsync() + { + string fileName = await GetLaunchSettingsFilePathAsync(); - _fileSystem.CreateDirectory(parentPath); - } + string parentPath = Path.GetDirectoryName(fileName); - /// - /// Cleans up our watcher on the debugsettings.Json file - /// - private void CleanupFileWatcher() + _fileSystem.CreateDirectory(parentPath); + } + + /// + /// Cleans up our watcher on the debugsettings.Json file + /// + private void CleanupFileWatcher() + { + if (FileWatcher is not null) { - if (FileWatcher is not null) - { - FileWatcher.Dispose(); - FileWatcher = null; - } + FileWatcher.Dispose(); + FileWatcher = null; } + } - /// - /// Sets up a file system watcher to look for changes to the launchsettings.json file. It watches at the root of the - /// project otherwise we force the project to have a properties folder. - /// - private void WatchLaunchSettingsFile() + /// + /// Sets up a file system watcher to look for changes to the launchsettings.json file. It watches at the root of the + /// project otherwise we force the project to have a properties folder. + /// + private void WatchLaunchSettingsFile() + { + if (FileWatcher is null) { - if (FileWatcher is null) - { - FileChangeScheduler?.Dispose(); + FileChangeScheduler?.Dispose(); - Assumes.Present(_projectServices.ProjectAsynchronousTasks); + Assumes.Present(_projectServices.ProjectAsynchronousTasks); - // Create our scheduler for processing file changes - FileChangeScheduler = new TaskDelayScheduler(FileChangeProcessingDelay, _commonProjectServices.ThreadingService, - _projectServices.ProjectAsynchronousTasks.UnloadCancellationToken); + // Create our scheduler for processing file changes + FileChangeScheduler = new TaskDelayScheduler(FileChangeProcessingDelay, _commonProjectServices.ThreadingService, + _projectServices.ProjectAsynchronousTasks.UnloadCancellationToken); - try - { - FileWatcher = new SimpleFileWatcher(_commonProjectServices.Project.GetProjectDirectory(), - true, - NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite, - LaunchSettingsFilename, - LaunchSettingsFile_Changed, - LaunchSettingsFile_Changed); - } - catch (Exception ex) when (ex is IOException || ex is ArgumentException) - { - // If the project folder is no longer available this will throw, which can happen during branch switching - } + try + { + FileWatcher = new SimpleFileWatcher(_commonProjectServices.Project.GetProjectDirectory(), + true, + NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite, + LaunchSettingsFilename, + LaunchSettingsFile_Changed, + LaunchSettingsFile_Changed); + } + catch (Exception ex) when (ex is IOException || ex is ArgumentException) + { + // If the project folder is no longer available this will throw, which can happen during branch switching } } + } - /// - /// Need to make sure we cleanup the dataflow links and file watcher - /// - /// - protected override void Dispose(bool disposing) + /// + /// Need to make sure we cleanup the dataflow links and file watcher + /// + /// + protected override void Dispose(bool disposing) + { + if (disposing) { - if (disposing) + CleanupFileWatcher(); + if (FileChangeScheduler is not null) { - CleanupFileWatcher(); - if (FileChangeScheduler is not null) - { - FileChangeScheduler.Dispose(); - FileChangeScheduler = null; - } - - _sequentialTaskQueue.Dispose(); - - if (_versionedBroadcastBlock is not null) - { - _versionedBroadcastBlock.Complete(); - _versionedBroadcastBlock = null; - } - - if (_broadcastBlock is not null) - { - _broadcastBlock.Complete(); - _broadcastBlock = null; - } - - if (_projectRuleSubscriptionLink is not null) - { - _projectRuleSubscriptionLink.Dispose(); - _projectRuleSubscriptionLink = null; - } - - _firstSnapshotCompletionSource.TrySetCanceled(); + FileChangeScheduler.Dispose(); + FileChangeScheduler = null; } - } - /// - /// Replaces the current set of profiles with the contents of profiles. If changes were - /// made, the file will be checked out and saved. Note it ignores the value of the active profile - /// as this setting is controlled by a user property. - /// - public Task UpdateAndSaveSettingsAsync(ILaunchSettings newSettings) - { - // Updates need to be sequenced. Do not call this version from within an ExecuteTask as it - // will deadlock - return _sequentialTaskQueue.ExecuteTask(() => UpdateAndSaveSettingsInternalAsync(newSettings)); - } + _sequentialTaskQueue.Dispose(); - /// - /// Replaces the current set of profiles with the contents of profiles. If changes were - /// made, the file will be checked out and saved. Note it ignores the value of the active profile - /// as this setting is controlled by a user property. - /// - protected async Task UpdateAndSaveSettingsInternalAsync(ILaunchSettings newSettings, bool persistToDisk = true) - { - if (persistToDisk) + if (_versionedBroadcastBlock is not null) { - await CheckoutSettingsFileAsync(); + _versionedBroadcastBlock.Complete(); + _versionedBroadcastBlock = null; } - // Make sure the profiles are copied. We don't want them to mutate. - string? activeProfileName = ActiveProfile?.Name; + if (_broadcastBlock is not null) + { + _broadcastBlock.Complete(); + _broadcastBlock = null; + } - ILaunchSettings newSnapshot = new LaunchSettings(newSettings.Profiles, newSettings.GlobalSettings, activeProfileName, version: GetNextVersion()); - if (persistToDisk) + if (_projectRuleSubscriptionLink is not null) { - await SaveSettingsToDiskAsync(newSettings); + _projectRuleSubscriptionLink.Dispose(); + _projectRuleSubscriptionLink = null; } - FinishUpdate(newSnapshot); + _firstSnapshotCompletionSource.TrySetCanceled(); } + } - /// - /// This function blocks until a snapshot is available. It will return null if the timeout occurs - /// prior to the snapshot is available. The wait operation will be cancelled if this object is disposed or the project is unloaded. - /// - public async Task WaitForFirstSnapshot(int timeout) + /// + /// Replaces the current set of profiles with the contents of profiles. If changes were + /// made, the file will be checked out and saved. Note it ignores the value of the active profile + /// as this setting is controlled by a user property. + /// + public Task UpdateAndSaveSettingsAsync(ILaunchSettings newSettings) + { + // Updates need to be sequenced. Do not call this version from within an ExecuteTask as it + // will deadlock + return _sequentialTaskQueue.ExecuteTask(() => UpdateAndSaveSettingsInternalAsync(newSettings)); + } + + /// + /// Replaces the current set of profiles with the contents of profiles. If changes were + /// made, the file will be checked out and saved. Note it ignores the value of the active profile + /// as this setting is controlled by a user property. + /// + protected async Task UpdateAndSaveSettingsInternalAsync(ILaunchSettings newSettings, bool persistToDisk = true) + { + if (persistToDisk) { - if (CurrentSnapshot is not null) - { - return CurrentSnapshot; - } + await CheckoutSettingsFileAsync(); + } - Assumes.Present(_projectServices.ProjectAsynchronousTasks); + // Make sure the profiles are copied. We don't want them to mutate. + string? activeProfileName = ActiveProfile?.Name; - // Ensure we don't hang if the project is unloaded before we get a snapshot - Task task = _firstSnapshotCompletionSource.Task.WithCancellation(_projectServices.ProjectAsynchronousTasks.UnloadCancellationToken); + ILaunchSettings newSnapshot = new LaunchSettings(newSettings.Profiles, newSettings.GlobalSettings, activeProfileName, version: GetNextVersion()); + if (persistToDisk) + { + await SaveSettingsToDiskAsync(newSettings); + } - if (await task.TryWaitForCompleteOrTimeoutAsync(timeout)) - { - Assumes.NotNull(CurrentSnapshot); - } + FinishUpdate(newSnapshot); + } + /// + /// This function blocks until a snapshot is available. It will return null if the timeout occurs + /// prior to the snapshot is available. The wait operation will be cancelled if this object is disposed or the project is unloaded. + /// + public async Task WaitForFirstSnapshot(int timeout) + { + if (CurrentSnapshot is not null) + { return CurrentSnapshot; } - /// - /// Adds the given profile to the list and saves to disk. If a profile with the same - /// name exists (case sensitive), it will be replaced with the new profile. If addToFront is - /// true the profile will be the first one in the list. This is useful since quite often callers want - /// their just added profile to be listed first in the start menu. If addToFront is false but there is - /// an existing profile, the new one will be inserted at the same location rather than at the end. - /// - public Task AddOrUpdateProfileAsync(ILaunchProfile profile, bool addToFront) - { - // Updates need to be sequenced - return _sequentialTaskQueue.ExecuteTask(async () => + Assumes.Present(_projectServices.ProjectAsynchronousTasks); + + // Ensure we don't hang if the project is unloaded before we get a snapshot + Task task = _firstSnapshotCompletionSource.Task.WithCancellation(_projectServices.ProjectAsynchronousTasks.UnloadCancellationToken); + + if (await task.TryWaitForCompleteOrTimeoutAsync(timeout)) + { + Assumes.NotNull(CurrentSnapshot); + } + + return CurrentSnapshot; + } + + /// + /// Adds the given profile to the list and saves to disk. If a profile with the same + /// name exists (case sensitive), it will be replaced with the new profile. If addToFront is + /// true the profile will be the first one in the list. This is useful since quite often callers want + /// their just added profile to be listed first in the start menu. If addToFront is false but there is + /// an existing profile, the new one will be inserted at the same location rather than at the end. + /// + public Task AddOrUpdateProfileAsync(ILaunchProfile profile, bool addToFront) + { + // Updates need to be sequenced + return _sequentialTaskQueue.ExecuteTask(async () => + { + ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); + ILaunchProfile? existingProfile = null; + int insertionIndex = 0; + foreach (ILaunchProfile p in currentSettings.Profiles) { - ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); - ILaunchProfile? existingProfile = null; - int insertionIndex = 0; - foreach (ILaunchProfile p in currentSettings.Profiles) + if (LaunchProfile.IsSameProfileName(p.Name, profile.Name)) { - if (LaunchProfile.IsSameProfileName(p.Name, profile.Name)) - { - existingProfile = p; - break; - } - insertionIndex++; + existingProfile = p; + break; } + insertionIndex++; + } - ImmutableList profiles; - if (existingProfile is not null) - { - profiles = currentSettings.Profiles.Remove(existingProfile); - } - else - { - profiles = currentSettings.Profiles; - } + ImmutableList profiles; + if (existingProfile is not null) + { + profiles = currentSettings.Profiles.Remove(existingProfile); + } + else + { + profiles = currentSettings.Profiles; + } - if (addToFront) - { - profiles = profiles.Insert(0, LaunchProfile.Clone(profile)); - } - else - { - // Insertion index will be set to the current count (end of list) if an existing item was not found otherwise - // it will point to where the previous one was found - profiles = profiles.Insert(insertionIndex, LaunchProfile.Clone(profile)); - } + if (addToFront) + { + profiles = profiles.Insert(0, LaunchProfile.Clone(profile)); + } + else + { + // Insertion index will be set to the current count (end of list) if an existing item was not found otherwise + // it will point to where the previous one was found + profiles = profiles.Insert(insertionIndex, LaunchProfile.Clone(profile)); + } - // If the new profile is in-memory only, we don't want to touch the disk unless it replaces an existing disk based - // profile - bool saveToDisk = !profile.IsInMemoryObject() || (existingProfile?.IsInMemoryObject() == false); + // If the new profile is in-memory only, we don't want to touch the disk unless it replaces an existing disk based + // profile + bool saveToDisk = !profile.IsInMemoryObject() || (existingProfile?.IsInMemoryObject() == false); - var newSnapshot = new LaunchSettings(profiles, currentSettings?.GlobalSettings, currentSettings?.ActiveProfile?.Name, version: GetNextVersion()); - await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); - }); - } + var newSnapshot = new LaunchSettings(profiles, currentSettings?.GlobalSettings, currentSettings?.ActiveProfile?.Name, version: GetNextVersion()); + await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); + }); + } - /// - /// Removes the specified profile from the list and saves to disk. - /// - public Task RemoveProfileAsync(string profileName) + /// + /// Removes the specified profile from the list and saves to disk. + /// + public Task RemoveProfileAsync(string profileName) + { + // Updates need to be sequenced + return _sequentialTaskQueue.ExecuteTask(async () => { - // Updates need to be sequenced - return _sequentialTaskQueue.ExecuteTask(async () => + ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); + ILaunchProfile? existingProfile = currentSettings.Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, profileName)); + if (existingProfile is not null) { - ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); - ILaunchProfile? existingProfile = currentSettings.Profiles.FirstOrDefault(p => LaunchProfile.IsSameProfileName(p.Name, profileName)); - if (existingProfile is not null) - { - ImmutableList profiles = currentSettings.Profiles.Remove(existingProfile); + ImmutableList profiles = currentSettings.Profiles.Remove(existingProfile); - // If the new profile is in-memory only, we don't want to touch the disk - bool saveToDisk = !existingProfile.IsInMemoryObject(); - var newSnapshot = new LaunchSettings(profiles, currentSettings.GlobalSettings, currentSettings.ActiveProfile?.Name, version: GetNextVersion()); - await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); - } - }); - } + // If the new profile is in-memory only, we don't want to touch the disk + bool saveToDisk = !existingProfile.IsInMemoryObject(); + var newSnapshot = new LaunchSettings(profiles, currentSettings.GlobalSettings, currentSettings.ActiveProfile?.Name, version: GetNextVersion()); + await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); + } + }); + } - public Task TryUpdateProfileAsync(string profileName, Action updateAction) + public Task TryUpdateProfileAsync(string profileName, Action updateAction) + { + // Updates need to be sequenced + return _sequentialTaskQueue.ExecuteTask(async () => { - // Updates need to be sequenced - return _sequentialTaskQueue.ExecuteTask(async () => + ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); + ILaunchProfile? existingProfile = null; + int insertionIndex = 0; + foreach (ILaunchProfile p in currentSettings.Profiles) { - ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); - ILaunchProfile? existingProfile = null; - int insertionIndex = 0; - foreach (ILaunchProfile p in currentSettings.Profiles) + if (LaunchProfile.IsSameProfileName(p.Name, profileName)) { - if (LaunchProfile.IsSameProfileName(p.Name, profileName)) - { - existingProfile = p; - break; - } - insertionIndex++; + existingProfile = p; + break; } + insertionIndex++; + } - if (existingProfile is null) - { - return false; - } + if (existingProfile is null) + { + return false; + } - var writableProfile = new WritableLaunchProfile(existingProfile); - updateAction(writableProfile); - ILaunchProfile updatedProfile = writableProfile.ToLaunchProfile(); + var writableProfile = new WritableLaunchProfile(existingProfile); + updateAction(writableProfile); + ILaunchProfile updatedProfile = writableProfile.ToLaunchProfile(); - ImmutableList profiles = currentSettings.Profiles.Remove(existingProfile); + ImmutableList profiles = currentSettings.Profiles.Remove(existingProfile); - // Insertion index will point to where the previous one was found - profiles = profiles.Insert(insertionIndex, updatedProfile); + // Insertion index will point to where the previous one was found + profiles = profiles.Insert(insertionIndex, updatedProfile); - // If the updated profile is in-memory only, we don't want to touch the disk unless it replaces an existing disk based - // profile - bool saveToDisk = !updatedProfile.IsInMemoryObject() || (existingProfile?.IsInMemoryObject() == false); + // If the updated profile is in-memory only, we don't want to touch the disk unless it replaces an existing disk based + // profile + bool saveToDisk = !updatedProfile.IsInMemoryObject() || (existingProfile?.IsInMemoryObject() == false); - var newSnapshot = new LaunchSettings(profiles, currentSettings?.GlobalSettings, currentSettings?.ActiveProfile?.Name, version: GetNextVersion()); - await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); + var newSnapshot = new LaunchSettings(profiles, currentSettings?.GlobalSettings, currentSettings?.ActiveProfile?.Name, version: GetNextVersion()); + await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); - return true; - }); - } + return true; + }); + } - /// - /// Adds or updates the global settings represented by settingName. Saves the - /// updated settings to disk. Note that the settings object must be serializable. - /// - public Task AddOrUpdateGlobalSettingAsync(string settingName, object settingContent) + /// + /// Adds or updates the global settings represented by settingName. Saves the + /// updated settings to disk. Note that the settings object must be serializable. + /// + public Task AddOrUpdateGlobalSettingAsync(string settingName, object settingContent) + { + // Updates need to be sequenced + return _sequentialTaskQueue.ExecuteTask(async () => { - // Updates need to be sequenced - return _sequentialTaskQueue.ExecuteTask(async () => + ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); + ImmutableDictionary globalSettings = ImmutableStringDictionary.EmptyOrdinal; + if (currentSettings.GlobalSettings.TryGetValue(settingName, out object? currentValue)) { - ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); - ImmutableDictionary globalSettings = ImmutableStringDictionary.EmptyOrdinal; - if (currentSettings.GlobalSettings.TryGetValue(settingName, out object? currentValue)) - { - globalSettings = currentSettings.GlobalSettings.Remove(settingName); - } - else - { - globalSettings = currentSettings.GlobalSettings; - } + globalSettings = currentSettings.GlobalSettings.Remove(settingName); + } + else + { + globalSettings = currentSettings.GlobalSettings; + } - bool saveToDisk = !settingContent.IsInMemoryObject() || (currentValue?.IsInMemoryObject() == false); + bool saveToDisk = !settingContent.IsInMemoryObject() || (currentValue?.IsInMemoryObject() == false); - var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings.Add(settingName, settingContent), currentSettings.ActiveProfile?.Name, version: GetNextVersion()); - await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); - }); - } + var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings.Add(settingName, settingContent), currentSettings.ActiveProfile?.Name, version: GetNextVersion()); + await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); + }); + } - /// - /// Removes the specified global setting and saves the settings to disk - /// - public Task RemoveGlobalSettingAsync(string settingName) + /// + /// Removes the specified global setting and saves the settings to disk + /// + public Task RemoveGlobalSettingAsync(string settingName) + { + // Updates need to be sequenced + return _sequentialTaskQueue.ExecuteTask(async () => { - // Updates need to be sequenced - return _sequentialTaskQueue.ExecuteTask(async () => + ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); + if (currentSettings.GlobalSettings.TryGetValue(settingName, out object? currentValue)) { - ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); - if (currentSettings.GlobalSettings.TryGetValue(settingName, out object? currentValue)) - { - bool saveToDisk = !currentValue.IsInMemoryObject(); - ImmutableDictionary globalSettings = currentSettings.GlobalSettings.Remove(settingName); - var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name, version: GetNextVersion()); - await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); - } - }); - } + bool saveToDisk = !currentValue.IsInMemoryObject(); + ImmutableDictionary globalSettings = currentSettings.GlobalSettings.Remove(settingName); + var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name, version: GetNextVersion()); + await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); + } + }); + } - /// - /// Retrieves and updates the global settings as a single operation and saves the - /// settings to disk. - /// - public Task UpdateGlobalSettingsAsync(Func, ImmutableDictionary> updateFunction) + /// + /// Retrieves and updates the global settings as a single operation and saves the + /// settings to disk. + /// + public Task UpdateGlobalSettingsAsync(Func, ImmutableDictionary> updateFunction) + { + return _sequentialTaskQueue.ExecuteTask(async () => { - return _sequentialTaskQueue.ExecuteTask(async () => - { - ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); - ImmutableDictionary globalSettings = currentSettings.GlobalSettings; + ILaunchSettings currentSettings = await GetSnapshotThrowIfErrorsAsync(); + ImmutableDictionary globalSettings = currentSettings.GlobalSettings; - ImmutableDictionary updatesToGlobalSettings = updateFunction(globalSettings); + ImmutableDictionary updatesToGlobalSettings = updateFunction(globalSettings); - bool saveToDisk = false; + bool saveToDisk = false; - foreach ((string updatedSettingName, object? updatedSettingValue) in updatesToGlobalSettings) - { - globalSettings.TryGetValue(updatedSettingName, out object? currentValue); + foreach ((string updatedSettingName, object? updatedSettingValue) in updatesToGlobalSettings) + { + globalSettings.TryGetValue(updatedSettingName, out object? currentValue); - bool originalValueWasSavedToDisk = currentValue?.IsInMemoryObject() == false; + bool originalValueWasSavedToDisk = currentValue?.IsInMemoryObject() == false; - globalSettings = globalSettings.Remove(updatedSettingName); - if (updatedSettingValue is not null) - { - globalSettings = globalSettings.Add(updatedSettingName, updatedSettingValue); - } + globalSettings = globalSettings.Remove(updatedSettingName); + if (updatedSettingValue is not null) + { + globalSettings = globalSettings.Add(updatedSettingName, updatedSettingValue); + } - bool saveThisSettingToDisk = (updatedSettingValue is not null && !updatedSettingValue.IsInMemoryObject()) - || originalValueWasSavedToDisk; + bool saveThisSettingToDisk = (updatedSettingValue is not null && !updatedSettingValue.IsInMemoryObject()) + || originalValueWasSavedToDisk; - saveToDisk = saveToDisk || saveThisSettingToDisk; - } + saveToDisk = saveToDisk || saveThisSettingToDisk; + } - var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name, version: GetNextVersion()); - await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); - }); - } + var newSnapshot = new LaunchSettings(currentSettings.Profiles, globalSettings, currentSettings.ActiveProfile?.Name, version: GetNextVersion()); + await UpdateAndSaveSettingsInternalAsync(newSnapshot, saveToDisk); + }); + } - /// - /// Sets the active profile. This just sets the property it does not validate that the setting matches an - /// existing profile - /// - public async Task SetActiveProfileAsync(string profileName) - { - ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); - await props.ActiveDebugProfile.SetValueAsync(profileName); - } + /// + /// Sets the active profile. This just sets the property it does not validate that the setting matches an + /// existing profile + /// + public async Task SetActiveProfileAsync(string profileName) + { + ProjectDebugger props = await _commonProjectServices.ActiveConfiguredProjectProperties.GetProjectDebuggerPropertiesAsync(); + await props.ActiveDebugProfile.SetValueAsync(profileName); + } - internal async Task GetLaunchSettingsFilePathNoCacheAsync() - { - // NOTE: To reduce behavior changes, we currently cache the folder that we get from the AppDesignerSpecialFileProvider, - // even though it can change over the lifetime of the project. We should fix this and convert to using dataflow - // see: https://github.com/dotnet/project-system/issues/2316. + internal async Task GetLaunchSettingsFilePathNoCacheAsync() + { + // NOTE: To reduce behavior changes, we currently cache the folder that we get from the AppDesignerSpecialFileProvider, + // even though it can change over the lifetime of the project. We should fix this and convert to using dataflow + // see: https://github.com/dotnet/project-system/issues/2316. - // Default to the project directory if we're not able to get the AppDesigner folder. - string folder = _commonProjectServices.Project.GetProjectDirectory(); - - if (_projectProperties.Value is not null) + // Default to the project directory if we're not able to get the AppDesigner folder. + string folder = _commonProjectServices.Project.GetProjectDirectory(); + + if (_projectProperties.Value is not null) + { + AppDesigner appDesignerProperties = await _projectProperties.Value.GetAppDesignerPropertiesAsync(); + if (await appDesignerProperties.FolderName.GetValueAsync() is string appDesignerFolderName) { - AppDesigner appDesignerProperties = await _projectProperties.Value.GetAppDesignerPropertiesAsync(); - if (await appDesignerProperties.FolderName.GetValueAsync() is string appDesignerFolderName) - { - folder = Path.Combine(folder, appDesignerFolderName); - } + folder = Path.Combine(folder, appDesignerFolderName); } - - return Path.Combine(folder, LaunchSettingsFilename); } - /// - /// Helper retrieves the current snapshot and if there were errors in the launchSettings.json file - /// or there isn't a snapshot, it throws an error. There should always be a snapshot of some kind returned. - /// - private async Task GetSnapshotThrowIfErrorsAsync() + return Path.Combine(folder, LaunchSettingsFilename); + } + + /// + /// Helper retrieves the current snapshot and if there were errors in the launchSettings.json file + /// or there isn't a snapshot, it throws an error. There should always be a snapshot of some kind returned. + /// + private async Task GetSnapshotThrowIfErrorsAsync() + { + ILaunchSettings? currentSettings = await WaitForFirstSnapshot(Timeout.Infinite); + Assumes.NotNull(currentSettings); + + if (currentSettings.Profiles.Count == 1 && string.Equals(currentSettings.Profiles[0].CommandName, ErrorProfileCommandName, StringComparisons.LaunchProfileCommandNames)) { - ILaunchSettings? currentSettings = await WaitForFirstSnapshot(Timeout.Infinite); - Assumes.NotNull(currentSettings); + string fileName = await GetLaunchSettingsFilePathAsync(); - if (currentSettings.Profiles.Count == 1 && string.Equals(currentSettings.Profiles[0].CommandName, ErrorProfileCommandName, StringComparisons.LaunchProfileCommandNames)) + if (currentSettings.Profiles[0].OtherSettings is { } otherSettings + && otherSettings.TryGetValue(ErrorProfileErrorMessageSettingsKey, out object? errorMessageObject)) { - string fileName = await GetLaunchSettingsFilePathAsync(); - - if (currentSettings.Profiles[0].OtherSettings is { } otherSettings - && otherSettings.TryGetValue(ErrorProfileErrorMessageSettingsKey, out object? errorMessageObject)) - { - throw new Exception(string.Format(Resources.JsonErrorsNeedToBeCorrected_WithErrorMessage_2, fileName, errorMessageObject)); - } - else - { - throw new Exception(string.Format(Resources.JsonErrorsNeedToBeCorrected_1, fileName)); - } + throw new Exception(string.Format(Resources.JsonErrorsNeedToBeCorrected_WithErrorMessage_2, fileName, errorMessageObject)); + } + else + { + throw new Exception(string.Format(Resources.JsonErrorsNeedToBeCorrected_1, fileName)); } - - return currentSettings; } - private long GetNextVersion() => _nextVersion++; - - /// - /// Protected method for testing purposes. - /// - protected void SetNextVersion(long nextVersion) => _nextVersion = nextVersion; + return currentSettings; } + + private long GetNextVersion() => _nextVersion++; + + /// + /// Protected method for testing purposes. + /// + protected void SetNextVersion(long nextVersion) => _nextVersion = nextVersion; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProviderExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProviderExtensions.cs index 106f7cea32..1b2f352a95 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProviderExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/LaunchSettingsProviderExtensions.cs @@ -2,33 +2,32 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal static class LaunchSettingsProviderExtensions { - internal static class LaunchSettingsProviderExtensions + /// + /// Blocks until at least one snapshot has been generated. + /// + /// The underlying provider to satisfy this request. + /// An optional token to signal cancellation of the request. + /// + /// The current snapshot. + /// + public static async Task WaitForFirstSnapshot(this ILaunchSettingsProvider provider, CancellationToken token = default) { - /// - /// Blocks until at least one snapshot has been generated. - /// - /// The underlying provider to satisfy this request. - /// An optional token to signal cancellation of the request. - /// - /// The current snapshot. - /// - public static async Task WaitForFirstSnapshot(this ILaunchSettingsProvider provider, CancellationToken token = default) - { - // With an infinite timeout, the provider is contractually obligated to return a non-null value. - Task task = provider.WaitForFirstSnapshot(Timeout.Infinite); + // With an infinite timeout, the provider is contractually obligated to return a non-null value. + Task task = provider.WaitForFirstSnapshot(Timeout.Infinite); - if (token.CanBeCanceled) - { - task = task.WithCancellation(token); - } + if (token.CanBeCanceled) + { + task = task.WithCancellation(token); + } - ILaunchSettings? launchSettings = await task; + ILaunchSettings? launchSettings = await task; - Assumes.NotNull(launchSettings); + Assumes.NotNull(launchSettings); - return launchSettings; - } + return launchSettings; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/UIProfilePropertyName.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/UIProfilePropertyName.cs index 5bd4117b36..3f6de3c5a3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/UIProfilePropertyName.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/UIProfilePropertyName.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public static class UIProfilePropertyName { - public static class UIProfilePropertyName - { - public const string Executable = "Executable"; - public const string Arguments = "Arguments"; - public const string LaunchUrl = "LaunchUrl"; - public const string EnvironmentVariables = "EnvironmentVariables"; - public const string WorkingDirectory = "WorkingDirectory"; - public const string RemoteDebug = "RemoteDebug"; - public const string NativeDebugging = "NativeDebugging"; - public const string SqlDebugging = "SQLDebugging"; - public const string JSWebView2Debugging = "JSWebView2Debugging"; - } + public const string Executable = "Executable"; + public const string Arguments = "Arguments"; + public const string LaunchUrl = "LaunchUrl"; + public const string EnvironmentVariables = "EnvironmentVariables"; + public const string WorkingDirectory = "WorkingDirectory"; + public const string RemoteDebug = "RemoteDebug"; + public const string NativeDebugging = "NativeDebugging"; + public const string SqlDebugging = "SQLDebugging"; + public const string JSWebView2Debugging = "JSWebView2Debugging"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchProfile.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchProfile.cs index ba99e23335..6e3fcf4b73 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchProfile.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchProfile.cs @@ -1,74 +1,73 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +/// +/// Represents one launch profile read from the launchSettings file. +/// +internal class WritableLaunchProfile : IWritableLaunchProfile, IWritablePersistOption { - /// - /// Represents one launch profile read from the launchSettings file. - /// - internal class WritableLaunchProfile : IWritableLaunchProfile, IWritablePersistOption + public WritableLaunchProfile() { - public WritableLaunchProfile() - { - EnvironmentVariables = new(StringComparers.EnvironmentVariableNames); - OtherSettings = new(StringComparers.LaunchSettingsPropertyNames); - } + EnvironmentVariables = new(StringComparers.EnvironmentVariableNames); + OtherSettings = new(StringComparers.LaunchSettingsPropertyNames); + } - public WritableLaunchProfile(ILaunchProfile profile) - { - Name = profile.Name; - ExecutablePath = profile.ExecutablePath; - CommandName = profile.CommandName; - CommandLineArgs = profile.CommandLineArgs; - WorkingDirectory = profile.WorkingDirectory; - LaunchBrowser = profile.LaunchBrowser; - LaunchUrl = profile.LaunchUrl; - DoNotPersist = profile.IsInMemoryObject(); + public WritableLaunchProfile(ILaunchProfile profile) + { + Name = profile.Name; + ExecutablePath = profile.ExecutablePath; + CommandName = profile.CommandName; + CommandLineArgs = profile.CommandLineArgs; + WorkingDirectory = profile.WorkingDirectory; + LaunchBrowser = profile.LaunchBrowser; + LaunchUrl = profile.LaunchUrl; + DoNotPersist = profile.IsInMemoryObject(); - EnvironmentVariables = profile.GetEnvironmentVariablesDictionary() ?? new(StringComparers.EnvironmentVariableNames); - OtherSettings = profile.GetOtherSettingsDictionary() ?? new(StringComparers.LaunchSettingsPropertyNames); - } + EnvironmentVariables = profile.GetEnvironmentVariablesDictionary() ?? new(StringComparers.EnvironmentVariableNames); + OtherSettings = profile.GetOtherSettingsDictionary() ?? new(StringComparers.LaunchSettingsPropertyNames); + } - public string? Name { get; set; } - public string? CommandName { get; set; } - public string? ExecutablePath { get; set; } - public string? CommandLineArgs { get; set; } - public string? WorkingDirectory { get; set; } - public bool LaunchBrowser { get; set; } - public string? LaunchUrl { get; set; } - public bool DoNotPersist { get; set; } - public Dictionary EnvironmentVariables { get; } - public Dictionary OtherSettings { get; } + public string? Name { get; set; } + public string? CommandName { get; set; } + public string? ExecutablePath { get; set; } + public string? CommandLineArgs { get; set; } + public string? WorkingDirectory { get; set; } + public bool LaunchBrowser { get; set; } + public string? LaunchUrl { get; set; } + public bool DoNotPersist { get; set; } + public Dictionary EnvironmentVariables { get; } + public Dictionary OtherSettings { get; } - public ILaunchProfile ToLaunchProfile() - { - return new LaunchProfile( - name: Name, - executablePath: ExecutablePath, - commandName: CommandName, - commandLineArgs: CommandLineArgs, - workingDirectory: WorkingDirectory, - launchBrowser: LaunchBrowser, - launchUrl: LaunchUrl, - environmentVariables: Flatten(EnvironmentVariables), - otherSettings: Flatten(OtherSettings), - doNotPersist: DoNotPersist); + public ILaunchProfile ToLaunchProfile() + { + return new LaunchProfile( + name: Name, + executablePath: ExecutablePath, + commandName: CommandName, + commandLineArgs: CommandLineArgs, + workingDirectory: WorkingDirectory, + launchBrowser: LaunchBrowser, + launchUrl: LaunchUrl, + environmentVariables: Flatten(EnvironmentVariables), + otherSettings: Flatten(OtherSettings), + doNotPersist: DoNotPersist); - static ImmutableArray<(string, T)> Flatten(Dictionary? dictionary) + static ImmutableArray<(string, T)> Flatten(Dictionary? dictionary) + { + if (dictionary is null) { - if (dictionary is null) - { - return ImmutableArray<(string, T)>.Empty; - } - - ImmutableArray<(string, T)>.Builder builder = ImmutableArray.CreateBuilder<(string, T)>(dictionary.Count); + return ImmutableArray<(string, T)>.Empty; + } - foreach ((string key, T value) in dictionary) - { - builder.Add(new(key, value)); - } + ImmutableArray<(string, T)>.Builder builder = ImmutableArray.CreateBuilder<(string, T)>(dictionary.Count); - return builder.MoveToImmutable(); + foreach ((string key, T value) in dictionary) + { + builder.Add(new(key, value)); } + + return builder.MoveToImmutable(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettings.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettings.cs index ff7ff80e55..1bfffdbddb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettings.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettings.cs @@ -1,42 +1,41 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal class WritableLaunchSettings : IWritableLaunchSettings { - internal class WritableLaunchSettings : IWritableLaunchSettings + public WritableLaunchSettings(ILaunchSettings settings) { - public WritableLaunchSettings(ILaunchSettings settings) + if (settings.Profiles is not null) { - if (settings.Profiles is not null) + foreach (ILaunchProfile profile in settings.Profiles) { - foreach (ILaunchProfile profile in settings.Profiles) - { - // Make a mutable/writable copy of each profile - Profiles.Add(new WritableLaunchProfile(profile)); - } + // Make a mutable/writable copy of each profile + Profiles.Add(new WritableLaunchProfile(profile)); } + } - foreach ((string key, object value) in LaunchSettings.CloneGlobalSettingsValues(settings.GlobalSettings)) - { - GlobalSettings.Add(key, value); - } + foreach ((string key, object value) in LaunchSettings.CloneGlobalSettingsValues(settings.GlobalSettings)) + { + GlobalSettings.Add(key, value); + } - if (settings.ActiveProfile is not null) - { - ActiveProfile = Profiles.Find(profile => LaunchProfile.IsSameProfileName(profile.Name, settings.ActiveProfile.Name)); - } + if (settings.ActiveProfile is not null) + { + ActiveProfile = Profiles.Find(profile => LaunchProfile.IsSameProfileName(profile.Name, settings.ActiveProfile.Name)); } + } - public IWritableLaunchProfile? ActiveProfile { get; set; } + public IWritableLaunchProfile? ActiveProfile { get; set; } - public List Profiles { get; } = new List(); + public List Profiles { get; } = new List(); - public Dictionary GlobalSettings { get; } = new Dictionary(StringComparers.LaunchProfileProperties); + public Dictionary GlobalSettings { get; } = new Dictionary(StringComparers.LaunchProfileProperties); - public ILaunchSettings ToLaunchSettings() - { - return new LaunchSettings( - profiles: Profiles.Select(static profile => profile.ToLaunchProfile()), - globalSettings: ImmutableStringDictionary.EmptyOrdinal.AddRange(LaunchSettings.CloneGlobalSettingsValues(GlobalSettings))); - } + public ILaunchSettings ToLaunchSettings() + { + return new LaunchSettings( + profiles: Profiles.Select(static profile => profile.ToLaunchProfile()), + globalSettings: ImmutableStringDictionary.EmptyOrdinal.AddRange(LaunchSettings.CloneGlobalSettingsValues(GlobalSettings))); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettingsExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettingsExtensions.cs index 3dac720656..7db5661eb8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettingsExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Debug/WritableLaunchSettingsExtensions.cs @@ -2,54 +2,53 @@ using Microsoft.VisualStudio.Collections; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal static class WritableLaunchSettingsExtensions { - internal static class WritableLaunchSettingsExtensions + public static bool SettingsDiffer(this IWritableLaunchSettings launchSettings, IWritableLaunchSettings settingsToCompare) { - public static bool SettingsDiffer(this IWritableLaunchSettings launchSettings, IWritableLaunchSettings settingsToCompare) + if (launchSettings.Profiles.Count != settingsToCompare.Profiles.Count) { - if (launchSettings.Profiles.Count != settingsToCompare.Profiles.Count) + return true; + } + + // Now compare each item. We can compare in order. If the lists are different then the settings are different even + // if they contain the same items + for (int i = 0; i < launchSettings.Profiles.Count; i++) + { + if (!ProfilesAreEqual(launchSettings.Profiles[i], settingsToCompare.Profiles[i])) { return true; } + } - // Now compare each item. We can compare in order. If the lists are different then the settings are different even - // if they contain the same items - for (int i = 0; i < launchSettings.Profiles.Count; i++) + // Check the global settings + return !DictionaryEqualityComparer.Instance.Equals(launchSettings.GlobalSettings, settingsToCompare.GlobalSettings); + + static bool ProfilesAreEqual(IWritableLaunchProfile debugProfile1, IWritableLaunchProfile debugProfile2) + { + // Same instance are equal + if (ReferenceEquals(debugProfile1, debugProfile2)) { - if (!ProfilesAreEqual(launchSettings.Profiles[i], settingsToCompare.Profiles[i])) - { - return true; - } + return true; } - // Check the global settings - return !DictionaryEqualityComparer.Instance.Equals(launchSettings.GlobalSettings, settingsToCompare.GlobalSettings); - - static bool ProfilesAreEqual(IWritableLaunchProfile debugProfile1, IWritableLaunchProfile debugProfile2) + if (!string.Equals(debugProfile1.Name, debugProfile2.Name, StringComparisons.LaunchProfileProperties) || + !string.Equals(debugProfile1.CommandName, debugProfile2.CommandName, StringComparisons.LaunchProfileCommandNames) || + !string.Equals(debugProfile1.ExecutablePath, debugProfile2.ExecutablePath, StringComparisons.LaunchProfileProperties) || + !string.Equals(debugProfile1.CommandLineArgs, debugProfile2.CommandLineArgs, StringComparisons.LaunchProfileProperties) || + !string.Equals(debugProfile1.WorkingDirectory, debugProfile2.WorkingDirectory, StringComparisons.LaunchProfileProperties) || + !string.Equals(debugProfile1.LaunchUrl, debugProfile2.LaunchUrl, StringComparisons.LaunchProfileProperties) || + debugProfile1.LaunchBrowser != debugProfile2.LaunchBrowser || + !DictionaryEqualityComparer.Instance.Equals(debugProfile1.OtherSettings, debugProfile2.OtherSettings) || + !DictionaryEqualityComparer.Instance.Equals(debugProfile1.EnvironmentVariables, debugProfile2.EnvironmentVariables)) { - // Same instance are equal - if (ReferenceEquals(debugProfile1, debugProfile2)) - { - return true; - } - - if (!string.Equals(debugProfile1.Name, debugProfile2.Name, StringComparisons.LaunchProfileProperties) || - !string.Equals(debugProfile1.CommandName, debugProfile2.CommandName, StringComparisons.LaunchProfileCommandNames) || - !string.Equals(debugProfile1.ExecutablePath, debugProfile2.ExecutablePath, StringComparisons.LaunchProfileProperties) || - !string.Equals(debugProfile1.CommandLineArgs, debugProfile2.CommandLineArgs, StringComparisons.LaunchProfileProperties) || - !string.Equals(debugProfile1.WorkingDirectory, debugProfile2.WorkingDirectory, StringComparisons.LaunchProfileProperties) || - !string.Equals(debugProfile1.LaunchUrl, debugProfile2.LaunchUrl, StringComparisons.LaunchProfileProperties) || - debugProfile1.LaunchBrowser != debugProfile2.LaunchBrowser || - !DictionaryEqualityComparer.Instance.Equals(debugProfile1.OtherSettings, debugProfile2.OtherSettings) || - !DictionaryEqualityComparer.Instance.Equals(debugProfile1.EnvironmentVariables, debugProfile2.EnvironmentVariables)) - { - return false; - } - - // Compare in-memory states - return debugProfile1.IsInMemoryObject() == debugProfile2.IsInMemoryObject(); + return false; } + + // Compare in-memory states + return debugProfile1.IsInMemoryObject() == debugProfile2.IsInMemoryObject(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/IProjectExportProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/IProjectExportProvider.cs index 25eda38af8..df2ed46a31 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/IProjectExportProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/IProjectExportProvider.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Extensibility +namespace Microsoft.VisualStudio.ProjectSystem.VS.Extensibility; + +/// +/// Interface definition for global scope VS MEF component, which helps to get MEF exports from a +/// project level scope given IVsHierarchy or project file path. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +public interface IProjectExportProvider { /// - /// Interface definition for global scope VS MEF component, which helps to get MEF exports from a - /// project level scope given IVsHierarchy or project file path. + /// Returns the export for the given project without having to go to the + /// UI thread. This is the preferred method for getting access to project specific + /// exports. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - public interface IProjectExportProvider - { - /// - /// Returns the export for the given project without having to go to the - /// UI thread. This is the preferred method for getting access to project specific - /// exports. - /// - /// is or empty. - T? GetExport(string projectFilePath) where T : class; - } + /// is or empty. + T? GetExport(string projectFilePath) where T : class; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/ProjectExportProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/ProjectExportProvider.cs index 13430e6452..793637f1d7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/ProjectExportProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Extensibility/ProjectExportProvider.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Extensibility +namespace Microsoft.VisualStudio.ProjectSystem.VS.Extensibility; + +/// +/// MEF component which has methods for consumers to get to project specific MEF exports +/// +[Export(typeof(IProjectExportProvider))] +[AppliesTo(ProjectCapability.DotNet)] +internal class ProjectExportProvider : IProjectExportProvider { - /// - /// MEF component which has methods for consumers to get to project specific MEF exports - /// - [Export(typeof(IProjectExportProvider))] - [AppliesTo(ProjectCapability.DotNet)] - internal class ProjectExportProvider : IProjectExportProvider - { - private readonly IProjectServiceAccessor _projectServiceAccessor; + private readonly IProjectServiceAccessor _projectServiceAccessor; - [ImportingConstructor] - public ProjectExportProvider(IProjectServiceAccessor serviceAccessor) - { - _projectServiceAccessor = serviceAccessor; - } + [ImportingConstructor] + public ProjectExportProvider(IProjectServiceAccessor serviceAccessor) + { + _projectServiceAccessor = serviceAccessor; + } - public T? GetExport(string projectFilePath) where T : class - { - Requires.NotNullOrEmpty(projectFilePath); + public T? GetExport(string projectFilePath) where T : class + { + Requires.NotNullOrEmpty(projectFilePath); - IProjectService projectService = _projectServiceAccessor.GetProjectService(); + IProjectService projectService = _projectServiceAccessor.GetProjectService(); - UnconfiguredProject? project = projectService?.LoadedUnconfiguredProjects - .FirstOrDefault(x => string.Equals(x.FullPath, projectFilePath, StringComparisons.Paths)); + UnconfiguredProject? project = projectService?.LoadedUnconfiguredProjects + .FirstOrDefault(x => string.Equals(x.FullPath, projectFilePath, StringComparisons.Paths)); - return project?.Services.ExportProvider.GetExportedValueOrDefault(); - } + return project?.Services.ExportProvider.GetExportedValueOrDefault(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FaultExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FaultExtensions.cs index ec1f097b77..bde154f021 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FaultExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FaultExtensions.cs @@ -5,260 +5,259 @@ using System.Globalization; using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides extensions methods for APIs in the project system that end up filing Watson reports. +/// +internal static class FaultExtensions { + private static readonly ErrorReportSettings s_defaultReportSettings = new( + eventName: "VisualStudioNonFatalErrors2", + component: "ManagedProjectSystem", + reportType: ErrorReportType.Critical, + submitFlags: ErrorReportSubmitFlags.OutOfProcess | ErrorReportSubmitFlags.NoCloseUI, + submitUIOptions: ImmutableDictionary.Create()); + /// - /// Provides extensions methods for APIs in the project system that end up filing Watson reports. + /// Reports the specified fault. /// - internal static class FaultExtensions + /// + /// The that should handle the fault. + /// + /// + /// Exception containing the fault information. + /// + /// + /// The severity of the failure. + /// + /// + /// The project related to the failure, if applicable. Can be . + /// + public static Task ReportFaultAsync( + this IProjectFaultHandlerService faultHandlerService, + Exception ex, + UnconfiguredProject? project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) { - private static readonly ErrorReportSettings s_defaultReportSettings = new( - eventName: "VisualStudioNonFatalErrors2", - component: "ManagedProjectSystem", - reportType: ErrorReportType.Critical, - submitFlags: ErrorReportSubmitFlags.OutOfProcess | ErrorReportSubmitFlags.NoCloseUI, - submitUIOptions: ImmutableDictionary.Create()); + Requires.NotNull(faultHandlerService); - /// - /// Reports the specified fault. - /// - /// - /// The that should handle the fault. - /// - /// - /// Exception containing the fault information. - /// - /// - /// The severity of the failure. - /// - /// - /// The project related to the failure, if applicable. Can be . - /// - public static Task ReportFaultAsync( - this IProjectFaultHandlerService faultHandlerService, - Exception ex, - UnconfiguredProject? project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) - { - Requires.NotNull(faultHandlerService); + return faultHandlerService.HandleFaultAsync(ex, s_defaultReportSettings, severity, project); + } - return faultHandlerService.HandleFaultAsync(ex, s_defaultReportSettings, severity, project); - } + /// + /// Attaches error handling to a task so that if it throws an unhandled exception, + /// the error will be reported to the user. + /// + /// + /// The that should handle the fault. + /// + /// + /// The task to attach error handling to. + /// + /// + /// The project related to the failure, if applicable. Can be . + /// + /// + /// The severity of the failure. + /// + public static void Forget( + this IProjectFaultHandlerService faultHandlerService, + Task task, + UnconfiguredProject? project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) + { + Requires.NotNull(faultHandlerService); - /// - /// Attaches error handling to a task so that if it throws an unhandled exception, - /// the error will be reported to the user. - /// - /// - /// The that should handle the fault. - /// - /// - /// The task to attach error handling to. - /// - /// - /// The project related to the failure, if applicable. Can be . - /// - /// - /// The severity of the failure. - /// - public static void Forget( - this IProjectFaultHandlerService faultHandlerService, - Task task, - UnconfiguredProject? project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) - { - Requires.NotNull(faultHandlerService); + faultHandlerService.RegisterFaultHandler(task, s_defaultReportSettings, severity, project); + } - faultHandlerService.RegisterFaultHandler(task, s_defaultReportSettings, severity, project); - } + /// + /// Attaches error handling to a task so that if it throws an unhandled exception, + /// the error will be reported to the user. + /// + /// + /// The that should handle the fault. + /// + /// + /// The task to attach error handling to. + /// + /// + /// The project related to the failure, if applicable. Can be . + /// + /// + /// The severity of the failure. + /// + public static void Forget( + this IProjectFaultHandlerService faultHandlerService, + Task task, + UnconfiguredProject? project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) + { + Requires.NotNull(faultHandlerService); - /// - /// Attaches error handling to a task so that if it throws an unhandled exception, - /// the error will be reported to the user. - /// - /// - /// The that should handle the fault. - /// - /// - /// The task to attach error handling to. - /// - /// - /// The project related to the failure, if applicable. Can be . - /// - /// - /// The severity of the failure. - /// - public static void Forget( - this IProjectFaultHandlerService faultHandlerService, - Task task, - UnconfiguredProject? project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) - { - Requires.NotNull(faultHandlerService); + faultHandlerService.RegisterFaultHandler(task, s_defaultReportSettings, severity, project); + } - faultHandlerService.RegisterFaultHandler(task, s_defaultReportSettings, severity, project); - } + /// + /// Executes the specified delegate in a safe fire-and-forget manner, prevent the project from + /// closing until it has completed. + /// + /// + /// The that handles the fork. + /// + /// + /// The async delegate to invoke. It is invoked asynchronously with respect to the caller. + /// + /// + /// The unconfigured project which the delegate operates on, if applicable. Can be . + /// + /// + /// Suggests to the user how severe the fault is if the delegate throws. + /// + /// + /// Influences the environment in which the delegate is executed. + /// + public static void RunAndForget( + this IProjectThreadingService threadingService, + Func asyncAction, + UnconfiguredProject? unconfiguredProject, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, + ForkOptions options = ForkOptions.Default) + { + Requires.NotNull(threadingService); - /// - /// Executes the specified delegate in a safe fire-and-forget manner, prevent the project from - /// closing until it has completed. - /// - /// - /// The that handles the fork. - /// - /// - /// The async delegate to invoke. It is invoked asynchronously with respect to the caller. - /// - /// - /// The unconfigured project which the delegate operates on, if applicable. Can be . - /// - /// - /// Suggests to the user how severe the fault is if the delegate throws. - /// - /// - /// Influences the environment in which the delegate is executed. - /// - public static void RunAndForget( - this IProjectThreadingService threadingService, - Func asyncAction, - UnconfiguredProject? unconfiguredProject, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, - ForkOptions options = ForkOptions.Default) + // If you do not pass in a project it is not legal to ask the threading service to cancel this operation on project unloading + if (unconfiguredProject is null) { - Requires.NotNull(threadingService); + options &= ~ForkOptions.CancelOnUnload; + } - // If you do not pass in a project it is not legal to ask the threading service to cancel this operation on project unloading - if (unconfiguredProject is null) - { - options &= ~ForkOptions.CancelOnUnload; - } + threadingService.Fork(asyncAction, factory: null, unconfiguredProject: unconfiguredProject, watsonReportSettings: s_defaultReportSettings, faultSeverity: severity, options: options); + } - threadingService.Fork(asyncAction, factory: null, unconfiguredProject: unconfiguredProject, watsonReportSettings: s_defaultReportSettings, faultSeverity: severity, options: options); - } + /// + /// Executes the specified delegate in a safe fire-and-forget manner, prevent the project from + /// closing until it has completed. + /// + /// + /// The that handles the fork. + /// + /// + /// The async delegate to invoke. It is invoked asynchronously with respect to the caller. + /// + /// + /// The configured project which the delegate operates on, if applicable. Can be . + /// + /// + /// Suggests to the user how severe the fault is if the delegate throws. + /// + /// + /// Influences the environment in which the delegate is executed. + /// + public static void RunAndForget( + this IProjectThreadingService threadingService, + Func asyncAction, + ConfiguredProject? configuredProject, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, + ForkOptions options = ForkOptions.Default) + { + Requires.NotNull(threadingService); - /// - /// Executes the specified delegate in a safe fire-and-forget manner, prevent the project from - /// closing until it has completed. - /// - /// - /// The that handles the fork. - /// - /// - /// The async delegate to invoke. It is invoked asynchronously with respect to the caller. - /// - /// - /// The configured project which the delegate operates on, if applicable. Can be . - /// - /// - /// Suggests to the user how severe the fault is if the delegate throws. - /// - /// - /// Influences the environment in which the delegate is executed. - /// - public static void RunAndForget( - this IProjectThreadingService threadingService, - Func asyncAction, - ConfiguredProject? configuredProject, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable, - ForkOptions options = ForkOptions.Default) + // If you do not pass in a project it is not legal to ask the threading service to cancel this operation on project unloading + if (configuredProject is null) { - Requires.NotNull(threadingService); + options &= ~ForkOptions.CancelOnUnload; + } - // If you do not pass in a project it is not legal to ask the threading service to cancel this operation on project unloading - if (configuredProject is null) - { - options &= ~ForkOptions.CancelOnUnload; - } + threadingService.Fork(asyncAction, factory: null, configuredProject: configuredProject, watsonReportSettings: s_defaultReportSettings, faultSeverity: severity, options: options); + } - threadingService.Fork(asyncAction, factory: null, configuredProject: configuredProject, watsonReportSettings: s_defaultReportSettings, faultSeverity: severity, options: options); - } + /// + /// Attaches error handling to a block so that if it throws an unhandled exception, + /// the error will be reported to the user. + /// + /// + /// The that should handle the fault. + /// + /// + /// The block to attach error handling to. + /// + /// + /// The project related to the failure, if applicable. Can be . + /// + /// + /// The severity of the failure. + /// + public static Task RegisterFaultHandlerAsync( + this IProjectFaultHandlerService faultHandlerService, + IDataflowBlock block, + UnconfiguredProject? project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) + { + Requires.NotNull(faultHandlerService); + Requires.NotNull(block); - /// - /// Attaches error handling to a block so that if it throws an unhandled exception, - /// the error will be reported to the user. - /// - /// - /// The that should handle the fault. - /// - /// - /// The block to attach error handling to. - /// - /// - /// The project related to the failure, if applicable. Can be . - /// - /// - /// The severity of the failure. - /// - public static Task RegisterFaultHandlerAsync( - this IProjectFaultHandlerService faultHandlerService, - IDataflowBlock block, - UnconfiguredProject? project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) + return block.Completion.ContinueWith(_ => { - Requires.NotNull(faultHandlerService); - Requires.NotNull(block); + Exception exception = block.Completion.Exception; - return block.Completion.ContinueWith(_ => + // If the exception is an aggregate over a single inner exception, take the inner message + string innerMessage = exception switch { - Exception exception = block.Completion.Exception; + AggregateException { InnerExceptions: { Count: 1 } inner } => inner[0].Message, + _ => exception.Message + }; - // If the exception is an aggregate over a single inner exception, take the inner message - string innerMessage = exception switch - { - AggregateException { InnerExceptions: { Count: 1 } inner } => inner[0].Message, - _ => exception.Message - }; + var dataSourceException = new AggregateException( + string.Format( + CultureInfo.CurrentCulture, + Resources.DataFlowFaults, + block.ToString(), + innerMessage), + exception); - var dataSourceException = new AggregateException( - string.Format( - CultureInfo.CurrentCulture, - Resources.DataFlowFaults, - block.ToString(), - innerMessage), - exception); - - try - { - throw dataSourceException; - } - catch (AggregateException ex) - { - dataSourceException = ex; - } + try + { + throw dataSourceException; + } + catch (AggregateException ex) + { + dataSourceException = ex; + } - return faultHandlerService.ReportFaultAsync(dataSourceException, project, severity); - }, - CancellationToken.None, - TaskContinuationOptions.OnlyOnFaulted, - TaskScheduler.Default); - } + return faultHandlerService.ReportFaultAsync(dataSourceException, project, severity); + }, + CancellationToken.None, + TaskContinuationOptions.OnlyOnFaulted, + TaskScheduler.Default); + } - /// - /// Attaches error handling to a block so that if it throws an unhandled exception, - /// the error will be reported to the user. - /// - /// - /// The that should handle the fault. - /// - /// - /// The block to attach error handling to. - /// - /// - /// The project related to the failure, if applicable. Can be . - /// - /// - /// The severity of the failure. - /// - public static void RegisterFaultHandler( - this IProjectFaultHandlerService faultHandlerService, - IDataflowBlock block, - UnconfiguredProject? project, - ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) - { - Task task = RegisterFaultHandlerAsync(faultHandlerService, block, project, severity); + /// + /// Attaches error handling to a block so that if it throws an unhandled exception, + /// the error will be reported to the user. + /// + /// + /// The that should handle the fault. + /// + /// + /// The block to attach error handling to. + /// + /// + /// The project related to the failure, if applicable. Can be . + /// + /// + /// The severity of the failure. + /// + public static void RegisterFaultHandler( + this IProjectFaultHandlerService faultHandlerService, + IDataflowBlock block, + UnconfiguredProject? project, + ProjectFaultSeverity severity = ProjectFaultSeverity.Recoverable) + { + Task task = RegisterFaultHandlerAsync(faultHandlerService, block, project, severity); - // We don't actually care about the result of reporting the fault if one occurs - faultHandlerService.Forget(task, project); - } + // We don't actually care about the result of reporting the fault if one occurs + faultHandlerService.Forget(task, project); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FileItemServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FileItemServices.cs index ae7b44d4ce..d5da01adb7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FileItemServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/FileItemServices.cs @@ -1,53 +1,52 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class FileItemServices { - internal static class FileItemServices + /// + /// Returns the logical folder names of the specified , starting + /// at , using 'Link' metadata if it represents a linked file. + /// + public static string[]? GetLogicalFolderNames(string basePath, string fullPath, IImmutableDictionary metadata) { - /// - /// Returns the logical folder names of the specified , starting - /// at , using 'Link' metadata if it represents a linked file. - /// - public static string[]? GetLogicalFolderNames(string basePath, string fullPath, IImmutableDictionary metadata) - { - Requires.NotNullOrEmpty(basePath); - Requires.NotNullOrEmpty(fullPath); - Requires.NotNull(metadata); + Requires.NotNullOrEmpty(basePath); + Requires.NotNullOrEmpty(fullPath); + Requires.NotNull(metadata); - // Roslyn wants the effective set of folders from the source up to, but not including the project - // root to handle the cases where linked files have a different path in the tree than what its path - // on disk is. It uses these folders for code actions that create files alongside others, such as - // extract interface. + // Roslyn wants the effective set of folders from the source up to, but not including the project + // root to handle the cases where linked files have a different path in the tree than what its path + // on disk is. It uses these folders for code actions that create files alongside others, such as + // extract interface. - string linkOrFullPath = GetLinkFilePath(metadata) ?? fullPath; + string linkOrFullPath = GetLinkFilePath(metadata) ?? fullPath; - // We try to make either the link path or full path relative to the project path - string relativePath = PathHelper.MakeRelative(basePath, linkOrFullPath); - if (relativePath.Length == 0) - return null; + // We try to make either the link path or full path relative to the project path + string relativePath = PathHelper.MakeRelative(basePath, linkOrFullPath); + if (relativePath.Length == 0) + return null; - // Is this outside of base path? - if (Path.IsPathRooted(relativePath) || relativePath.StartsWith("..\\", StringComparisons.Paths)) - return null; + // Is this outside of base path? + if (Path.IsPathRooted(relativePath) || relativePath.StartsWith("..\\", StringComparisons.Paths)) + return null; - string? relativeDirectoryName = Path.GetDirectoryName(relativePath); - if (relativeDirectoryName?.Length == 0) - return null; + string? relativeDirectoryName = Path.GetDirectoryName(relativePath); + if (relativeDirectoryName?.Length == 0) + return null; - // We now have a folder in the form of `Folder1\Folder2` relative to the - // project directory split it up into individual path components - return relativeDirectoryName?.Split(Delimiter.Path); - } + // We now have a folder in the form of `Folder1\Folder2` relative to the + // project directory split it up into individual path components + return relativeDirectoryName?.Split(Delimiter.Path); + } - private static string? GetLinkFilePath(IImmutableDictionary metadata) + private static string? GetLinkFilePath(IImmutableDictionary metadata) + { + // This mimic's CPS's handling of Link metadata + if (metadata.TryGetValue(Compile.LinkProperty, out string? linkFilePath) && !string.IsNullOrWhiteSpace(linkFilePath)) { - // This mimic's CPS's handling of Link metadata - if (metadata.TryGetValue(Compile.LinkProperty, out string? linkFilePath) && !string.IsNullOrWhiteSpace(linkFilePath)) - { - return linkFilePath.TrimEnd(Delimiter.Path); - } - - return null; + return linkFilePath.TrimEnd(Delimiter.Path); } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfigurationComponent.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfigurationComponent.cs index 634d2b5d8f..e00c52ade6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfigurationComponent.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfigurationComponent.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// A configured-project service which will be activated when its configured project becomes active, or deactivated when it is not. +/// +/// +/// Exports of this interface must update with its associated capabilities. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface IActiveConfigurationComponent { /// - /// A configured-project service which will be activated when its configured project becomes active, or deactivated when it is not. + /// Activates the service. /// - /// - /// Exports of this interface must update with its associated capabilities. - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface IActiveConfigurationComponent - { - /// - /// Activates the service. - /// - Task ActivateAsync(); + Task ActivateAsync(); - /// - /// Deactivates the service. - /// - Task DeactivateAsync(); - } + /// + /// Deactivates the service. + /// + Task DeactivateAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredProjectsProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredProjectsProvider.cs index 0e04a73d19..0482c38a07 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredProjectsProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredProjectsProvider.cs @@ -1,59 +1,58 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// An UnconfiguredProject-level service that provides access to the and +/// objects that the host considers to be active. +/// +/// +/// This service replaces to handle projects where more than +/// is considered active at the same time, such as projects that produce +/// multiple outputs. See for more information. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IActiveConfiguredProjectsProvider { /// - /// An UnconfiguredProject-level service that provides access to the and - /// objects that the host considers to be active. + /// Gets all the active configured projects by TargetFramework dimension for the current unconfigured project. + /// If the current project is not a cross-targeting project, then it returns a singleton key-value pair with an + /// ignorable key and single active configured project as value. /// + /// + /// Map from TargetFramework dimension to active configured project, or if there + /// are no active objects. + /// + [Obsolete("This method will be removed in a future build.")] + Task?> GetActiveConfiguredProjectsMapAsync(); + + /// + /// Returns the ordered list of configured projects that are active for the current project, loading them if needed. + /// + /// + /// An containing the ordered set of objects + /// with the names of the configuration dimensions that participated in the calculation of the active + /// objects, or if there are no active + /// objects. + /// /// - /// This service replaces to handle projects where more than - /// is considered active at the same time, such as projects that produce - /// multiple outputs. See for more information. + /// The order in the returned matches the declared ordered within + /// the project file. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IActiveConfiguredProjectsProvider - { - /// - /// Gets all the active configured projects by TargetFramework dimension for the current unconfigured project. - /// If the current project is not a cross-targeting project, then it returns a singleton key-value pair with an - /// ignorable key and single active configured project as value. - /// - /// - /// Map from TargetFramework dimension to active configured project, or if there - /// are no active objects. - /// - [Obsolete("This method will be removed in a future build.")] - Task?> GetActiveConfiguredProjectsMapAsync(); - - /// - /// Returns the ordered list of configured projects that are active for the current project, loading them if needed. - /// - /// - /// An containing the ordered set of objects - /// with the names of the configuration dimensions that participated in the calculation of the active - /// objects, or if there are no active - /// objects. - /// - /// - /// The order in the returned matches the declared ordered within - /// the project file. - /// - Task?> GetActiveConfiguredProjectsAsync(); + Task?> GetActiveConfiguredProjectsAsync(); - /// - /// Returns the ordered list of project configurations that are active for the current project. - /// - /// - /// An containing the ordered set of objects - /// with the names of the configuration dimensions that participated in the calculation of the active - /// objects, or if there are no active - /// objects. - /// - /// - /// The order in the returned matches the declared ordered within - /// the project file. - /// - Task?> GetActiveProjectConfigurationsAsync(); - } + /// + /// Returns the ordered list of project configurations that are active for the current project. + /// + /// + /// An containing the ordered set of objects + /// with the names of the configuration dimensions that participated in the calculation of the active + /// objects, or if there are no active + /// objects. + /// + /// + /// The order in the returned matches the declared ordered within + /// the project file. + /// + Task?> GetActiveProjectConfigurationsAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValue.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValue.cs index 639b4804a2..5177a018e4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValue.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValue.cs @@ -2,32 +2,31 @@ #pragma warning disable RS0030 // Do not used banned APIs (this wraps this API) -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides an access to an export from the active +/// . This is the singular version of . +/// +/// +/// +/// This replaces by returning +/// instead of throwing when the value does not exist. +/// +/// +/// Consumers should specify a nullable type for if that import will be +/// satisfied by an export that will be applied to a particular capability. +/// +/// +internal interface IActiveConfiguredValue + where T : class? { /// - /// Provides an access to an export from the active - /// . This is the singular version of . + /// Gets the value from the active ; otherwise, + /// if it does not exist. /// - /// - /// - /// This replaces by returning - /// instead of throwing when the value does not exist. - /// - /// - /// Consumers should specify a nullable type for if that import will be - /// satisfied by an export that will be applied to a particular capability. - /// - /// - internal interface IActiveConfiguredValue - where T : class? + public T Value { - /// - /// Gets the value from the active ; otherwise, - /// if it does not exist. - /// - public T Value - { - get; - } + get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValues.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValues.cs index 315a038b88..9101068d5f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValues.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IActiveConfiguredValues.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides an access to exports from the active +/// . This is the plural version of . +/// +internal interface IActiveConfiguredValues + where T : class { /// - /// Provides an access to exports from the active - /// . This is the plural version of . + /// Gets the applicable values from the active . /// - internal interface IActiveConfiguredValues - where T : class + public IEnumerable> Values { - /// - /// Gets the applicable values from the active . - /// - public IEnumerable> Values - { - get; - } + get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ICreateFileFromTemplateService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ICreateFileFromTemplateService.cs index 55edca0577..c3a03bcffc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ICreateFileFromTemplateService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ICreateFileFromTemplateService.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// This service creates a file from a given file template. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface ICreateFileFromTemplateService { /// - /// This service creates a file from a given file template. + /// Create a file with the given template file and add it to the parent node. /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface ICreateFileFromTemplateService - { - /// - /// Create a file with the given template file and add it to the parent node. - /// - /// The name of the template zip file. - /// The path to the file to be created. - /// true if file is added successfully. - Task CreateFileAsync(string templateFile, string path); - } + /// The name of the template zip file. + /// The path to the file to be created. + /// true if file is added successfully. + Task CreateFileAsync(string templateFile, string path); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IImplicitlyActiveConfigurationComponent.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IImplicitlyActiveConfigurationComponent.cs index 131a6ebc74..5d0ef2d76a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IImplicitlyActiveConfigurationComponent.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IImplicitlyActiveConfigurationComponent.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// A configured-project service which will be activated when its configured project becomes implicitly active, or deactivated when it is not. +/// +/// +/// Exports of this interface must update with its associated capabilities. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface IImplicitlyActiveConfigurationComponent { /// - /// A configured-project service which will be activated when its configured project becomes implicitly active, or deactivated when it is not. + /// Activates the service. /// - /// - /// Exports of this interface must update with its associated capabilities. - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface IImplicitlyActiveConfigurationComponent - { - /// - /// Activates the service. - /// - Task ActivateAsync(); + Task ActivateAsync(); - /// - /// Deactivates the service. - /// - Task DeactivateAsync(); - } + /// + /// Deactivates the service. + /// + Task DeactivateAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ILoadedInHostListener.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ILoadedInHostListener.cs index f3685f74d7..9fb69d28bd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ILoadedInHostListener.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ILoadedInHostListener.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Represents a service that listen for project loaded events in a host. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private)] +internal interface ILoadedInHostListener { /// - /// Represents a service that listen for project loaded events in a host. + /// Starts listening for project events in a host. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private)] - internal interface ILoadedInHostListener - { - /// - /// Starts listening for project events in a host. - /// - /// - /// Once this method has been called once, all future calls are no-ops. - /// - Task StartListeningAsync(); - } + /// + /// Once this method has been called once, all future calls are no-ops. + /// + Task StartListeningAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IMultiLifetimeInstance.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IMultiLifetimeInstance.cs index 2ac28daea8..8bfd0af074 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IMultiLifetimeInstance.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IMultiLifetimeInstance.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Represents an instance that is automatically initialized when its parent +/// is loaded, or disposed when it is unloaded. +/// +internal interface IMultiLifetimeInstance { /// - /// Represents an instance that is automatically initialized when its parent - /// is loaded, or disposed when it is unloaded. + /// Initializes the . /// - internal interface IMultiLifetimeInstance - { - /// - /// Initializes the . - /// - Task InitializeAsync(); + Task InitializeAsync(); - /// - /// Disposes the . - /// - Task DisposeAsync(); - } + /// + /// Disposes the . + /// + Task DisposeAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTree.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTree.cs index 09da900f9a..6ed4c4b831 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTree.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTree.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Represents the physical project tree in Solution Explorer. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IPhysicalProjectTree { /// - /// Represents the physical project tree in Solution Explorer. + /// Gets the service that provides file and folder operations that operate on the physical . /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IPhysicalProjectTree - { - /// - /// Gets the service that provides file and folder operations that operate on the physical . - /// - IPhysicalProjectTreeStorage TreeStorage { get; } + IPhysicalProjectTreeStorage TreeStorage { get; } - /// - /// Gets the most recently published tree, or if it has not yet be published. - /// - IProjectTree? CurrentTree { get; } + /// + /// Gets the most recently published tree, or if it has not yet be published. + /// + IProjectTree? CurrentTree { get; } - /// - /// Gets the service that manages the tree in Solution Explorer. - /// - IProjectTreeService TreeService { get; } + /// + /// Gets the service that manages the tree in Solution Explorer. + /// + IProjectTreeService TreeService { get; } - /// - /// Gets the project tree provider that creates the Solution Explorer tree. - /// - IProjectTreeProvider TreeProvider { get; } - } + /// + /// Gets the project tree provider that creates the Solution Explorer tree. + /// + IProjectTreeProvider TreeProvider { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTreeStorage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTreeStorage.cs index c742557634..13a82b8124 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTreeStorage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IPhysicalProjectTreeStorage.cs @@ -2,144 +2,143 @@ using Microsoft.VisualStudio.IO; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides file and folder operations that operate on the physical . +/// +/// +/// This interface provides a simple facade over the , , +/// , and interfaces. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IPhysicalProjectTreeStorage { /// - /// Provides file and folder operations that operate on the physical . + /// Adds an existing file to the physical project tree. /// + /// + /// The path of the file to add, can be relative to the project directory. + /// /// - /// This interface provides a simple facade over the , , - /// , and interfaces. + /// This method will automatically publish the resulting tree to . /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IPhysicalProjectTreeStorage - { - /// - /// Adds an existing file to the physical project tree. - /// - /// - /// The path of the file to add, can be relative to the project directory. - /// - /// - /// This method will automatically publish the resulting tree to . - /// - /// - /// is . - /// - /// - /// is an empty string (""), contains only white space, or contains one or more invalid characters. - /// - /// -or- - /// - /// is prefixed with, or contains, only a colon character (:). - /// - /// - /// contains a colon character (:) that is not part of a drive label ("C:\"). - /// - Task AddFileAsync(string path); + /// + /// is . + /// + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. + /// + /// -or- + /// + /// is prefixed with, or contains, only a colon character (:). + /// + /// + /// contains a colon character (:) that is not part of a drive label ("C:\"). + /// + Task AddFileAsync(string path); - /// - /// Creates a zero-byte file on disk, adding it add to the physical project tree. - /// - /// - /// The path of the file to create, can be relative to the project directory. - /// - /// - /// This method will automatically publish the resulting tree to . - /// - /// - /// is . - /// - /// - /// is an empty string (""), contains only white space, or contains one or more invalid characters. - /// - /// -or- - /// - /// is prefixed with, or contains, only a colon character (:). - /// - /// - /// The file specified by is a directory. - /// - /// -or- - /// - /// The network name is not known. - /// - /// - /// The caller does not have the required permission. - /// - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// - /// - /// contains a colon character (:) that is not part of a drive label ("C:\"). - /// - Task CreateEmptyFileAsync(string path); + /// + /// Creates a zero-byte file on disk, adding it add to the physical project tree. + /// + /// + /// The path of the file to create, can be relative to the project directory. + /// + /// + /// This method will automatically publish the resulting tree to . + /// + /// + /// is . + /// + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. + /// + /// -or- + /// + /// is prefixed with, or contains, only a colon character (:). + /// + /// + /// The file specified by is a directory. + /// + /// -or- + /// + /// The network name is not known. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. + /// + /// + /// The specified path is invalid (for example, it is on an unmapped drive). + /// + /// + /// contains a colon character (:) that is not part of a drive label ("C:\"). + /// + Task CreateEmptyFileAsync(string path); - /// - /// Creates a folder on disk, adding it add to the physical project tree. - /// - /// - /// The path of the folder to create, can be relative to the project directory. - /// - /// - /// This method will automatically publish the resulting tree to . - /// - /// - /// is . - /// - /// - /// is an empty string (""), contains only white space, or contains one or more invalid characters. - /// - /// -or- - /// - /// is prefixed with, or contains, only a colon character (:). - /// - /// - /// The directory specified by is a file. - /// - /// -or- - /// - /// The network name is not known. - /// - /// - /// The caller does not have the required permission. - /// - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// - /// - /// contains a colon character (:) that is not part of a drive label ("C:\"). - /// - Task CreateFolderAsync(string path); + /// + /// Creates a folder on disk, adding it add to the physical project tree. + /// + /// + /// The path of the folder to create, can be relative to the project directory. + /// + /// + /// This method will automatically publish the resulting tree to . + /// + /// + /// is . + /// + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. + /// + /// -or- + /// + /// is prefixed with, or contains, only a colon character (:). + /// + /// + /// The directory specified by is a file. + /// + /// -or- + /// + /// The network name is not known. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The specified path, file name, or both exceed the system-defined maximum length. + /// + /// + /// The specified path is invalid (for example, it is on an unmapped drive). + /// + /// + /// contains a colon character (:) that is not part of a drive label ("C:\"). + /// + Task CreateFolderAsync(string path); - /// - /// Adds an existing folder to the physical project tree. - /// - /// - /// The path of the folder to add, can be relative to the project directory. - /// - /// - /// This method will automatically publish the resulting tree to . - /// - /// - /// is . - /// - /// - /// is an empty string (""), contains only white space, or contains one or more invalid characters. - /// - /// -or- - /// - /// is prefixed with, or contains, only a colon character (:). - /// - /// - /// contains a colon character (:) that is not part of a drive label ("C:\"). - /// - Task AddFolderAsync(string path); - } + /// + /// Adds an existing folder to the physical project tree. + /// + /// + /// The path of the folder to add, can be relative to the project directory. + /// + /// + /// This method will automatically publish the resulting tree to . + /// + /// + /// is . + /// + /// + /// is an empty string (""), contains only white space, or contains one or more invalid characters. + /// + /// -or- + /// + /// is prefixed with, or contains, only a colon character (:). + /// + /// + /// contains a colon character (:) that is not part of a drive label ("C:\"). + /// + Task AddFolderAsync(string path); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectAccessor.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectAccessor.cs index 8631eab213..ee8cdc0364 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectAccessor.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectAccessor.cs @@ -3,198 +3,197 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// A project-service level component that provides methods for accessing the MSBuild evaluation and +/// construction models for a or . +/// +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IProjectAccessor { /// - /// A project-service level component that provides methods for accessing the MSBuild evaluation and - /// construction models for a or . + /// Obtains a write lock, asynchronously awaiting for the lock if it is not immediately available. /// - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectAccessor - { - /// - /// Obtains a write lock, asynchronously awaiting for the lock if it is not immediately available. - /// - /// - /// The to run while holding the lock. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code other than methods on within . - /// - Task EnterWriteLockAsync(Func action, CancellationToken cancellationToken = default); + /// + /// The to run while holding the lock. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code other than methods on within . + /// + Task EnterWriteLockAsync(Func action, CancellationToken cancellationToken = default); - /// - /// Opens the MSBuild project construction model for the specified project, passing it to the specified action for reading. - /// - /// - /// The whose underlying MSBuild object model is required. - /// - /// - /// The to run while holding the lock. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . - /// - Task OpenProjectXmlForReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default); + /// + /// Opens the MSBuild project construction model for the specified project, passing it to the specified action for reading. + /// + /// + /// The whose underlying MSBuild object model is required. + /// + /// + /// The to run while holding the lock. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . + /// + Task OpenProjectXmlForReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default); - /// - /// Opens the MSBuild project construction model for the specified project, passing it to the specified action for reading, with the option to upgrade for writing. - /// - /// - /// The whose underlying MSBuild object model is required. - /// - /// - /// The to run while holding the lock. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code other than within . - /// - Task OpenProjectXmlForUpgradeableReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default); + /// + /// Opens the MSBuild project construction model for the specified project, passing it to the specified action for reading, with the option to upgrade for writing. + /// + /// + /// The whose underlying MSBuild object model is required. + /// + /// + /// The to run while holding the lock. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code other than within . + /// + Task OpenProjectXmlForUpgradeableReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default); - /// - /// Opens the MSBuild project construction model for the specified project, passing it to the specified action for writing. - /// - /// - /// The whose underlying MSBuild object model is required. - /// - /// - /// The to run while holding the lock. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . - /// - Task OpenProjectXmlForWriteAsync(UnconfiguredProject project, Action action, CancellationToken cancellationToken = default); + /// + /// Opens the MSBuild project construction model for the specified project, passing it to the specified action for writing. + /// + /// + /// The whose underlying MSBuild object model is required. + /// + /// + /// The to run while holding the lock. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . + /// + Task OpenProjectXmlForWriteAsync(UnconfiguredProject project, Action action, CancellationToken cancellationToken = default); - /// - /// Opens the MSBuild project evaluation model for the specified project, passing it to the specified action for reading. - /// - /// - /// The whose underlying MSBuild object model is required. - /// - /// - /// The to run while holding the lock. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . - /// - Task OpenProjectForReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default); + /// + /// Opens the MSBuild project evaluation model for the specified project, passing it to the specified action for reading. + /// + /// + /// The whose underlying MSBuild object model is required. + /// + /// + /// The to run while holding the lock. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . + /// + Task OpenProjectForReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default); - /// - /// Opens the MSBuild project evaluation model for the specified project, passing it to the specified action for reading, with the option to upgrade for writing. - /// - /// - /// The whose underlying MSBuild object model is required. - /// - /// - /// The to run while holding the lock. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . - /// - Task OpenProjectForUpgradeableReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default); + /// + /// Opens the MSBuild project evaluation model for the specified project, passing it to the specified action for reading, with the option to upgrade for writing. + /// + /// + /// The whose underlying MSBuild object model is required. + /// + /// + /// The to run while holding the lock. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . + /// + Task OpenProjectForUpgradeableReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default); - /// - /// Opens the MSBuild project evaluation model for the specified project, passing it to the specified action for writing. - /// - /// - /// The whose underlying MSBuild object model is required. - /// - /// - /// The to run while holding the lock. - /// - /// - /// Indicates whether to checkout the project from source control. The default is . - /// - /// - /// A token whose cancellation signals lost interest in the result. The default is . - /// - /// - /// The result of executing over the . - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . - /// - Task OpenProjectForWriteAsync(ConfiguredProject project, Action action, ProjectCheckoutOption option = ProjectCheckoutOption.Checkout, CancellationToken cancellationToken = default); - } + /// + /// Opens the MSBuild project evaluation model for the specified project, passing it to the specified action for writing. + /// + /// + /// The whose underlying MSBuild object model is required. + /// + /// + /// The to run while holding the lock. + /// + /// + /// Indicates whether to checkout the project from source control. The default is . + /// + /// + /// A token whose cancellation signals lost interest in the result. The default is . + /// + /// + /// The result of executing over the . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// NOTE: To avoid deadlocks, do not call arbitrary services or asynchronous code within . + /// + Task OpenProjectForWriteAsync(ConfiguredProject project, Action action, ProjectCheckoutOption option = ProjectCheckoutOption.Checkout, CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectCapabilitiesService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectCapabilitiesService.cs index 640770e51b..411e36e36a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectCapabilitiesService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectCapabilitiesService.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem -{ +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides methods for querying and testing the current project's capabilities. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IProjectCapabilitiesService +{ // This interface introduced just so that we can mock checks for capabilities, + // to avoid static state and call context data that we cannot influence + /// - /// Provides methods for querying and testing the current project's capabilities. + /// Returns a value indicating whether the current project has the specified capability /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectCapabilitiesService - { // This interface introduced just so that we can mock checks for capabilities, - // to avoid static state and call context data that we cannot influence - - /// - /// Returns a value indicating whether the current project has the specified capability - /// - /// - /// is . - /// - /// - /// is an empty string (""). - /// - bool Contains(string capability); - } + /// + /// is . + /// + /// + /// is an empty string (""). + /// + bool Contains(string capability); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectItemProviderExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectItemProviderExtensions.cs index b9d42c661b..a8c326f33d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectItemProviderExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectItemProviderExtensions.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectItemProviderExtensions { - internal static class IProjectItemProviderExtensions + public static async Task GetItemAsync(this IProjectItemProvider provider, string itemType, Func> predicate) { - public static async Task GetItemAsync(this IProjectItemProvider provider, string itemType, Func> predicate) + foreach (IProjectItem item in await provider.GetItemsAsync(itemType)) { - foreach (IProjectItem item in await provider.GetItemsAsync(itemType)) + if (await predicate(item)) { - if (await predicate(item)) - { - return item; - } + return item; } - - return null; } - public static async Task GetItemAsync(this IProjectItemProvider provider, string itemType, Func predicate) + return null; + } + + public static async Task GetItemAsync(this IProjectItemProvider provider, string itemType, Func predicate) + { + foreach (IProjectItem item in await provider.GetItemsAsync(itemType)) { - foreach (IProjectItem item in await provider.GetItemsAsync(itemType)) + if (predicate(item)) { - if (predicate(item)) - { - return item; - } + return item; } - - return null; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs index b6fd4c5c4a..bf035d4e25 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectSystemOptions.cs @@ -1,74 +1,73 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides properties for retrieving options for the project system. +/// +[ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IProjectSystemOptions { /// - /// Provides properties for retrieving options for the project system. + /// Gets a value indicating if the project fast up to date check is enabled. /// - [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectSystemOptions - { - /// - /// Gets a value indicating if the project fast up to date check is enabled. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// if the project fast up to date check is enabled; otherwise, - /// - Task GetIsFastUpToDateCheckEnabledAsync(CancellationToken cancellationToken); + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// if the project fast up to date check is enabled; otherwise, + /// + Task GetIsFastUpToDateCheckEnabledAsync(CancellationToken cancellationToken); - /// - /// Gets a value indicating the level of fast up to date check logging. - /// - /// - /// A token whose cancellation signals lost interest in the result. - /// - /// - /// The level of fast up to date check logging. - /// - Task GetFastUpToDateLoggingLevelAsync(CancellationToken cancellationToken); + /// + /// Gets a value indicating the level of fast up to date check logging. + /// + /// + /// A token whose cancellation signals lost interest in the result. + /// + /// + /// The level of fast up to date check logging. + /// + Task GetFastUpToDateLoggingLevelAsync(CancellationToken cancellationToken); - /// - /// Gets a value indicating whether the designer view is the default editor for the specified designer category. - /// - Task GetUseDesignerByDefaultAsync(string designerCategory, bool defaultValue, CancellationToken cancellationToken); + /// + /// Gets a value indicating whether the designer view is the default editor for the specified designer category. + /// + Task GetUseDesignerByDefaultAsync(string designerCategory, bool defaultValue, CancellationToken cancellationToken); - /// - /// Sets a value indicating whether the designer view is the default editor for the specified designer category. - /// - Task SetUseDesignerByDefaultAsync(string designerCategory, bool value, CancellationToken cancellationToken); + /// + /// Sets a value indicating whether the designer view is the default editor for the specified designer category. + /// + Task SetUseDesignerByDefaultAsync(string designerCategory, bool value, CancellationToken cancellationToken); - /// - /// Gets a value indicating if analyzers should be skipped for implicitly triggered build. - /// - Task GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(CancellationToken cancellationToken); + /// + /// Gets a value indicating if analyzers should be skipped for implicitly triggered build. + /// + Task GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(CancellationToken cancellationToken); - /// - /// Gets a value indicating if single-target builds should be preferred for startup projects. - /// - Task GetPreferSingleTargetBuildsForStartupProjectsAsync(CancellationToken cancellationToken); + /// + /// Gets a value indicating if single-target builds should be preferred for startup projects. + /// + Task GetPreferSingleTargetBuildsForStartupProjectsAsync(CancellationToken cancellationToken); - /// - /// Gets whether incremental build failure detection should write to the output window when failures are detected. - /// - ValueTask IsIncrementalBuildFailureOutputLoggingEnabledAsync(CancellationToken cancellationToken); + /// + /// Gets whether incremental build failure detection should write to the output window when failures are detected. + /// + ValueTask IsIncrementalBuildFailureOutputLoggingEnabledAsync(CancellationToken cancellationToken); - /// - /// Gets whether incremental build failure detection should send telemetry. - /// - ValueTask IsIncrementalBuildFailureTelemetryEnabledAsync(CancellationToken cancellationToken); + /// + /// Gets whether incremental build failure detection should send telemetry. + /// + ValueTask IsIncrementalBuildFailureTelemetryEnabledAsync(CancellationToken cancellationToken); - /// - /// Gets whether Build Acceleration should be enabled when a project does not explicitly opt in - /// or out via the AccelerateBuildsInVisualStudio MSBuild property. - /// - ValueTask IsBuildAccelerationEnabledByDefaultAsync(CancellationToken cancellationToken); + /// + /// Gets whether Build Acceleration should be enabled when a project does not explicitly opt in + /// or out via the AccelerateBuildsInVisualStudio MSBuild property. + /// + ValueTask IsBuildAccelerationEnabledByDefaultAsync(CancellationToken cancellationToken); - /// - /// Gets whether LSP pull diagnostics are enabled. - /// - ValueTask IsLspPullDiagnosticsEnabledAsync(CancellationToken cancellationToken); - } + /// + /// Gets whether LSP pull diagnostics are enabled. + /// + ValueTask IsLspPullDiagnosticsEnabledAsync(CancellationToken cancellationToken); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectTreeExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectTreeExtensions.cs index 0a18614dd8..0bd82c2ee8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectTreeExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IProjectTreeExtensions.cs @@ -4,80 +4,79 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectTreeExtensions { - internal static class IProjectTreeExtensions + /// + /// Gets the direct child of with + /// if found, otherwise . + /// + [Pure] + public static IProjectTree? FindChildWithCaption(this IProjectTree tree, string caption) { - /// - /// Gets the direct child of with - /// if found, otherwise . - /// - [Pure] - public static IProjectTree? FindChildWithCaption(this IProjectTree tree, string caption) - { - return tree.Children.FirstOrDefault( - static (child, cap) => string.Equals(cap, child.Caption, StringComparisons.ProjectTreeCaptionIgnoreCase), - caption); - } + return tree.Children.FirstOrDefault( + static (child, cap) => string.Equals(cap, child.Caption, StringComparisons.ProjectTreeCaptionIgnoreCase), + caption); + } - /// - /// Gets the direct child of that represents if - /// one exists, otherwise . - /// - [Pure] - public static IProjectTree? FindChildForDependency(this IProjectTree tree, IDependency dependency) - { - return tree.Children.FirstOrDefault( - static (child, dependency) => - StringComparers.ItemNames.Equals(dependency.Id, child.BrowseObjectProperties?.ItemName) || - StringComparers.ProjectTreeCaptionIgnoreCase.Equals(dependency.Caption, child.Caption), - dependency); - } + /// + /// Gets the direct child of that represents if + /// one exists, otherwise . + /// + [Pure] + public static IProjectTree? FindChildForDependency(this IProjectTree tree, IDependency dependency) + { + return tree.Children.FirstOrDefault( + static (child, dependency) => + StringComparers.ItemNames.Equals(dependency.Id, child.BrowseObjectProperties?.ItemName) || + StringComparers.ProjectTreeCaptionIgnoreCase.Equals(dependency.Caption, child.Caption), + dependency); + } - /// - /// Finds the first child node having , or if no child matches. - /// - [Pure] - internal static IProjectTree? FindChildWithFlags(this IProjectTree self, ProjectTreeFlags flags) + /// + /// Finds the first child node having , or if no child matches. + /// + [Pure] + internal static IProjectTree? FindChildWithFlags(this IProjectTree self, ProjectTreeFlags flags) + { + foreach (IProjectTree child in self.Children) { - foreach (IProjectTree child in self.Children) + if (child.Flags.Contains(flags)) { - if (child.Flags.Contains(flags)) - { - return child; - } + return child; } - - return null; } - /// - /// Returns the properties of a , returning the result from a - /// project snapshot if it is available, otherwise, returns the live results. - /// - /// - /// Prefer this method over to avoid - /// needing to take a project lock to read properties, which can avoid UI delays but - /// with possibility of out-of-date data. - /// - internal static IRule? GetBrowseObjectPropertiesViaSnapshotIfAvailable(this IProjectTree node, ConfiguredProject project) - { - Assumes.Present(project.Services.PropertyPagesCatalog); + return null; + } - IRule? properties = node.BrowseObjectProperties; + /// + /// Returns the properties of a , returning the result from a + /// project snapshot if it is available, otherwise, returns the live results. + /// + /// + /// Prefer this method over to avoid + /// needing to take a project lock to read properties, which can avoid UI delays but + /// with possibility of out-of-date data. + /// + internal static IRule? GetBrowseObjectPropertiesViaSnapshotIfAvailable(this IProjectTree node, ConfiguredProject project) + { + Assumes.Present(project.Services.PropertyPagesCatalog); - if (properties?.Schema is null || !project.Services.PropertyPagesCatalog.SourceBlock.TryReceive(null, out IProjectVersionedValue? catalogSnapshot)) - return properties; + IRule? properties = node.BrowseObjectProperties; - // We let the snapshot be out of date with the "live" project - if (!catalogSnapshot.Value.NamedCatalogs.TryGetValue(PropertyPageContexts.BrowseObject, out IPropertyPagesCatalog? pagesCatalog)) - return properties; + if (properties?.Schema is null || !project.Services.PropertyPagesCatalog.SourceBlock.TryReceive(null, out IProjectVersionedValue? catalogSnapshot)) + return properties; - Assumes.NotNull(catalogSnapshot.Value.Project); + // We let the snapshot be out of date with the "live" project + if (!catalogSnapshot.Value.NamedCatalogs.TryGetValue(PropertyPageContexts.BrowseObject, out IPropertyPagesCatalog? pagesCatalog)) + return properties; - IRule? snapshot = pagesCatalog.BindToContext(properties.Schema.Name, catalogSnapshot.Value.Project.ProjectInstance, properties.ItemType, properties.ItemName); + Assumes.NotNull(catalogSnapshot.Value.Project); - return snapshot ?? properties; - } + IRule? snapshot = pagesCatalog.BindToContext(properties.Schema.Name, catalogSnapshot.Value.Project.ProjectInstance, properties.ItemType, properties.ItemName); + + return snapshot ?? properties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISafeProjectGuidService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISafeProjectGuidService.cs index a81a20fbc3..2ecadbaeee 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISafeProjectGuidService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISafeProjectGuidService.cs @@ -1,37 +1,36 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides a mechanism to safely access the project GUID. Replaces _most_ usage of the IProjectGuidService +/// and IProjectGuidService2 interfaces from CPS which are not safe to use in all contexts. +/// +/// +/// +/// IProjectGuidService and IProjectGuidService2 will retrieve the project GUID of the project *at the time* +/// that it is called. During project initialization, the GUID may be changed by the solution in reaction to a +/// clash with another project. will wait until it is safe to retrieve +/// the project GUID before returning it. +/// +/// +/// Note that, since this waits until the project is initialized, it is NOT safe to use in code called during +/// initialization. For example, it is not safe to use in +/// as that may be called during project initialization; attempting to do so will result in a deadlock. +/// +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface ISafeProjectGuidService { /// - /// Provides a mechanism to safely access the project GUID. Replaces _most_ usage of the IProjectGuidService - /// and IProjectGuidService2 interfaces from CPS which are not safe to use in all contexts. + /// Returns the project GUID, waiting until project load has safely progressed + /// to a point where the GUID is guaranteed not to change. /// - /// - /// - /// IProjectGuidService and IProjectGuidService2 will retrieve the project GUID of the project *at the time* - /// that it is called. During project initialization, the GUID may be changed by the solution in reaction to a - /// clash with another project. will wait until it is safe to retrieve - /// the project GUID before returning it. - /// - /// - /// Note that, since this waits until the project is initialized, it is NOT safe to use in code called during - /// initialization. For example, it is not safe to use in - /// as that may be called during project initialization; attempting to do so will result in a deadlock. - /// - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface ISafeProjectGuidService - { - /// - /// Returns the project GUID, waiting until project load has safely progressed - /// to a point where the GUID is guaranteed not to change. - /// - /// - /// The GUID of the current project. - /// - /// - /// The project was unloaded before project load had finished. - /// - Task GetProjectGuidAsync(CancellationToken cancellationToken = default); - } + /// + /// The GUID of the current project. + /// + /// + /// The project was unloaded before project load had finished. + /// + Task GetProjectGuidAsync(CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISolutionService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISolutionService.cs index 45ee72df7c..3d6c221388 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISolutionService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ISolutionService.cs @@ -2,43 +2,42 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Operations and properties related to the solution. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private)] +internal interface ISolutionService { /// - /// Operations and properties related to the solution. + /// Gets a task that completes when the host recognizes that the solution is loaded. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private)] - internal interface ISolutionService + /// + /// Use if + /// within project context. + /// + /// + /// Thrown when host is closed without a solution being loaded. + /// + Task LoadedInHost { - /// - /// Gets a task that completes when the host recognizes that the solution is loaded. - /// - /// - /// Use if - /// within project context. - /// - /// - /// Thrown when host is closed without a solution being loaded. - /// - Task LoadedInHost - { - get; - } + get; + } - /// - /// Gets the VS solution object. - /// - /// - /// Must be called from the main thread. - /// - IVsSolution Solution { get; } + /// + /// Gets the VS solution object. + /// + /// + /// Must be called from the main thread. + /// + IVsSolution Solution { get; } - /// - /// Creates a new subscription for solution events that will call back via . - /// - /// The callback for events. - /// A token whose cancellation marks lost interest in the result of this task. - /// An object that unsubscribes when disposed. - Task SubscribeAsync(IVsSolutionEvents eventListener, CancellationToken cancellationToken = default); - } + /// + /// Creates a new subscription for solution events that will call back via . + /// + /// The callback for events. + /// A token whose cancellation marks lost interest in the result of this task. + /// An object that unsubscribes when disposed. + Task SubscribeAsync(IVsSolutionEvents eventListener, CancellationToken cancellationToken = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IUnconfiguredProjectCommonServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IUnconfiguredProjectCommonServices.cs index 3ee772e497..fa6dd68997 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IUnconfiguredProjectCommonServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/IUnconfiguredProjectCommonServices.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides access to common project services provided by the . +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IUnconfiguredProjectCommonServices { /// - /// Provides access to common project services provided by the . + /// Gets the for the current . /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IUnconfiguredProjectCommonServices - { - /// - /// Gets the for the current . - /// - IProjectThreadingService ThreadingService { get; } + IProjectThreadingService ThreadingService { get; } - /// - /// Gets the current . - /// - UnconfiguredProject Project { get; } + /// + /// Gets the current . + /// + UnconfiguredProject Project { get; } - /// - /// Gets the current active . - /// - ConfiguredProject ActiveConfiguredProject { get; } + /// + /// Gets the current active . + /// + ConfiguredProject ActiveConfiguredProject { get; } - /// - /// Gets the of the currently active configured project. - /// - ProjectProperties ActiveConfiguredProjectProperties { get; } + /// + /// Gets the of the currently active configured project. + /// + ProjectProperties ActiveConfiguredProjectProperties { get; } - /// - /// Gets the which provides access to MSBuild evaluation and construction models for a project. - /// - IProjectAccessor ProjectAccessor { get; } - } + /// + /// Gets the which provides access to MSBuild evaluation and construction models for a project. + /// + IProjectAccessor ProjectAccessor { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/AppDesignerFolderProjectImageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/AppDesignerFolderProjectImageProvider.cs index 0e45a998c9..944527c86f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/AppDesignerFolderProjectImageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/AppDesignerFolderProjectImageProvider.cs @@ -2,33 +2,32 @@ using Microsoft.VisualStudio.Imaging; -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +/// +/// Provides the AppDesigner folder image. +/// +[Export(typeof(IProjectImageProvider))] +[AppliesTo(ProjectCapability.AppDesigner)] +internal class AppDesignerFolderProjectImageProvider : IProjectImageProvider { - /// - /// Provides the AppDesigner folder image. - /// - [Export(typeof(IProjectImageProvider))] - [AppliesTo(ProjectCapability.AppDesigner)] - internal class AppDesignerFolderProjectImageProvider : IProjectImageProvider + private static readonly ProjectImageMoniker s_iconClosed = KnownMonikers.PropertiesFolderClosed.ToProjectSystemType(); + private static readonly ProjectImageMoniker s_iconOpened = KnownMonikers.PropertiesFolderOpen.ToProjectSystemType(); + + [ImportingConstructor] + public AppDesignerFolderProjectImageProvider() { - private static readonly ProjectImageMoniker s_iconClosed = KnownMonikers.PropertiesFolderClosed.ToProjectSystemType(); - private static readonly ProjectImageMoniker s_iconOpened = KnownMonikers.PropertiesFolderOpen.ToProjectSystemType(); + } - [ImportingConstructor] - public AppDesignerFolderProjectImageProvider() - { - } + public ProjectImageMoniker? GetProjectImage(string key) + { + Requires.NotNullOrEmpty(key); - public ProjectImageMoniker? GetProjectImage(string key) + return key switch { - Requires.NotNullOrEmpty(key); - - return key switch - { - ProjectImageKey.AppDesignerFolder => s_iconClosed, - ProjectImageKey.ExpandedAppDesignerFolder => s_iconOpened, - _ => null - }; - } + ProjectImageKey.AppDesignerFolder => s_iconClosed, + ProjectImageKey.ExpandedAppDesignerFolder => s_iconOpened, + _ => null + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/CSharp/CSharpProjectImageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/CSharp/CSharpProjectImageProvider.cs index 88a426eed4..5b63a11d35 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/CSharp/CSharpProjectImageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/CSharp/CSharpProjectImageProvider.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.CSharp; + +/// +/// Provides C# project images. +/// +[Export(typeof(IProjectImageProvider))] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpProjectImageProvider : IProjectImageProvider { - /// - /// Provides C# project images. - /// - [Export(typeof(IProjectImageProvider))] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpProjectImageProvider : IProjectImageProvider + [ImportingConstructor] + public CSharpProjectImageProvider() { - [ImportingConstructor] - public CSharpProjectImageProvider() - { - } + } - public ProjectImageMoniker? GetProjectImage(string key) - { - Requires.NotNullOrEmpty(key); + public ProjectImageMoniker? GetProjectImage(string key) + { + Requires.NotNullOrEmpty(key); - return key switch - { - ProjectImageKey.ProjectRoot => KnownProjectImageMonikers.CSProjectNode, - ProjectImageKey.SharedItemsImportFile or ProjectImageKey.SharedProjectRoot => KnownProjectImageMonikers.CSSharedProject, - _ => null - }; - } + return key switch + { + ProjectImageKey.ProjectRoot => KnownProjectImageMonikers.CSProjectNode, + ProjectImageKey.SharedItemsImportFile or ProjectImageKey.SharedProjectRoot => KnownProjectImageMonikers.CSSharedProject, + _ => null + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpProjectImageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpProjectImageProvider.cs index 4d2c43d7fe..ad89e4f10f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpProjectImageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpProjectImageProvider.cs @@ -2,27 +2,26 @@ using Microsoft.VisualStudio.Imaging; -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.FSharp; + +/// +/// Provides F# project images. +/// +[Export(typeof(IProjectImageProvider))] +[AppliesTo(ProjectCapability.FSharp)] +internal class FSharpProjectImageProvider : IProjectImageProvider { - /// - /// Provides F# project images. - /// - [Export(typeof(IProjectImageProvider))] - [AppliesTo(ProjectCapability.FSharp)] - internal class FSharpProjectImageProvider : IProjectImageProvider + [ImportingConstructor] + public FSharpProjectImageProvider() { - [ImportingConstructor] - public FSharpProjectImageProvider() - { - } + } - public ProjectImageMoniker? GetProjectImage(string key) - { - Requires.NotNullOrEmpty(key); + public ProjectImageMoniker? GetProjectImage(string key) + { + Requires.NotNullOrEmpty(key); - return key == ProjectImageKey.ProjectRoot - ? KnownMonikers.FSProjectNode.ToProjectSystemType() - : null; - } + return key == ProjectImageKey.ProjectRoot + ? KnownMonikers.FSProjectNode.ToProjectSystemType() + : null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpSourcesIconProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpSourcesIconProvider.cs index 426d13f035..a9502fb346 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpSourcesIconProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/FSharp/FSharpSourcesIconProvider.cs @@ -2,42 +2,41 @@ using Microsoft.VisualStudio.Imaging; -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.FSharp; + +[Export(typeof(IProjectTreePropertiesProvider))] +[AppliesTo(ProjectCapability.FSharp)] +[Order(Order.Default)] +internal class FSharpSourcesIconProvider : IProjectTreePropertiesProvider { - [Export(typeof(IProjectTreePropertiesProvider))] - [AppliesTo(ProjectCapability.FSharp)] - [Order(Order.Default)] - internal class FSharpSourcesIconProvider : IProjectTreePropertiesProvider + private static readonly Dictionary s_fileExtensionImageMap = new(StringComparers.Paths) { - private static readonly Dictionary s_fileExtensionImageMap = new(StringComparers.Paths) - { - { ".fs", KnownMonikers.FSFileNode.ToProjectSystemType() }, - { ".fsi", KnownMonikers.FSSignatureFile.ToProjectSystemType() }, - { ".fsx", KnownMonikers.FSScript.ToProjectSystemType() } - }; + { ".fs", KnownMonikers.FSFileNode.ToProjectSystemType() }, + { ".fsi", KnownMonikers.FSSignatureFile.ToProjectSystemType() }, + { ".fsx", KnownMonikers.FSScript.ToProjectSystemType() } + }; - public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + { + if (!propertyValues.Flags.Contains(ProjectTreeFlags.Common.Folder)) { - if (!propertyValues.Flags.Contains(ProjectTreeFlags.Common.Folder)) + if (!propertyValues.Flags.Contains(ProjectTreeFlags.Common.SourceFile | ProjectTreeFlags.Common.FileSystemEntity)) { - if (!propertyValues.Flags.Contains(ProjectTreeFlags.Common.SourceFile | ProjectTreeFlags.Common.FileSystemEntity)) - { - return; - } - - propertyValues.Icon = GetIconForItem(propertyContext.ItemName); + return; } + + propertyValues.Icon = GetIconForItem(propertyContext.ItemName); } + } - private static ProjectImageMoniker? GetIconForItem(string itemName) + private static ProjectImageMoniker? GetIconForItem(string itemName) + { + if (s_fileExtensionImageMap.TryGetValue(Path.GetExtension(itemName), out ProjectImageMoniker moniker)) { - if (s_fileExtensionImageMap.TryGetValue(Path.GetExtension(itemName), out ProjectImageMoniker moniker)) - { - return moniker; - } - - // Return null so VS can supply the default icons. - return null; + return moniker; } + + // Return null so VS can supply the default icons. + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/IProjectImageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/IProjectImageProvider.cs index e6ac940f2b..f62e8e128d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/IProjectImageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/IProjectImageProvider.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +/// +/// Provides project images given a specific key. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface IProjectImageProvider { /// - /// Provides project images given a specific key. + /// Returns the for the specified key, returning + /// if the provider does handle the specified key. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface IProjectImageProvider - { - /// - /// Returns the for the specified key, returning - /// if the provider does handle the specified key. - /// - ProjectImageMoniker? GetProjectImage(string key); - } + ProjectImageMoniker? GetProjectImage(string key); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ImageMonikerDebuggerDisplay.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ImageMonikerDebuggerDisplay.cs index 65c80f216b..88fa6152d4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ImageMonikerDebuggerDisplay.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ImageMonikerDebuggerDisplay.cs @@ -9,40 +9,39 @@ [assembly: DebuggerDisplay("{Microsoft.VisualStudio.ProjectSystem.Imaging.ImageMonikerDebuggerDisplay.FromImageMoniker(this)}", Target = typeof(ImageMoniker))] [assembly: DebuggerDisplay("{Microsoft.VisualStudio.ProjectSystem.Imaging.ImageMonikerDebuggerDisplay.FromProjectImageMoniker(this)}", Target = typeof(ProjectImageMoniker))] -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +/// +/// Provides a friendly display for and instances +/// based on the well known values from . +/// +internal static class ImageMonikerDebuggerDisplay { - /// - /// Provides a friendly display for and instances - /// based on the well known values from . - /// - internal static class ImageMonikerDebuggerDisplay - { - private static readonly Dictionary s_displayNames = CalculateDisplayNames(); + private static readonly Dictionary s_displayNames = CalculateDisplayNames(); - internal static string FromImageMoniker(ImageMoniker moniker) => DebugDisplay(moniker.Guid, moniker.Id); + internal static string FromImageMoniker(ImageMoniker moniker) => DebugDisplay(moniker.Guid, moniker.Id); - internal static string FromProjectImageMoniker(ProjectImageMoniker moniker) => DebugDisplay(moniker.Guid, moniker.Id); + internal static string FromProjectImageMoniker(ProjectImageMoniker moniker) => DebugDisplay(moniker.Guid, moniker.Id); - private static string DebugDisplay(Guid guid, int id) + private static string DebugDisplay(Guid guid, int id) + { + if (guid == KnownImageIds.ImageCatalogGuid && + s_displayNames.TryGetValue(id, out string displayName)) { - if (guid == KnownImageIds.ImageCatalogGuid && - s_displayNames.TryGetValue(id, out string displayName)) - { - return displayName; - } - - return $"{guid} ({id})"; + return displayName; } - private static Dictionary CalculateDisplayNames() - { - Type type = typeof(KnownImageIds); + return $"{guid} ({id})"; + } - return type.GetFields() - .Where(field => field.IsLiteral && field.FieldType == typeof(int)) - .ToDictionary(field => - (int)field.GetRawConstantValue(), - field => $"{type.Name}.{field.Name}"); - } + private static Dictionary CalculateDisplayNames() + { + Type type = typeof(KnownImageIds); + + return type.GetFields() + .Where(field => field.IsLiteral && field.FieldType == typeof(int)) + .ToDictionary(field => + (int)field.GetRawConstantValue(), + field => $"{type.Name}.{field.Name}"); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageKey.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageKey.cs index 5fa02caff1..ceda700cf4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageKey.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageKey.cs @@ -1,35 +1,34 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +/// +/// Provides common well-known keys. +/// +internal static class ProjectImageKey { /// - /// Provides common well-known keys. + /// Represents the image key for the root of a project hierarchy. /// - internal static class ProjectImageKey - { - /// - /// Represents the image key for the root of a project hierarchy. - /// - public const string ProjectRoot = nameof(ProjectRoot); + public const string ProjectRoot = nameof(ProjectRoot); - /// - /// Represents the image key for the root of a shared project hierarchy. - /// - public const string SharedProjectRoot = nameof(SharedProjectRoot); + /// + /// Represents the image key for the root of a shared project hierarchy. + /// + public const string SharedProjectRoot = nameof(SharedProjectRoot); - /// - /// Represents the image key for the Shared.items file that is imported into this project in order to add a shared folder. - /// - public const string SharedItemsImportFile = nameof(SharedItemsImportFile); + /// + /// Represents the image key for the Shared.items file that is imported into this project in order to add a shared folder. + /// + public const string SharedItemsImportFile = nameof(SharedItemsImportFile); - /// - /// Represents the image key for the AppDesigner folder (called "Properties" in C# and "My Project" in VB) when it is closed. - /// - public const string AppDesignerFolder = nameof(AppDesignerFolder); + /// + /// Represents the image key for the AppDesigner folder (called "Properties" in C# and "My Project" in VB) when it is closed. + /// + public const string AppDesignerFolder = nameof(AppDesignerFolder); - /// - /// Represents the image key for the AppDesigner folder (called "Properties" in C# and "My Project" in VB) when it is expanded. - /// - public const string ExpandedAppDesignerFolder = nameof(ExpandedAppDesignerFolder); - } + /// + /// Represents the image key for the AppDesigner folder (called "Properties" in C# and "My Project" in VB) when it is expanded. + /// + public const string ExpandedAppDesignerFolder = nameof(ExpandedAppDesignerFolder); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageProviderAggregator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageProviderAggregator.cs index 3b32dd4a06..8a5a20dbc4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageProviderAggregator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/ProjectImageProviderAggregator.cs @@ -2,41 +2,40 @@ using Microsoft.VisualStudio.Composition; -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +/// +/// Aggregates instances into a single importable +/// . +/// +[Export] +[AppliesTo(ProjectCapability.DotNet)] +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal class ProjectImageProviderAggregator : IProjectImageProvider { - /// - /// Aggregates instances into a single importable - /// . - /// - [Export] - [AppliesTo(ProjectCapability.DotNet)] - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal class ProjectImageProviderAggregator : IProjectImageProvider + [ImportingConstructor] + public ProjectImageProviderAggregator(UnconfiguredProject project) { - [ImportingConstructor] - public ProjectImageProviderAggregator(UnconfiguredProject project) - { - ImageProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); - } + ImageProviders = new OrderPrecedenceImportCollection(projectCapabilityCheckProvider: project); + } - [ImportMany] - public OrderPrecedenceImportCollection ImageProviders { get; } + [ImportMany] + public OrderPrecedenceImportCollection ImageProviders { get; } - public ProjectImageMoniker? GetProjectImage(string key) + public ProjectImageMoniker? GetProjectImage(string key) + { + Requires.NotNullOrEmpty(key); + + foreach (Lazy provider in ImageProviders) { - Requires.NotNullOrEmpty(key); + ProjectImageMoniker? image = provider.Value.GetProjectImage(key); - foreach (Lazy provider in ImageProviders) + if (image is not null) { - ProjectImageMoniker? image = provider.Value.GetProjectImage(key); - - if (image is not null) - { - return image; - } + return image; } - - return null; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProvider.cs index 130d50ed59..6e87b9646c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProvider.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.VisualBasic; + +/// +/// Provides Visual Basic project images. +/// +[Export(typeof(IProjectImageProvider))] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicProjectImageProvider : IProjectImageProvider { - /// - /// Provides Visual Basic project images. - /// - [Export(typeof(IProjectImageProvider))] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicProjectImageProvider : IProjectImageProvider + [ImportingConstructor] + public VisualBasicProjectImageProvider() { - [ImportingConstructor] - public VisualBasicProjectImageProvider() - { - } + } - public ProjectImageMoniker? GetProjectImage(string key) - { - Requires.NotNullOrEmpty(key); + public ProjectImageMoniker? GetProjectImage(string key) + { + Requires.NotNullOrEmpty(key); - return key switch - { - ProjectImageKey.ProjectRoot => KnownProjectImageMonikers.VBProjectNode, - ProjectImageKey.SharedItemsImportFile or ProjectImageKey.SharedProjectRoot => KnownProjectImageMonikers.VBSharedProject, - _ => null - }; - } + return key switch + { + ProjectImageKey.ProjectRoot => KnownProjectImageMonikers.VBProjectNode, + ProjectImageKey.SharedItemsImportFile or ProjectImageKey.SharedProjectRoot => KnownProjectImageMonikers.VBSharedProject, + _ => null + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractProjectCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractProjectCommand.cs index fe9c435b4b..2bb1789605 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractProjectCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractProjectCommand.cs @@ -2,58 +2,57 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Input +namespace Microsoft.VisualStudio.ProjectSystem.Input; + +/// +/// Provides the base class for all commands that operate on nodes. +/// +internal abstract class AbstractProjectCommand : IAsyncCommandGroupHandler { - /// - /// Provides the base class for all commands that operate on nodes. - /// - internal abstract class AbstractProjectCommand : IAsyncCommandGroupHandler + private readonly Lazy _commandIds; + + protected AbstractProjectCommand() { - private readonly Lazy _commandIds; + _commandIds = new Lazy(() => GetCommandIds(this)); + } - protected AbstractProjectCommand() - { - _commandIds = new Lazy(() => GetCommandIds(this)); - } + public Task GetCommandStatusAsync(IImmutableSet nodes, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + { + Requires.NotNull(nodes); - public Task GetCommandStatusAsync(IImmutableSet nodes, long commandId, bool focused, string? commandText, CommandStatus progressiveStatus) + foreach (long otherCommandId in _commandIds.Value) { - Requires.NotNull(nodes); - - foreach (long otherCommandId in _commandIds.Value) - { - if (otherCommandId == commandId) - return GetCommandStatusAsync(nodes, focused, commandText, progressiveStatus); - } - - return GetCommandStatusResult.Unhandled; + if (otherCommandId == commandId) + return GetCommandStatusAsync(nodes, focused, commandText, progressiveStatus); } - public Task TryHandleCommandAsync(IImmutableSet nodes, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - Requires.NotNull(nodes); + return GetCommandStatusResult.Unhandled; + } - foreach (long otherCommandId in _commandIds.Value) - { - if (otherCommandId == commandId) - return TryHandleCommandAsync(nodes, focused, commandExecuteOptions, variantArgIn, variantArgOut); - } + public Task TryHandleCommandAsync(IImmutableSet nodes, long commandId, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + Requires.NotNull(nodes); - return TaskResult.False; + foreach (long otherCommandId in _commandIds.Value) + { + if (otherCommandId == commandId) + return TryHandleCommandAsync(nodes, focused, commandExecuteOptions, variantArgIn, variantArgOut); } - protected abstract Task GetCommandStatusAsync(IImmutableSet nodes, bool focused, string? commandText, CommandStatus progressiveStatus); + return TaskResult.False; + } + + protected abstract Task GetCommandStatusAsync(IImmutableSet nodes, bool focused, string? commandText, CommandStatus progressiveStatus); - protected abstract Task TryHandleCommandAsync(IImmutableSet nodes, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut); + protected abstract Task TryHandleCommandAsync(IImmutableSet nodes, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut); - private static long[] GetCommandIds(AbstractProjectCommand command) - { - var attribute = (ProjectCommandAttribute?)Attribute.GetCustomAttribute(command.GetType(), typeof(ProjectCommandAttribute)); + private static long[] GetCommandIds(AbstractProjectCommand command) + { + var attribute = (ProjectCommandAttribute?)Attribute.GetCustomAttribute(command.GetType(), typeof(ProjectCommandAttribute)); - // All ProjectCommand's should be marked with [ProjectCommandAttribute] - Assumes.NotNull(attribute); + // All ProjectCommand's should be marked with [ProjectCommandAttribute] + Assumes.NotNull(attribute); - return attribute.CommandIds; - } + return attribute.CommandIds; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractSingleNodeProjectCommand.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractSingleNodeProjectCommand.cs index 96c387007e..0b3e0c3383 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractSingleNodeProjectCommand.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/AbstractSingleNodeProjectCommand.cs @@ -2,35 +2,34 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Input +namespace Microsoft.VisualStudio.ProjectSystem.Input; + +/// +/// Provides the base class for all commands that handle a single node. +/// +internal abstract class AbstractSingleNodeProjectCommand : AbstractProjectCommand { - /// - /// Provides the base class for all commands that handle a single node. - /// - internal abstract class AbstractSingleNodeProjectCommand : AbstractProjectCommand + protected sealed override Task GetCommandStatusAsync(IImmutableSet nodes, bool focused, string? commandText, CommandStatus progressiveStatus) { - protected sealed override Task GetCommandStatusAsync(IImmutableSet nodes, bool focused, string? commandText, CommandStatus progressiveStatus) + if (nodes.Count == 1) { - if (nodes.Count == 1) - { - return GetCommandStatusAsync(nodes.First(), focused, commandText, progressiveStatus); - } - - return GetCommandStatusResult.Unhandled; + return GetCommandStatusAsync(nodes.First(), focused, commandText, progressiveStatus); } - protected sealed override Task TryHandleCommandAsync(IImmutableSet nodes, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) - { - if (nodes.Count == 1) - { - return TryHandleCommandAsync(nodes.First(), focused, commandExecuteOptions, variantArgIn, variantArgOut); - } + return GetCommandStatusResult.Unhandled; + } - return TaskResult.False; + protected sealed override Task TryHandleCommandAsync(IImmutableSet nodes, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut) + { + if (nodes.Count == 1) + { + return TryHandleCommandAsync(nodes.First(), focused, commandExecuteOptions, variantArgIn, variantArgOut); } - protected abstract Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus); - - protected abstract Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut); + return TaskResult.False; } + + protected abstract Task GetCommandStatusAsync(IProjectTree node, bool focused, string? commandText, CommandStatus progressiveStatus); + + protected abstract Task TryHandleCommandAsync(IProjectTree node, bool focused, long commandExecuteOptions, IntPtr variantArgIn, IntPtr variantArgOut); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/GetCommandStatusResult.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/GetCommandStatusResult.cs index 3e94ad433b..214a97a7c7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/GetCommandStatusResult.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/GetCommandStatusResult.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Input +namespace Microsoft.VisualStudio.ProjectSystem.Input; + +internal static class GetCommandStatusResult { - internal static class GetCommandStatusResult - { - public static Task Unhandled { get; } = CommandStatusResult.Unhandled.AsTask(); + public static Task Unhandled { get; } = CommandStatusResult.Unhandled.AsTask(); - public static Task Suppressed { get; } = Task.FromResult(new CommandStatusResult(handled: true, null, CommandStatus.NotSupported | CommandStatus.Invisible)); + public static Task Suppressed { get; } = Task.FromResult(new CommandStatusResult(handled: true, null, CommandStatus.NotSupported | CommandStatus.Invisible)); - public static Task Handled(string? commandText, CommandStatus progressiveStatus) - { - return new CommandStatusResult(true, commandText, progressiveStatus | CommandStatus.Supported).AsTask(); - } + public static Task Handled(string? commandText, CommandStatus progressiveStatus) + { + return new CommandStatusResult(true, commandText, progressiveStatus | CommandStatus.Supported).AsTask(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/ProjectCommandAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/ProjectCommandAttribute.cs index 8fb6880f57..5ce97ceeba 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/ProjectCommandAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Input/ProjectCommandAttribute.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Input +namespace Microsoft.VisualStudio.ProjectSystem.Input; + +/// +/// Specifies the command group and ID of a given . +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface, AllowMultiple = false)] +[MetadataAttribute] +internal class ProjectCommandAttribute : ExportAttribute { - /// - /// Specifies the command group and ID of a given . - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface, AllowMultiple = false)] - [MetadataAttribute] - internal class ProjectCommandAttribute : ExportAttribute + public ProjectCommandAttribute(string group, long commandId) + : this(group, new long[] { commandId }) { - public ProjectCommandAttribute(string group, long commandId) - : this(group, new long[] { commandId }) - { - } + } - public ProjectCommandAttribute(string group, params long[] commandIds) - : base(typeof(IAsyncCommandGroupHandler)) - { - Group = new Guid(group); - CommandIds = commandIds; - } + public ProjectCommandAttribute(string group, params long[] commandIds) + : base(typeof(IAsyncCommandGroupHandler)) + { + Group = new Guid(group); + CommandIds = commandIds; + } - public long[] CommandIds { get; } + public long[] CommandIds { get; } - public Guid Group { get; } - } + public Guid Group { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ActiveEditorContextTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ActiveEditorContextTracker.cs index e920feb8bc..d88db046a6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ActiveEditorContextTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ActiveEditorContextTracker.cs @@ -3,64 +3,63 @@ using Microsoft.VisualStudio.ProjectSystem.Utilities; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Provides an implementation of that tracks the "active" context for the editor. +/// +[Export(typeof(IActiveEditorContextTracker))] +[AppliesTo(ProjectCapability.DotNetLanguageService)] +internal class ActiveEditorContextTracker : IActiveEditorContextTracker { - /// - /// Provides an implementation of that tracks the "active" context for the editor. - /// - [Export(typeof(IActiveEditorContextTracker))] - [AppliesTo(ProjectCapability.DotNetLanguageService)] - internal class ActiveEditorContextTracker : IActiveEditorContextTracker + private ImmutableList _contexts = ImmutableList.Empty; + private string? _activeIntellisenseProjectContext; + + // UnconfiguredProject is only included for scoping reasons. + [ImportingConstructor] + public ActiveEditorContextTracker(UnconfiguredProject? project) { - private ImmutableList _contexts = ImmutableList.Empty; - private string? _activeIntellisenseProjectContext; + } - // UnconfiguredProject is only included for scoping reasons. - [ImportingConstructor] - public ActiveEditorContextTracker(UnconfiguredProject? project) - { - } + public string? ActiveIntellisenseProjectContext + { + get { return _activeIntellisenseProjectContext ?? _contexts.FirstOrDefault(); } + set { _activeIntellisenseProjectContext = value; } + } - public string? ActiveIntellisenseProjectContext - { - get { return _activeIntellisenseProjectContext ?? _contexts.FirstOrDefault(); } - set { _activeIntellisenseProjectContext = value; } - } + public bool IsActiveEditorContext(string contextId) + { + Requires.NotNullOrEmpty(contextId); - public bool IsActiveEditorContext(string contextId) + if (!_contexts.Contains(contextId)) { - Requires.NotNullOrEmpty(contextId); - - if (!_contexts.Contains(contextId)) - { - throw new InvalidOperationException($"Context with ID '{contextId}' has not been registered or has already been unregistered."); - } - - return StringComparers.WorkspaceProjectContextIds.Equals(ActiveIntellisenseProjectContext, contextId); + throw new InvalidOperationException($"Context with ID '{contextId}' has not been registered or has already been unregistered."); } - public IDisposable RegisterContext(string contextId) - { - Requires.NotNullOrEmpty(contextId); - - bool changed = ThreadingTools.ApplyChangeOptimistically(ref _contexts, projectContexts => - { - if (!projectContexts.Contains(contextId)) - { - projectContexts = projectContexts.Add(contextId); - } + return StringComparers.WorkspaceProjectContextIds.Equals(ActiveIntellisenseProjectContext, contextId); + } - return projectContexts; - }); + public IDisposable RegisterContext(string contextId) + { + Requires.NotNullOrEmpty(contextId); - if (!changed) + bool changed = ThreadingTools.ApplyChangeOptimistically(ref _contexts, projectContexts => + { + if (!projectContexts.Contains(contextId)) { - throw new InvalidOperationException($"Context with ID '{contextId}' has already been registered."); + projectContexts = projectContexts.Add(contextId); } - return new DisposableDelegate(() => - Assumes.True(ThreadingTools.ApplyChangeOptimistically(ref _contexts, contextId, static (projectContexts, contextId) => - projectContexts.Remove(contextId)))); + return projectContexts; + }); + + if (!changed) + { + throw new InvalidOperationException($"Context with ID '{contextId}' has already been registered."); } + + return new DisposableDelegate(() => + Assumes.True(ThreadingTools.ApplyChangeOptimistically(ref _contexts, contextId, static (projectContexts, contextId) => + projectContexts.Remove(contextId)))); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/BuildOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/BuildOptions.cs index 16047a23cd..cb1d8e0a5c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/BuildOptions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/BuildOptions.cs @@ -2,44 +2,43 @@ using Microsoft.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal class BuildOptions { - internal class BuildOptions + public BuildOptions( + ImmutableArray sourceFiles, + ImmutableArray additionalFiles, + ImmutableArray metadataReferences, + ImmutableArray analyzerReferences, + ImmutableArray analyzerConfigFiles) + { + SourceFiles = sourceFiles; + AdditionalFiles = additionalFiles; + MetadataReferences = metadataReferences; + AnalyzerReferences = analyzerReferences; + AnalyzerConfigFiles = analyzerConfigFiles; + } + + public ImmutableArray SourceFiles { get; } + + public ImmutableArray AdditionalFiles { get; } + + public ImmutableArray MetadataReferences { get; } + + public ImmutableArray AnalyzerReferences { get; } + + public ImmutableArray AnalyzerConfigFiles { get; } + + public static BuildOptions FromCommandLineArguments(CommandLineArguments commandLineArguments) { - public BuildOptions( - ImmutableArray sourceFiles, - ImmutableArray additionalFiles, - ImmutableArray metadataReferences, - ImmutableArray analyzerReferences, - ImmutableArray analyzerConfigFiles) - { - SourceFiles = sourceFiles; - AdditionalFiles = additionalFiles; - MetadataReferences = metadataReferences; - AnalyzerReferences = analyzerReferences; - AnalyzerConfigFiles = analyzerConfigFiles; - } - - public ImmutableArray SourceFiles { get; } - - public ImmutableArray AdditionalFiles { get; } - - public ImmutableArray MetadataReferences { get; } - - public ImmutableArray AnalyzerReferences { get; } - - public ImmutableArray AnalyzerConfigFiles { get; } - - public static BuildOptions FromCommandLineArguments(CommandLineArguments commandLineArguments) - { - Requires.NotNull(commandLineArguments); - - return new BuildOptions( - commandLineArguments.SourceFiles, - commandLineArguments.AdditionalFiles, - commandLineArguments.MetadataReferences, - commandLineArguments.AnalyzerReferences, - commandLineArguments.AnalyzerConfigPaths); - } + Requires.NotNull(commandLineArguments); + + return new BuildOptions( + commandLineArguments.SourceFiles, + commandLineArguments.AdditionalFiles, + commandLineArguments.MetadataReferences, + commandLineArguments.AnalyzerReferences, + commandLineArguments.AnalyzerConfigPaths); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserService.cs index ee3bf82a53..12597c2ac5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserService.cs @@ -2,19 +2,18 @@ using Microsoft.CodeAnalysis.CSharp; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp; + +[Export(typeof(ICommandLineParserService))] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpCommandLineParserService : ICommandLineParserService { - [Export(typeof(ICommandLineParserService))] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpCommandLineParserService : ICommandLineParserService + public BuildOptions Parse(IEnumerable arguments, string baseDirectory) { - public BuildOptions Parse(IEnumerable arguments, string baseDirectory) - { - Requires.NotNull(arguments); - Requires.NotNullOrEmpty(baseDirectory); + Requires.NotNull(arguments); + Requires.NotNullOrEmpty(baseDirectory); - return BuildOptions.FromCommandLineArguments( - CSharpCommandLineParser.Default.Parse(arguments, baseDirectory, sdkDirectory: null, additionalReferenceDirectories: null)); - } + return BuildOptions.FromCommandLineArguments( + CSharpCommandLineParser.Default.Parse(arguments, baseDirectory, sdkDirectory: null, additionalReferenceDirectories: null)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsService.cs index ac0f155e64..be881fff16 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsService.cs @@ -2,21 +2,20 @@ using Microsoft.CodeAnalysis.CSharp; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp; + +[Export(typeof(ISyntaxFactsService))] +[Order(Order.Default)] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpSyntaxFactsService : ISyntaxFactsService { - [Export(typeof(ISyntaxFactsService))] - [Order(Order.Default)] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpSyntaxFactsService : ISyntaxFactsService + [ImportingConstructor] + public CSharpSyntaxFactsService() { - [ImportingConstructor] - public CSharpSyntaxFactsService() - { - } + } - public bool IsValidIdentifier(string identifierName) - { - return SyntaxFacts.IsValidIdentifier(identifierName); - } + public bool IsValidIdentifier(string identifierName) + { + return SyntaxFacts.IsValidIdentifier(identifierName); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ContextState.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ContextState.cs index 9721231491..cdb9c72ff7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ContextState.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ContextState.cs @@ -1,35 +1,34 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal readonly struct ContextState { - internal readonly struct ContextState + public ContextState(bool isActiveEditorContext, bool isActiveConfiguration) { - public ContextState(bool isActiveEditorContext, bool isActiveConfiguration) - { - IsActiveEditorContext = isActiveEditorContext; - IsActiveConfiguration = isActiveConfiguration; - } + IsActiveEditorContext = isActiveEditorContext; + IsActiveConfiguration = isActiveConfiguration; + } - /// - /// Gets a value indicating whether the related language service project serves as the active "context" - /// for the editor. - /// - /// - /// The "active" context for the editor is the one that Roslyn uses to drive IntelliSense, refactorings - /// and code fixes. This is typically controlled by the user via the project drop down in the top-left - /// of the editor, but can be changed in reaction to other factors. - /// - public bool IsActiveEditorContext { get; } + /// + /// Gets a value indicating whether the related language service project serves as the active "context" + /// for the editor. + /// + /// + /// The "active" context for the editor is the one that Roslyn uses to drive IntelliSense, refactorings + /// and code fixes. This is typically controlled by the user via the project drop down in the top-left + /// of the editor, but can be changed in reaction to other factors. + /// + public bool IsActiveEditorContext { get; } - /// - /// Gets a value indicating whether the related language service project is context in the active - /// configuration for a project. - /// - /// - /// The context in the active configuration for the project is the one that Roslyn uses to analyze types - /// to push "SubType" metadata of source files to the project system. This avoids having conflicting - /// metadata between contexts of the same source file in multi-targeted projects. - /// - public bool IsActiveConfiguration { get; } - } + /// + /// Gets a value indicating whether the related language service project is context in the active + /// configuration for a project. + /// + /// + /// The context in the active configuration for the project is the one that Roslyn uses to analyze types + /// to push "SubType" metadata of source files to the project system. This avoids having conflicting + /// metadata between contexts of the same source file in multi-targeted projects. + /// + public bool IsActiveConfiguration { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpBuildOptions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpBuildOptions.cs index 93c3ae2ea2..2d075c6ef2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpBuildOptions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpBuildOptions.cs @@ -2,20 +2,19 @@ using Microsoft.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.FSharp; + +internal class FSharpBuildOptions : BuildOptions { - internal class FSharpBuildOptions : BuildOptions + public FSharpBuildOptions(ImmutableArray sourceFiles, + ImmutableArray additionalFiles, + ImmutableArray metadataReferences, + ImmutableArray analyzerReferences, + ImmutableArray compileOptions) + : base(sourceFiles, additionalFiles, metadataReferences, analyzerReferences, ImmutableArray.Empty) { - public FSharpBuildOptions(ImmutableArray sourceFiles, - ImmutableArray additionalFiles, - ImmutableArray metadataReferences, - ImmutableArray analyzerReferences, - ImmutableArray compileOptions) - : base(sourceFiles, additionalFiles, metadataReferences, analyzerReferences, ImmutableArray.Empty) - { - CompileOptions = compileOptions; - } - - public ImmutableArray CompileOptions { get; } + CompileOptions = compileOptions; } + + public ImmutableArray CompileOptions { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserService.cs index 9ceefb009f..d8a398f22e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserService.cs @@ -3,95 +3,94 @@ using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.FSharp; + +[Export(typeof(ICommandLineParserService))] +[AppliesTo(ProjectCapability.FSharp)] +internal class FSharpCommandLineParserService : ICommandLineParserService { - [Export(typeof(ICommandLineParserService))] - [AppliesTo(ProjectCapability.FSharp)] - internal class FSharpCommandLineParserService : ICommandLineParserService - { - private const string HyphenReferencePrefix = "-r:"; - private const string SlashReferencePrefix = "/r:"; - private const string LongReferencePrefix = "--reference:"; + private const string HyphenReferencePrefix = "-r:"; + private const string SlashReferencePrefix = "/r:"; + private const string LongReferencePrefix = "--reference:"; - [ImportMany] - private readonly IEnumerable, ImmutableArray, ImmutableArray>> _handlers = null!; + [ImportMany] + private readonly IEnumerable, ImmutableArray, ImmutableArray>> _handlers = null!; - [ImportingConstructor] - public FSharpCommandLineParserService() - { - } + [ImportingConstructor] + public FSharpCommandLineParserService() + { + } - public BuildOptions Parse(IEnumerable arguments, string baseDirectory) - { - Requires.NotNull(arguments); - Requires.NotNullOrEmpty(baseDirectory); + public BuildOptions Parse(IEnumerable arguments, string baseDirectory) + { + Requires.NotNull(arguments); + Requires.NotNullOrEmpty(baseDirectory); - var sourceFiles = new List(); - var metadataReferences = new List(); - var commandLineOptions = new List(); + var sourceFiles = new List(); + var metadataReferences = new List(); + var commandLineOptions = new List(); - foreach (string commandLineArgument in arguments) + foreach (string commandLineArgument in arguments) + { + foreach (string arg in new LazyStringSplit(commandLineArgument, ';')) { - foreach (string arg in new LazyStringSplit(commandLineArgument, ';')) + if (arg.StartsWith(HyphenReferencePrefix)) { - if (arg.StartsWith(HyphenReferencePrefix)) - { - // e.g., /r:C:\Path\To\FSharp.Core.dll - metadataReferences.Add(new CommandLineReference(arg.Substring(HyphenReferencePrefix.Length), MetadataReferenceProperties.Assembly)); - } - else if (arg.StartsWith(SlashReferencePrefix)) - { - // e.g., -r:C:\Path\To\FSharp.Core.dll - metadataReferences.Add(new CommandLineReference(arg.Substring(SlashReferencePrefix.Length), MetadataReferenceProperties.Assembly)); - } - else if (arg.StartsWith(LongReferencePrefix)) - { - // e.g., --reference:C:\Path\To\FSharp.Core.dll - metadataReferences.Add(new CommandLineReference(arg.Substring(LongReferencePrefix.Length), MetadataReferenceProperties.Assembly)); - } - else if (!(arg.StartsWith("-") || arg.StartsWith("/"))) - { - // not an option, should be a regular file - string extension = Path.GetExtension(arg).ToLowerInvariant(); - if (extension is - ".fs" or - ".fsi" or - ".fsx" or - ".fsscript" or - ".ml" or - ".mli") - { - sourceFiles.Add(new CommandLineSourceFile(arg, isScript: (extension == ".fsx") || (extension == ".fsscript"))); - } - } - else + // e.g., /r:C:\Path\To\FSharp.Core.dll + metadataReferences.Add(new CommandLineReference(arg.Substring(HyphenReferencePrefix.Length), MetadataReferenceProperties.Assembly)); + } + else if (arg.StartsWith(SlashReferencePrefix)) + { + // e.g., -r:C:\Path\To\FSharp.Core.dll + metadataReferences.Add(new CommandLineReference(arg.Substring(SlashReferencePrefix.Length), MetadataReferenceProperties.Assembly)); + } + else if (arg.StartsWith(LongReferencePrefix)) + { + // e.g., --reference:C:\Path\To\FSharp.Core.dll + metadataReferences.Add(new CommandLineReference(arg.Substring(LongReferencePrefix.Length), MetadataReferenceProperties.Assembly)); + } + else if (!(arg.StartsWith("-") || arg.StartsWith("/"))) + { + // not an option, should be a regular file + string extension = Path.GetExtension(arg).ToLowerInvariant(); + if (extension is + ".fs" or + ".fsi" or + ".fsx" or + ".fsscript" or + ".ml" or + ".mli") { - // Neither a reference, nor a source file - commandLineOptions.Add(arg); + sourceFiles.Add(new CommandLineSourceFile(arg, isScript: (extension == ".fsx") || (extension == ".fsscript"))); } } + else + { + // Neither a reference, nor a source file + commandLineOptions.Add(arg); + } } - - return new FSharpBuildOptions( - sourceFiles: sourceFiles.ToImmutableArray(), - additionalFiles: [], - metadataReferences: metadataReferences.ToImmutableArray(), - analyzerReferences: [], - compileOptions: commandLineOptions.ToImmutableArray()); } - [Export] - [AppliesTo(ProjectCapability.FSharp)] - public void HandleCommandLineNotifications(string binPath, BuildOptions added, BuildOptions removed) - { - // NOTE this method is imported in CommandLineNotificationHandler as an Action + return new FSharpBuildOptions( + sourceFiles: sourceFiles.ToImmutableArray(), + additionalFiles: [], + metadataReferences: metadataReferences.ToImmutableArray(), + analyzerReferences: [], + compileOptions: commandLineOptions.ToImmutableArray()); + } + + [Export] + [AppliesTo(ProjectCapability.FSharp)] + public void HandleCommandLineNotifications(string binPath, BuildOptions added, BuildOptions removed) + { + // NOTE this method is imported in CommandLineNotificationHandler as an Action - if (added is FSharpBuildOptions fscAdded) + if (added is FSharpBuildOptions fscAdded) + { + foreach (Action, ImmutableArray, ImmutableArray> handler in _handlers) { - foreach (Action, ImmutableArray, ImmutableArray> handler in _handlers) - { - handler?.Invoke(binPath, fscAdded.SourceFiles, fscAdded.MetadataReferences, fscAdded.CompileOptions); - } + handler?.Invoke(binPath, fscAdded.SourceFiles, fscAdded.MetadataReferences, fscAdded.CompileOptions); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/IActiveEditorContextTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/IActiveEditorContextTracker.cs index a1f716b3f6..5c39430408 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/IActiveEditorContextTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/IActiveEditorContextTracker.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Tracks the "active" language service project for the editor within an . +/// +/// +/// +/// The "active" context for the editor is the one that Roslyn uses to drive IntelliSense, refactorings +/// and code fixes. This is typically controlled by the user via the project drop down in the top-left +/// of the editor, but can be changed in reaction to other factors. +/// +/// +/// NOTE: This is distinct from the "active" context for an . +/// +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IActiveEditorContextTracker { /// - /// Tracks the "active" language service project for the editor within an . + /// Gets or sets the current shared item context intellisense project name. Can be null. /// - /// - /// - /// The "active" context for the editor is the one that Roslyn uses to drive IntelliSense, refactorings - /// and code fixes. This is typically controlled by the user via the project drop down in the top-left - /// of the editor, but can be changed in reaction to other factors. - /// - /// - /// NOTE: This is distinct from the "active" context for an . - /// - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IActiveEditorContextTracker - { - /// - /// Gets or sets the current shared item context intellisense project name. Can be null. - /// - string? ActiveIntellisenseProjectContext { get; set; } + string? ActiveIntellisenseProjectContext { get; set; } - /// - /// Returns a value indicating whether the specified language service project is the active one for the editor. - /// - /// - /// is - /// - /// - /// is an empty string (""). - /// - /// - /// has not been registered or has already been unregistered. - /// - bool IsActiveEditorContext(string contextId); + /// + /// Returns a value indicating whether the specified language service project is the active one for the editor. + /// + /// + /// is + /// + /// + /// is an empty string (""). + /// + /// + /// has not been registered or has already been unregistered. + /// + bool IsActiveEditorContext(string contextId); - /// - /// Registers the language service project with the tracker. - /// - /// - /// An object that, when disposed, unregisters the context. - /// - /// - /// is . - /// - /// - /// is an empty string (""). - /// - /// - /// has already been registered. - /// - IDisposable RegisterContext(string contextId); - } + /// + /// Registers the language service project with the tracker. + /// + /// + /// An object that, when disposed, unregisters the context. + /// + /// + /// is . + /// + /// + /// is an empty string (""). + /// + /// + /// has already been registered. + /// + IDisposable RegisterContext(string contextId); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ICommandLineParserService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ICommandLineParserService.cs index b612a123c7..3ff8378685 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ICommandLineParserService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ICommandLineParserService.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// Parses instances from string-based command-line arguments. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface ICommandLineParserService { /// - /// Parses instances from string-based command-line arguments. + /// Parses the specified string-based command-line arguments. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface ICommandLineParserService - { - /// - /// Parses the specified string-based command-line arguments. - /// - /// - /// A of representing the individual command-line - /// arguments. - /// - /// - /// A containing the base directory used for qualifying file locations. - /// - /// - /// An representing the result. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - /// - /// is an empty string (""). - /// - BuildOptions Parse(IEnumerable arguments, string baseDirectory); - } + /// + /// A of representing the individual command-line + /// arguments. + /// + /// + /// A containing the base directory used for qualifying file locations. + /// + /// + /// An representing the result. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is an empty string (""). + /// + BuildOptions Parse(IEnumerable arguments, string baseDirectory); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ISyntaxFactsService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ISyntaxFactsService.cs index 2010c4e0a7..2eaee1998c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ISyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/ISyntaxFactsService.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +/// +/// A Language service for Unconfigured Projects that can answer questions about language syntax +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +internal interface ISyntaxFactsService { - /// - /// A Language service for Unconfigured Projects that can answer questions about language syntax - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - internal interface ISyntaxFactsService - { - bool IsValidIdentifier(string identifierName); - } + bool IsValidIdentifier(string identifierName); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserService.cs index 1106ede12a..41deacf1f9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserService.cs @@ -2,19 +2,18 @@ using Microsoft.CodeAnalysis.VisualBasic; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic; + +[Export(typeof(ICommandLineParserService))] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicCommandLineParserService : ICommandLineParserService { - [Export(typeof(ICommandLineParserService))] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicCommandLineParserService : ICommandLineParserService + public BuildOptions Parse(IEnumerable arguments, string baseDirectory) { - public BuildOptions Parse(IEnumerable arguments, string baseDirectory) - { - Requires.NotNull(arguments); - Requires.NotNullOrEmpty(baseDirectory); + Requires.NotNull(arguments); + Requires.NotNullOrEmpty(baseDirectory); - return BuildOptions.FromCommandLineArguments( - VisualBasicCommandLineParser.Default.Parse(arguments, baseDirectory, sdkDirectory: null, additionalReferenceDirectories: null)); - } + return BuildOptions.FromCommandLineArguments( + VisualBasicCommandLineParser.Default.Parse(arguments, baseDirectory, sdkDirectory: null, additionalReferenceDirectories: null)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsService.cs index 10dd7cbfea..b976564fec 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsService.cs @@ -2,21 +2,20 @@ using Microsoft.CodeAnalysis.VisualBasic; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic; + +[Export(typeof(ISyntaxFactsService))] +[Order(Order.Default)] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicSyntaxFactsService : ISyntaxFactsService { - [Export(typeof(ISyntaxFactsService))] - [Order(Order.Default)] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicSyntaxFactsService : ISyntaxFactsService + [ImportingConstructor] + public VisualBasicSyntaxFactsService() { - [ImportingConstructor] - public VisualBasicSyntaxFactsService() - { - } + } - public bool IsValidIdentifier(string identifierName) - { - return SyntaxFacts.IsValidIdentifier(identifierName); - } + public bool IsValidIdentifier(string identifierName) + { + return SyntaxFacts.IsValidIdentifier(identifierName); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LogLevel.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LogLevel.cs index 6f0654a19c..973c8c1edf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LogLevel.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LogLevel.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal enum LogLevel { - internal enum LogLevel - { - None, - Minimal, - Info, - Verbose - } + None, + Minimal, + Info, + Verbose } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/BatchLogger.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/BatchLogger.cs index 70800aa0b2..23810543d6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/BatchLogger.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/BatchLogger.cs @@ -2,86 +2,85 @@ using System.Text; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Begins a logging batch that batches up logging writes +/// and writes them all at once on . +/// +internal sealed class BatchLogger : IDisposable { + private readonly IManagedProjectDiagnosticOutputService _outputService; + private StringBuilder? _builder; + private int _indentLevel; + + public BatchLogger(IManagedProjectDiagnosticOutputService outputService) + { + Requires.NotNull(outputService); + + _outputService = outputService; + } + /// - /// Begins a logging batch that batches up logging writes - /// and writes them all at once on . + /// Gets or sets the indent level. /// - internal sealed class BatchLogger : IDisposable + /// + /// The indent level. The default is 0. + /// + /// + /// is less than 0. + /// + public int IndentLevel { - private readonly IManagedProjectDiagnosticOutputService _outputService; - private StringBuilder? _builder; - private int _indentLevel; - - public BatchLogger(IManagedProjectDiagnosticOutputService outputService) + get { return _indentLevel; } + set { - Requires.NotNull(outputService); + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), value, null); - _outputService = outputService; + _indentLevel = value; } + } - /// - /// Gets or sets the indent level. - /// - /// - /// The indent level. The default is 0. - /// - /// - /// is less than 0. - /// - public int IndentLevel + public bool IsEnabled + { + get { return _outputService.IsEnabled; } + } + + internal void WriteLine() + { + if (IsEnabled) { - get { return _indentLevel; } - set - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), value, null); + _builder ??= new StringBuilder(); - _indentLevel = value; - } + _builder.AppendLine(); } + } - public bool IsEnabled + public void WriteLine(string text) + { + if (IsEnabled) { - get { return _outputService.IsEnabled; } - } + _builder ??= new StringBuilder(); - internal void WriteLine() - { - if (IsEnabled) + // Need to factor in that when we eventually write to the logger + // it's going to append a new line to the string we write, so we + // only append the new line just before we write another string. + if (_builder.Length != 0) { - _builder ??= new StringBuilder(); - _builder.AppendLine(); } - } - - public void WriteLine(string text) - { - if (IsEnabled) - { - _builder ??= new StringBuilder(); - - // Need to factor in that when we eventually write to the logger - // it's going to append a new line to the string we write, so we - // only append the new line just before we write another string. - if (_builder.Length != 0) - { - _builder.AppendLine(); - } - _builder.Append(' ', 4 * _indentLevel); - _builder.Append(text); - } + _builder.Append(' ', 4 * _indentLevel); + _builder.Append(text); } + } - public void Dispose() + public void Dispose() + { + if (_builder is not null) { - if (_builder is not null) - { - _outputService.WriteLine(_builder.ToString()); - } + _outputService.WriteLine(_builder.ToString()); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/ProjectDiagnosticOutputServiceExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/ProjectDiagnosticOutputServiceExtensions.cs index c3654e5b62..796828bae8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/ProjectDiagnosticOutputServiceExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Logging/ProjectDiagnosticOutputServiceExtensions.cs @@ -1,114 +1,113 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Provides extension methods for instances. +/// +internal static partial class ProjectDiagnosticOutputServiceExtensions { /// - /// Provides extension methods for instances. + /// If is , + /// writes the text representation of the specified object, followed + /// by the current line terminator, to the log using the specified + /// format information. /// - internal static partial class ProjectDiagnosticOutputServiceExtensions + /// + /// is + /// + /// -or- + /// + /// is . + /// + /// + /// The format specification in is invalid. + /// + public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, object? argument) { - /// - /// If is , - /// writes the text representation of the specified object, followed - /// by the current line terminator, to the log using the specified - /// format information. - /// - /// - /// is - /// - /// -or- - /// - /// is . - /// - /// - /// The format specification in is invalid. - /// - public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, object? argument) - { - Requires.NotNull(logger); + Requires.NotNull(logger); - if (logger.IsEnabled) - { - logger.WriteLine(string.Format(format, argument)); - } + if (logger.IsEnabled) + { + logger.WriteLine(string.Format(format, argument)); } + } - /// - /// If is , - /// writes the text representation of the specified objects, followed - /// by the current line terminator, to the log using the specified format - /// information. - /// - /// - /// is - /// - /// -or- - /// - /// is . - /// - /// - /// The format specification in is invalid. - /// - public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, object? argument1, object? argument2) - { - Requires.NotNull(logger); + /// + /// If is , + /// writes the text representation of the specified objects, followed + /// by the current line terminator, to the log using the specified format + /// information. + /// + /// + /// is + /// + /// -or- + /// + /// is . + /// + /// + /// The format specification in is invalid. + /// + public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, object? argument1, object? argument2) + { + Requires.NotNull(logger); - if (logger.IsEnabled) - { - logger.WriteLine(string.Format(format, argument1, argument2)); - } + if (logger.IsEnabled) + { + logger.WriteLine(string.Format(format, argument1, argument2)); } + } - /// - /// If is , - /// writes the text representation of the specified objects, followed - /// by the current line terminator, to the log using the specified - /// format information. - /// - /// - /// is - /// - /// -or- - /// - /// is . - /// - /// - /// The format specification in is invalid. - /// - public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, object? argument1, object? argument2, object? argument3) - { - Requires.NotNull(logger); + /// + /// If is , + /// writes the text representation of the specified objects, followed + /// by the current line terminator, to the log using the specified + /// format information. + /// + /// + /// is + /// + /// -or- + /// + /// is . + /// + /// + /// The format specification in is invalid. + /// + public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, object? argument1, object? argument2, object? argument3) + { + Requires.NotNull(logger); - if (logger.IsEnabled) - { - logger.WriteLine(string.Format(format, argument1, argument2, argument3)); - } + if (logger.IsEnabled) + { + logger.WriteLine(string.Format(format, argument1, argument2, argument3)); } + } - /// - /// If is , - /// writes the text representation of the specified array of objects, - /// followed by the current line terminator, to the log using the - /// specified format information. - /// - /// - /// is - /// - /// -or- - /// - /// is . - /// - /// - /// The format specification in is invalid. - /// - public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, params object?[] arguments) - { - Requires.NotNull(logger); + /// + /// If is , + /// writes the text representation of the specified array of objects, + /// followed by the current line terminator, to the log using the + /// specified format information. + /// + /// + /// is + /// + /// -or- + /// + /// is . + /// + /// + /// The format specification in is invalid. + /// + public static void WriteLine(this IManagedProjectDiagnosticOutputService logger, string format, params object?[] arguments) + { + Requires.NotNull(logger); - if (logger.IsEnabled) - { - logger.WriteLine(string.Format(format, arguments)); - } + if (logger.IsEnabled) + { + logger.WriteLine(string.Format(format, arguments)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsync.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsync.cs index 3863c8abcc..1916c101d9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsync.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsync.cs @@ -2,171 +2,170 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides an implementation of that lets +/// implementers protect themselves from being disposed while doing work. +/// +/// +/// lets implementors prevent themselves from being disposed +/// by locking . This class provides a similar +/// mechanism by passing a delegate into . +/// +internal abstract class OnceInitializedOnceDisposedUnderLockAsync : OnceInitializedOnceDisposedAsync { + private readonly ReentrantSemaphore _semaphore; + + protected OnceInitializedOnceDisposedUnderLockAsync(JoinableTaskContextNode joinableTaskContextNode) + : base(joinableTaskContextNode) + { + _semaphore = ReentrantSemaphore.Create(1, joinableTaskContextNode.Context, ReentrantSemaphore.ReentrancyMode.Stack); + } + + protected sealed override async Task DisposeCoreAsync(bool initialized) + { + await _semaphore.ExecuteAsync(() => DisposeCoreUnderLockAsync(initialized)); + + _semaphore.Dispose(); + } + /// - /// Provides an implementation of that lets - /// implementers protect themselves from being disposed while doing work. + /// Disposes of managed and unmanaged resources owned by this instance, under a lock + /// that prevents overlap with any currently executing actions passed to + /// . /// - /// - /// lets implementors prevent themselves from being disposed - /// by locking . This class provides a similar - /// mechanism by passing a delegate into . - /// - internal abstract class OnceInitializedOnceDisposedUnderLockAsync : OnceInitializedOnceDisposedAsync + /// + /// A value indicating whether this instance had been previously initialized. + /// + protected abstract Task DisposeCoreUnderLockAsync(bool initialized); + + /// + /// Executes the specified action under a lock that prevents overlap with any currently executing actions passed to + /// and + /// . + /// + /// + /// The action to execute under the lock. This action is passed a which is cancelled + /// when either this object is disposed, or is cancelled. + /// + /// + /// The token to monitor for cancellation requests. The default value is . + /// + /// + /// The result of . + /// + /// + /// is . + /// + /// + /// The result is awaited and is cancelled. + /// + /// -or- + /// + /// The result is awaited and the + /// has been disposed of. + /// + protected Task ExecuteUnderLockAsync(Func> action, CancellationToken cancellationToken = default) { - private readonly ReentrantSemaphore _semaphore; + Requires.NotNull(action); - protected OnceInitializedOnceDisposedUnderLockAsync(JoinableTaskContextNode joinableTaskContextNode) - : base(joinableTaskContextNode) - { - _semaphore = ReentrantSemaphore.Create(1, joinableTaskContextNode.Context, ReentrantSemaphore.ReentrancyMode.Stack); - } + return ExecuteUnderLockCoreAsync(action, cancellationToken); + } - protected sealed override async Task DisposeCoreAsync(bool initialized) - { - await _semaphore.ExecuteAsync(() => DisposeCoreUnderLockAsync(initialized)); + /// + /// Executes the specified action under a lock that prevents overlap with any currently executing actions passed to + /// and + /// . + /// + /// + /// The action to execute under the lock. This action is passed a which is cancelled + /// when either this object is disposed, or is cancelled. + /// + /// + /// The token to monitor for cancellation requests. The default value is . + /// + /// + /// The result of . + /// + /// + /// is . + /// + /// + /// The result is awaited and is cancelled. + /// + /// -or- + /// + /// The result is awaited and the + /// has been disposed of. + /// + protected Task ExecuteUnderLockAsync(Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(action); - _semaphore.Dispose(); - } + return ExecuteUnderLockCoreAsync(action, cancellationToken); + } - /// - /// Disposes of managed and unmanaged resources owned by this instance, under a lock - /// that prevents overlap with any currently executing actions passed to - /// . - /// - /// - /// A value indicating whether this instance had been previously initialized. - /// - protected abstract Task DisposeCoreUnderLockAsync(bool initialized); - - /// - /// Executes the specified action under a lock that prevents overlap with any currently executing actions passed to - /// and - /// . - /// - /// - /// The action to execute under the lock. This action is passed a which is cancelled - /// when either this object is disposed, or is cancelled. - /// - /// - /// The token to monitor for cancellation requests. The default value is . - /// - /// - /// The result of . - /// - /// - /// is . - /// - /// - /// The result is awaited and is cancelled. - /// - /// -or- - /// - /// The result is awaited and the - /// has been disposed of. - /// - protected Task ExecuteUnderLockAsync(Func> action, CancellationToken cancellationToken = default) - { - Requires.NotNull(action); + private async Task ExecuteUnderLockCoreAsync(Func> action, CancellationToken cancellationToken = default) + { + Requires.NotNull(action); - return ExecuteUnderLockCoreAsync(action, cancellationToken); - } + using var source = CancellationTokenExtensions.CombineWith(cancellationToken, DisposalToken); + CancellationToken jointCancellationToken = source.Token; - /// - /// Executes the specified action under a lock that prevents overlap with any currently executing actions passed to - /// and - /// . - /// - /// - /// The action to execute under the lock. This action is passed a which is cancelled - /// when either this object is disposed, or is cancelled. - /// - /// - /// The token to monitor for cancellation requests. The default value is . - /// - /// - /// The result of . - /// - /// - /// is . - /// - /// - /// The result is awaited and is cancelled. - /// - /// -or- - /// - /// The result is awaited and the - /// has been disposed of. - /// - protected Task ExecuteUnderLockAsync(Func action, CancellationToken cancellationToken = default) + try { - Requires.NotNull(action); + T result = default!; + await _semaphore.ExecuteAsync(async () => { result = await action(jointCancellationToken); }, jointCancellationToken); - return ExecuteUnderLockCoreAsync(action, cancellationToken); + return result; } - - private async Task ExecuteUnderLockCoreAsync(Func> action, CancellationToken cancellationToken = default) + catch (Exception ex) { - Requires.NotNull(action); + if (!TryTranslateException(ex, cancellationToken, DisposalToken)) + throw; + } - using var source = CancellationTokenExtensions.CombineWith(cancellationToken, DisposalToken); - CancellationToken jointCancellationToken = source.Token; + throw Assumes.NotReachable(); + } - try - { - T result = default!; - await _semaphore.ExecuteAsync(async () => { result = await action(jointCancellationToken); }, jointCancellationToken); + private async Task ExecuteUnderLockCoreAsync(Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(action); - return result; - } - catch (Exception ex) - { - if (!TryTranslateException(ex, cancellationToken, DisposalToken)) - throw; - } + using var source = CancellationTokenExtensions.CombineWith(cancellationToken, DisposalToken); + CancellationToken jointCancellationToken = source.Token; - throw Assumes.NotReachable(); + try + { + await _semaphore.ExecuteAsync(() => action(jointCancellationToken), jointCancellationToken); + } + catch (Exception ex) + { + if (!TryTranslateException(ex, cancellationToken, DisposalToken)) + throw; } + } - private async Task ExecuteUnderLockCoreAsync(Func action, CancellationToken cancellationToken = default) + private bool TryTranslateException(Exception exception, CancellationToken cancellationToken1, CancellationToken cancellationToken2) + { + // Consumers treat cancellation due their specified token being cancelled differently from cancellation + // due our instance being disposed. Because we wrap the two in a source, the resulting exception + // contains it instead of the one that was actually cancelled. + if (exception is OperationCanceledException) { - Requires.NotNull(action); - - using var source = CancellationTokenExtensions.CombineWith(cancellationToken, DisposalToken); - CancellationToken jointCancellationToken = source.Token; - - try - { - await _semaphore.ExecuteAsync(() => action(jointCancellationToken), jointCancellationToken); - } - catch (Exception ex) - { - if (!TryTranslateException(ex, cancellationToken, DisposalToken)) - throw; - } + cancellationToken1.ThrowIfCancellationRequested(); + cancellationToken2.ThrowIfCancellationRequested(); } - private bool TryTranslateException(Exception exception, CancellationToken cancellationToken1, CancellationToken cancellationToken2) + if (exception is ObjectDisposedException && DisposalToken.IsCancellationRequested) { - // Consumers treat cancellation due their specified token being cancelled differently from cancellation - // due our instance being disposed. Because we wrap the two in a source, the resulting exception - // contains it instead of the one that was actually cancelled. - if (exception is OperationCanceledException) - { - cancellationToken1.ThrowIfCancellationRequested(); - cancellationToken2.ThrowIfCancellationRequested(); - } - - if (exception is ObjectDisposedException && DisposalToken.IsCancellationRequested) - { - // There's a tiny chance that between checking the cancellation token (wrapping DisposalToken) - // and checking if the underlying SemaphoreSlim has been disposed, that dispose for this instance - // (and hence _semaphore) has been run. Handle that and just treat it as a cancellation. - DisposalToken.ThrowIfCancellationRequested(); - } - - return false; + // There's a tiny chance that between checking the cancellation token (wrapping DisposalToken) + // and checking if the underlying SemaphoreSlim has been disposed, that dispose for this instance + // (and hence _semaphore) has been run. Handle that and just treat it as a cancellation. + DisposalToken.ThrowIfCancellationRequested(); } + + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/DataProgressTrackerServiceExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/DataProgressTrackerServiceExtensions.cs index 743656e0bf..1eaa43dad3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/DataProgressTrackerServiceExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/DataProgressTrackerServiceExtensions.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.OperationProgress +namespace Microsoft.VisualStudio.ProjectSystem.OperationProgress; + +/// +/// Provides extension methods for . +/// +internal static class DataProgressTrackerServiceExtensions { /// - /// Provides extension methods for . + /// Registers an output to be monitored for the "IntelliSense" stage. The returned registration needs to be notified when new data is calculated. /// - internal static class DataProgressTrackerServiceExtensions + public static IDataProgressTrackerServiceRegistration RegisterForIntelliSense(this IDataProgressTrackerService service, object owner, ConfiguredProject project, string name) { - /// - /// Registers an output to be monitored for the "IntelliSense" stage. The returned registration needs to be notified when new data is calculated. - /// - public static IDataProgressTrackerServiceRegistration RegisterForIntelliSense(this IDataProgressTrackerService service, object owner, ConfiguredProject project, string name) - { - // We deliberately do not want these individual operations in a stage (such as pushing evaluation - // results to Roslyn) to be visible to the user, so we avoiding specifying a display message. - var dataSource = new ProgressTrackerOutputDataSource(owner, project, operationProgressStageId: OperationProgressStageId.IntelliSense, name, displayMessage: ""); + // We deliberately do not want these individual operations in a stage (such as pushing evaluation + // results to Roslyn) to be visible to the user, so we avoiding specifying a display message. + var dataSource = new ProgressTrackerOutputDataSource(owner, project, operationProgressStageId: OperationProgressStageId.IntelliSense, name, displayMessage: ""); - return service.RegisterOutputDataSource(dataSource); - } + return service.RegisterOutputDataSource(dataSource); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/OperationProgressStageId.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/OperationProgressStageId.cs index 14172a6c83..4b75795daa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/OperationProgressStageId.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/OperationProgressStageId.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.OperationProgress +namespace Microsoft.VisualStudio.ProjectSystem.OperationProgress; + +internal static class OperationProgressStageId { - internal static class OperationProgressStageId - { - /// - /// Represents the stage that represents all the operations that contribute to IntelliSense results. - /// - public const string IntelliSense = "IntelliSense"; - } + /// + /// Represents the stage that represents all the operations that contribute to IntelliSense results. + /// + public const string IntelliSense = "IntelliSense"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/ProgressTrackerOutputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/ProgressTrackerOutputDataSource.cs index f0568d26a9..0beb995b67 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/ProgressTrackerOutputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/OperationProgress/ProgressTrackerOutputDataSource.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.OperationProgress +namespace Microsoft.VisualStudio.ProjectSystem.OperationProgress; + +internal class ProgressTrackerOutputDataSource : IProgressTrackerOutputDataSource { - internal class ProgressTrackerOutputDataSource : IProgressTrackerOutputDataSource + public ProgressTrackerOutputDataSource(object owner, ConfiguredProject configuredProject, string operationProgressStageId, string name, string displayMessage) { - public ProgressTrackerOutputDataSource(object owner, ConfiguredProject configuredProject, string operationProgressStageId, string name, string displayMessage) - { - Owner = owner; - ConfiguredProject = configuredProject; - OperationProgressStageId = operationProgressStageId; - Name = name; - DisplayMessage = displayMessage; - } + Owner = owner; + ConfiguredProject = configuredProject; + OperationProgressStageId = operationProgressStageId; + Name = name; + DisplayMessage = displayMessage; + } - // For inspection of dumps - public object Owner { get; } + // For inspection of dumps + public object Owner { get; } - public ConfiguredProject ConfiguredProject { get; } + public ConfiguredProject ConfiguredProject { get; } - public string OperationProgressStageId { get; } + public string OperationProgressStageId { get; } - public string Name { get; } + public string Name { get; } - public string DisplayMessage { get; } - } + public string DisplayMessage { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Order.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Order.cs index d5ccc1df91..41677d2ae0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Order.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Order.cs @@ -1,37 +1,36 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Contains constants representing the order precedence for managed components that can be passed to . +/// +internal static class Order { + // NOTE: For consistency, we use the priorities from 10 -> 30 for the managed project system. + // This lets us have a higher precedence than built-in CPS components (which default to 0), but at + // the same time let other 1st and 3rd party components have a higher precedence than us. + // Because we might want to order components within the managed project system itself, we also + // have a staggered set of priorities that also lets other components run between our individual + // components. + /// - /// Contains constants representing the order precedence for managed components that can be passed to . + /// Represents the lowest order precedence for components. /// - internal static class Order - { - // NOTE: For consistency, we use the priorities from 10 -> 30 for the managed project system. - // This lets us have a higher precedence than built-in CPS components (which default to 0), but at - // the same time let other 1st and 3rd party components have a higher precedence than us. - // Because we might want to order components within the managed project system itself, we also - // have a staggered set of priorities that also lets other components run between our individual - // components. - - /// - /// Represents the lowest order precedence for components. - /// - public const int Lowest = 0; + public const int Lowest = 0; - /// - /// Represents the default order precedence for managed components. - /// - public const int Default = 10; + /// + /// Represents the default order precedence for managed components. + /// + public const int Default = 10; - /// - /// Represents the an order precedence for managed components that is ordered before . - /// - public const int BeforeDefault = 20; + /// + /// Represents the an order precedence for managed components that is ordered before . + /// + public const int BeforeDefault = 20; - /// - /// Represents the an order precedence for managed components that is ordered before . - /// - public const int BeforeBeforeDefault = 30; - } + /// + /// Represents the an order precedence for managed components that is ordered before . + /// + public const int BeforeBeforeDefault = 30; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreConfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreConfiguredInputDataSource.cs index 9a73a7c8b4..4894ca1bc6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreConfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreConfiguredInputDataSource.cs @@ -1,14 +1,13 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents the data source of metadata needed for input into restore operations for an individual . +/// This will be later combined with other implicitly active instances within a +/// to provide enough data to restore the entire project. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IPackageRestoreConfiguredInputDataSource : IProjectValueDataSource { - /// - /// Represents the data source of metadata needed for input into restore operations for an individual . - /// This will be later combined with other implicitly active instances within a - /// to provide enough data to restore the entire project. - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IPackageRestoreConfiguredInputDataSource : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreDataSource.cs index baed1b8b30..02d7547091 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreDataSource.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Provides a view of the project's restore state. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IPackageRestoreDataSource : IProjectValueDataSource { - /// - /// Provides a view of the project's restore state. - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IPackageRestoreDataSource : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreUnconfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreUnconfiguredInputDataSource.cs index c7e62938ba..00e8cfdf35 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreUnconfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/IPackageRestoreUnconfiguredInputDataSource.cs @@ -1,14 +1,13 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents the data source of metadata needed for input into restore operations for a +/// instance by resolving conflicts and combining the data of all implicitly active +/// instances. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IPackageRestoreUnconfiguredInputDataSource : IProjectValueDataSource { - /// - /// Represents the data source of metadata needed for input into restore operations for a - /// instance by resolving conflicts and combining the data of all implicitly active - /// instances. - /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IPackageRestoreUnconfiguredInputDataSource : IProjectValueDataSource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreConfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreConfiguredInputDataSource.cs index e5898c101e..e117557778 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreConfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreConfiguredInputDataSource.cs @@ -4,70 +4,69 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using RestoreUpdate = Microsoft.VisualStudio.ProjectSystem.IProjectVersionedValue; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Provides an implementation of that combines +/// evaluation and build data into . +/// +[Export(typeof(IPackageRestoreConfiguredInputDataSource))] +[AppliesTo(ProjectCapability.PackageReferences)] +[ExportInitialBuildRulesSubscriptions([ + CollectedFrameworkReference.SchemaName, + CollectedPackageDownload.SchemaName, + CollectedPackageVersion.SchemaName, + CollectedNuGetAuditSuppressions.SchemaName, + CollectedPrunePackageReference.SchemaName, + CollectedPackageReference.SchemaName])] +internal class PackageRestoreConfiguredInputDataSource : ChainedProjectValueDataSourceBase, IPackageRestoreConfiguredInputDataSource { - /// - /// Provides an implementation of that combines - /// evaluation and build data into . - /// - [Export(typeof(IPackageRestoreConfiguredInputDataSource))] - [AppliesTo(ProjectCapability.PackageReferences)] - [ExportInitialBuildRulesSubscriptions([ - CollectedFrameworkReference.SchemaName, - CollectedPackageDownload.SchemaName, - CollectedPackageVersion.SchemaName, - CollectedNuGetAuditSuppressions.SchemaName, - CollectedPrunePackageReference.SchemaName, - CollectedPackageReference.SchemaName])] - internal class PackageRestoreConfiguredInputDataSource : ChainedProjectValueDataSourceBase, IPackageRestoreConfiguredInputDataSource - { - private static readonly ImmutableHashSet s_rules = Empty.OrdinalIgnoreCaseStringSet - .Add(NuGetRestore.SchemaName) // Evaluation - .Add(EvaluatedProjectReference.SchemaName) // Evaluation - .Add(DotNetCliToolReference.SchemaName) // Evaluation - .Add(CollectedFrameworkReference.SchemaName) // Build - .Add(CollectedPackageDownload.SchemaName) // Build - .Add(CollectedPackageVersion.SchemaName) // Build - .Add(CollectedNuGetAuditSuppressions.SchemaName) // Build - .Add(CollectedPrunePackageReference.SchemaName) // Build - .Add(CollectedPackageReference.SchemaName); // Build + private static readonly ImmutableHashSet s_rules = Empty.OrdinalIgnoreCaseStringSet + .Add(NuGetRestore.SchemaName) // Evaluation + .Add(EvaluatedProjectReference.SchemaName) // Evaluation + .Add(DotNetCliToolReference.SchemaName) // Evaluation + .Add(CollectedFrameworkReference.SchemaName) // Build + .Add(CollectedPackageDownload.SchemaName) // Build + .Add(CollectedPackageVersion.SchemaName) // Build + .Add(CollectedNuGetAuditSuppressions.SchemaName) // Build + .Add(CollectedPrunePackageReference.SchemaName) // Build + .Add(CollectedPackageReference.SchemaName); // Build - private readonly IProjectSubscriptionService _projectSubscriptionService; + private readonly IProjectSubscriptionService _projectSubscriptionService; - [ImportingConstructor] - public PackageRestoreConfiguredInputDataSource(ConfiguredProject project, IProjectSubscriptionService projectSubscriptionService) - : base(project, synchronousDisposal: false, registerDataSource: false) - { - _projectSubscriptionService = projectSubscriptionService; - } + [ImportingConstructor] + public PackageRestoreConfiguredInputDataSource(ConfiguredProject project, IProjectSubscriptionService projectSubscriptionService) + : base(project, synchronousDisposal: false, registerDataSource: false) + { + _projectSubscriptionService = projectSubscriptionService; + } - protected override IDisposable LinkExternalInput(ITargetBlock targetBlock) - { - IProjectValueDataSource source = _projectSubscriptionService.JointRuleSource; + protected override IDisposable LinkExternalInput(ITargetBlock targetBlock) + { + IProjectValueDataSource source = _projectSubscriptionService.JointRuleSource; - // Transform the changes from evaluation/design-time build -> restore data - DisposableValue> transformBlock = source.SourceBlock.TransformWithNoDelta( - update => update.Derive(u => CreateRestoreInput(update, u.ProjectConfiguration, u.CurrentState)), - suppressVersionOnlyUpdates: false, // We need to coordinate these at the unconfigured-level - ruleNames: s_rules); + // Transform the changes from evaluation/design-time build -> restore data + DisposableValue> transformBlock = source.SourceBlock.TransformWithNoDelta( + update => update.Derive(u => CreateRestoreInput(update, u.ProjectConfiguration, u.CurrentState)), + suppressVersionOnlyUpdates: false, // We need to coordinate these at the unconfigured-level + ruleNames: s_rules); - // Set the link up so that we publish changes to target block - transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + // Set the link up so that we publish changes to target block + transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - // Join the source blocks, so if they need to switch to UI thread to complete - // and someone is blocked on us on the same thread, the call proceeds - JoinUpstreamDataSources(source); + // Join the source blocks, so if they need to switch to UI thread to complete + // and someone is blocked on us on the same thread, the call proceeds + JoinUpstreamDataSources(source); - return transformBlock; - } + return transformBlock; + } - private static PackageRestoreConfiguredInput CreateRestoreInput(IProjectVersionedValue projectSubscriptionUpdate, ProjectConfiguration projectConfiguration, IImmutableDictionary update) - { - var restoreInfo = RestoreBuilder.ToProjectRestoreInfo(update); + private static PackageRestoreConfiguredInput CreateRestoreInput(IProjectVersionedValue projectSubscriptionUpdate, ProjectConfiguration projectConfiguration, IImmutableDictionary update) + { + var restoreInfo = RestoreBuilder.ToProjectRestoreInfo(update); - IComparable configuredProjectVersion = projectSubscriptionUpdate.DataSourceVersions[ProjectDataSources.ConfiguredProjectVersion]; + IComparable configuredProjectVersion = projectSubscriptionUpdate.DataSourceVersions[ProjectDataSources.ConfiguredProjectVersion]; - return new PackageRestoreConfiguredInput(projectConfiguration, restoreInfo, configuredProjectVersion); - } + return new PackageRestoreConfiguredInput(projectConfiguration, restoreInfo, configuredProjectVersion); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreDataSource.cs index fa218612af..c1df6feea6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreDataSource.cs @@ -6,284 +6,283 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Responsible for pushing ("nominating") project data such as referenced packages and +/// target frameworks to NuGet so that it can perform a package restore and returns the +/// result. +/// +[Export(typeof(IPackageRestoreDataSource))] +[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] +[AppliesTo(ProjectCapability.PackageReferences)] +internal partial class PackageRestoreDataSource : ChainedProjectValueDataSourceBase, IPackageRestoreDataSource, IProjectDynamicLoadComponent { - /// - /// Responsible for pushing ("nominating") project data such as referenced packages and - /// target frameworks to NuGet so that it can perform a package restore and returns the - /// result. - /// - [Export(typeof(IPackageRestoreDataSource))] - [Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))] - [AppliesTo(ProjectCapability.PackageReferences)] - internal partial class PackageRestoreDataSource : ChainedProjectValueDataSourceBase, IPackageRestoreDataSource, IProjectDynamicLoadComponent + // This class represents the last data source in the package restore chain, which is made up of the following: + // _________________________________________ _________________________________________ + // | | | | + // | PackageRestoreConfiguredInputDataSource | | PackageRestoreConfiguredInputDataSource | Produces data for each "implicitly active" config + // | (Debug|AnyCPU|net45) | | (Debug|AnyCPU|netcoreapp30) | + // |_________________________________________| |_________________________________________| + // || || + // PackageRestoreConfiguredInput + // || || + // ________\/_________________________\/______ + // | | + // | PackageRestoreUnconfiguredInputDataSource | Merges config data into single input + // |___________________________________________| + // || + // PackageRestoreUnconfiguredInput + // || + // ____________________\/_____________________ ___________________________ + // | | | | + // | PackageRestoreDataSource |===>| NuGetRestoreService | Pushes restore data to NuGet + // |___________________________________________| |___________________________| + // || + // RestoreData + // // \\ + // // \\ + // ____________________________________\/___ ___\/____________________________________ + // | | | | + // | PackageRestoreProgressTracker | | PackageRestoreProgressTracker | Publishes restore progress to operation progress + // | (Debug|AnyCPU|net45) | | (Debug|AnyCPU|netcoreapp30) | + // |_________________________________________| |_________________________________________| + // + + private readonly IPackageRestoreCycleDetector _cycleDetector; + private readonly UnconfiguredProject _project; + private readonly IPackageRestoreUnconfiguredInputDataSource _dataSource; + private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; + private readonly IFileSystem _fileSystem; + private readonly IManagedProjectDiagnosticOutputService _logger; + private readonly INuGetRestoreService _nuGetRestoreService; + private Hash? _lastHash; + private bool _enabled; + private bool _wasSourceBlockContinuationSet; + + [ImportingConstructor] + public PackageRestoreDataSource( + UnconfiguredProject project, + PackageRestoreSharedJoinableTaskCollection sharedJoinableTaskCollection, + IPackageRestoreUnconfiguredInputDataSource dataSource, + [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, + IFileSystem fileSystem, + IManagedProjectDiagnosticOutputService logger, + INuGetRestoreService nuGetRestoreService, + IPackageRestoreCycleDetector cycleDetector) + : base(project, sharedJoinableTaskCollection, synchronousDisposal: true, registerDataSource: false) { - // This class represents the last data source in the package restore chain, which is made up of the following: - // _________________________________________ _________________________________________ - // | | | | - // | PackageRestoreConfiguredInputDataSource | | PackageRestoreConfiguredInputDataSource | Produces data for each "implicitly active" config - // | (Debug|AnyCPU|net45) | | (Debug|AnyCPU|netcoreapp30) | - // |_________________________________________| |_________________________________________| - // || || - // PackageRestoreConfiguredInput - // || || - // ________\/_________________________\/______ - // | | - // | PackageRestoreUnconfiguredInputDataSource | Merges config data into single input - // |___________________________________________| - // || - // PackageRestoreUnconfiguredInput - // || - // ____________________\/_____________________ ___________________________ - // | | | | - // | PackageRestoreDataSource |===>| NuGetRestoreService | Pushes restore data to NuGet - // |___________________________________________| |___________________________| - // || - // RestoreData - // // \\ - // // \\ - // ____________________________________\/___ ___\/____________________________________ - // | | | | - // | PackageRestoreProgressTracker | | PackageRestoreProgressTracker | Publishes restore progress to operation progress - // | (Debug|AnyCPU|net45) | | (Debug|AnyCPU|netcoreapp30) | - // |_________________________________________| |_________________________________________| - // - - private readonly IPackageRestoreCycleDetector _cycleDetector; - private readonly UnconfiguredProject _project; - private readonly IPackageRestoreUnconfiguredInputDataSource _dataSource; - private readonly IProjectAsynchronousTasksService _projectAsynchronousTasksService; - private readonly IFileSystem _fileSystem; - private readonly IManagedProjectDiagnosticOutputService _logger; - private readonly INuGetRestoreService _nuGetRestoreService; - private Hash? _lastHash; - private bool _enabled; - private bool _wasSourceBlockContinuationSet; - - [ImportingConstructor] - public PackageRestoreDataSource( - UnconfiguredProject project, - PackageRestoreSharedJoinableTaskCollection sharedJoinableTaskCollection, - IPackageRestoreUnconfiguredInputDataSource dataSource, - [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, - IFileSystem fileSystem, - IManagedProjectDiagnosticOutputService logger, - INuGetRestoreService nuGetRestoreService, - IPackageRestoreCycleDetector cycleDetector) - : base(project, sharedJoinableTaskCollection, synchronousDisposal: true, registerDataSource: false) + _project = project; + _dataSource = dataSource; + _projectAsynchronousTasksService = projectAsynchronousTasksService; + _fileSystem = fileSystem; + _logger = logger; + _nuGetRestoreService = nuGetRestoreService; + _cycleDetector = cycleDetector; + } + + protected override IDisposable? LinkExternalInput(ITargetBlock> targetBlock) + { + JoinUpstreamDataSources(_dataSource); + + // Take the unconfigured "restore inputs", send them to NuGet, and then return the result of that restore. + // We make use of TransformMany so that we can opt out of returning. + DisposableValue>> transformBlock = _dataSource.SourceBlock.TransformManyWithNoDelta(RestoreAsync); + + transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + + return transformBlock; + } + + // internal for testing purposes only -- the only real caller is the transform block + internal async Task>> RestoreAsync(IProjectVersionedValue e) + { + // No configurations - likely during project close. + // Check if out of date to prevent extra restore under some conditions. + if (!_enabled || e.Value.RestoreInfo is null || IsRestoreDataVersionOutOfDate(e.DataSourceVersions) || IsProjectConfigurationVersionOutOfDate(e.Value.ConfiguredInputs)) { - _project = project; - _dataSource = dataSource; - _projectAsynchronousTasksService = projectAsynchronousTasksService; - _fileSystem = fileSystem; - _logger = logger; - _nuGetRestoreService = nuGetRestoreService; - _cycleDetector = cycleDetector; + return Enumerable.Empty>(); } - protected override IDisposable? LinkExternalInput(ITargetBlock> targetBlock) + bool succeeded = await RestoreCoreAsync(e.Value); + + RestoreData restoreData = CreateRestoreData(e.Value.RestoreInfo, succeeded); + + return new[] { - JoinUpstreamDataSources(_dataSource); + new ProjectVersionedValue(restoreData, e.DataSourceVersions) + }; + } - // Take the unconfigured "restore inputs", send them to NuGet, and then return the result of that restore. - // We make use of TransformMany so that we can opt out of returning. - DisposableValue>> transformBlock = _dataSource.SourceBlock.TransformManyWithNoDelta(RestoreAsync); + private async Task RestoreCoreAsync(PackageRestoreUnconfiguredInput value) + { + CancellationToken token = _projectAsynchronousTasksService.UnloadCancellationToken; - transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + ProjectRestoreInfo? restoreInfo = value.RestoreInfo; - return transformBlock; + Assumes.NotNull(restoreInfo); + + // Restore service always does work regardless of whether the value we pass + // them to actually contains changes, only nominate if there are any. + Hash hash = RestoreHasher.CalculateHash(restoreInfo); + + if (await _cycleDetector.IsCycleDetectedAsync(hash, value.ActiveConfiguration, token)) + { + _lastHash = hash; + return false; } - // internal for testing purposes only -- the only real caller is the transform block - internal async Task>> RestoreAsync(IProjectVersionedValue e) + if (_lastHash?.Equals(hash) == true) { - // No configurations - likely during project close. - // Check if out of date to prevent extra restore under some conditions. - if (!_enabled || e.Value.RestoreInfo is null || IsRestoreDataVersionOutOfDate(e.DataSourceVersions) || IsProjectConfigurationVersionOutOfDate(e.Value.ConfiguredInputs)) - { - return Enumerable.Empty>(); - } + await _nuGetRestoreService.UpdateWithoutNominationAsync(value.ConfiguredInputs); + return true; + } - bool succeeded = await RestoreCoreAsync(e.Value); + _lastHash = hash; - RestoreData restoreData = CreateRestoreData(e.Value.RestoreInfo, succeeded); + JoinableTask joinableTask = JoinableFactory.RunAsync(() => + { + return NominateForRestoreAsync(restoreInfo, value.ConfiguredInputs, token); + }); - return new[] - { - new ProjectVersionedValue(restoreData, e.DataSourceVersions) - }; - } + _projectAsynchronousTasksService.RegisterAsyncTask( + joinableTask, + operationFlags: ProjectCriticalOperation.Build | ProjectCriticalOperation.Unload | ProjectCriticalOperation.Rename, + registerFaultHandler: true); - private async Task RestoreCoreAsync(PackageRestoreUnconfiguredInput value) + return await joinableTask; + } + + private async Task NominateForRestoreAsync(ProjectRestoreInfo restoreInfo, IReadOnlyCollection versions, CancellationToken cancellationToken) + { + RestoreLogger.BeginNominateRestore(_logger, _project.FullPath, restoreInfo); + + try { - CancellationToken token = _projectAsynchronousTasksService.UnloadCancellationToken; + return await _nuGetRestoreService.NominateAsync(restoreInfo, versions, cancellationToken); + } + finally + { + RestoreLogger.EndNominateRestore(_logger, _project.FullPath); + } + } - ProjectRestoreInfo? restoreInfo = value.RestoreInfo; + private RestoreData CreateRestoreData(ProjectRestoreInfo restoreInfo, bool succeeded) + { + string projectAssetsFilePath = restoreInfo.ProjectAssetsFilePath; - Assumes.NotNull(restoreInfo); + // Restore service gives us a guarantee that the assets file + // will contain *at least* the changes that we pushed to it. - // Restore service always does work regardless of whether the value we pass - // them to actually contains changes, only nominate if there are any. - Hash hash = RestoreHasher.CalculateHash(restoreInfo); + if (projectAssetsFilePath.Length == 0) + return new RestoreData(string.Empty, DateTime.MinValue, succeeded: false); - if (await _cycleDetector.IsCycleDetectedAsync(hash, value.ActiveConfiguration, token)) - { - _lastHash = hash; - return false; - } + DateTime lastWriteTime = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(projectAssetsFilePath); + + return new RestoreData( + projectAssetsFilePath, + lastWriteTime, + succeeded: succeeded && lastWriteTime != DateTime.MinValue); + } - if (_lastHash?.Equals(hash) == true) + protected virtual bool IsRestoreDataVersionOutOfDate(IImmutableDictionary dataVersions) + { + Assumes.Present(_project.Services.DataSourceRegistry); + + IProjectDataSourceRegistry dataSourceRegistry = _project.Services.DataSourceRegistry; + foreach (KeyValuePair versionDescription in dataVersions) + { + if (dataSourceRegistry.TryGetDataSource(versionDescription.Key, out IProjectValueDataSource? dataSource) && versionDescription.Value.CompareTo(dataSource.DataSourceVersion) < 0) { - await _nuGetRestoreService.UpdateWithoutNominationAsync(value.ConfiguredInputs); return true; } + } - _lastHash = hash; - - JoinableTask joinableTask = JoinableFactory.RunAsync(() => - { - return NominateForRestoreAsync(restoreInfo, value.ConfiguredInputs, token); - }); + return false; + } - _projectAsynchronousTasksService.RegisterAsyncTask( - joinableTask, - operationFlags: ProjectCriticalOperation.Build | ProjectCriticalOperation.Unload | ProjectCriticalOperation.Rename, - registerFaultHandler: true); + protected virtual bool IsProjectConfigurationVersionOutOfDate(IReadOnlyCollection configuredInputs) + { + Assumes.Present(_project.Services.ActiveConfiguredProjectProvider); + Assumes.Present(_project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject); - return await joinableTask; + if (configuredInputs is null) + { + return false; } - private async Task NominateForRestoreAsync(ProjectRestoreInfo restoreInfo, IReadOnlyCollection versions, CancellationToken cancellationToken) - { - RestoreLogger.BeginNominateRestore(_logger, _project.FullPath, restoreInfo); + ConfiguredProject activeConfiguredProject = _project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject; - try - { - return await _nuGetRestoreService.NominateAsync(restoreInfo, versions, cancellationToken); - } - finally + IComparable? activeProjectConfigurationVersionFromConfiguredInputs = null; + foreach (PackageRestoreConfiguredInput configuredInput in configuredInputs) + { + if (configuredInput.ProjectConfiguration.Equals(activeConfiguredProject.ProjectConfiguration)) { - RestoreLogger.EndNominateRestore(_logger, _project.FullPath); + activeProjectConfigurationVersionFromConfiguredInputs = configuredInput.ConfiguredProjectVersion; } } - - private RestoreData CreateRestoreData(ProjectRestoreInfo restoreInfo, bool succeeded) + + if (activeProjectConfigurationVersionFromConfiguredInputs is null || + activeConfiguredProject.ProjectVersion.IsLaterThan( + activeProjectConfigurationVersionFromConfiguredInputs)) { - string projectAssetsFilePath = restoreInfo.ProjectAssetsFilePath; - - // Restore service gives us a guarantee that the assets file - // will contain *at least* the changes that we pushed to it. - - if (projectAssetsFilePath.Length == 0) - return new RestoreData(string.Empty, DateTime.MinValue, succeeded: false); - - DateTime lastWriteTime = _fileSystem.GetLastFileWriteTimeOrMinValueUtc(projectAssetsFilePath); - - return new RestoreData( - projectAssetsFilePath, - lastWriteTime, - succeeded: succeeded && lastWriteTime != DateTime.MinValue); + return true; } - protected virtual bool IsRestoreDataVersionOutOfDate(IImmutableDictionary dataVersions) + if (configuredInputs.Count == 1) { - Assumes.Present(_project.Services.DataSourceRegistry); + return false; + } - IProjectDataSourceRegistry dataSourceRegistry = _project.Services.DataSourceRegistry; - foreach (KeyValuePair versionDescription in dataVersions) + foreach (ConfiguredProject loadedProject in activeConfiguredProject.UnconfiguredProject.LoadedConfiguredProjects) + { + foreach (PackageRestoreConfiguredInput configuredInput in configuredInputs) { - if (dataSourceRegistry.TryGetDataSource(versionDescription.Key, out IProjectValueDataSource? dataSource) && versionDescription.Value.CompareTo(dataSource.DataSourceVersion) < 0) + if (loadedProject.ProjectConfiguration.Equals(configuredInput.ProjectConfiguration) && + loadedProject.ProjectVersion.IsLaterThan(configuredInput.ConfiguredProjectVersion)) { return true; } } - - return false; } - protected virtual bool IsProjectConfigurationVersionOutOfDate(IReadOnlyCollection configuredInputs) - { - Assumes.Present(_project.Services.ActiveConfiguredProjectProvider); - Assumes.Present(_project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject); + return false; + } - if (configuredInputs is null) - { - return false; - } + public Task LoadAsync() + { + _enabled = true; - ConfiguredProject activeConfiguredProject = _project.Services.ActiveConfiguredProjectProvider.ActiveConfiguredProject; + EnsureInitialized(); - IComparable? activeProjectConfigurationVersionFromConfiguredInputs = null; - foreach (PackageRestoreConfiguredInput configuredInput in configuredInputs) + if (!_wasSourceBlockContinuationSet) + { + _wasSourceBlockContinuationSet = true; + + // Inform the NuGet restore service when there will be no further updates so it can cancel any pending work. + _ = SourceBlock.Completion.ContinueWith(t => { - if (configuredInput.ProjectConfiguration.Equals(activeConfiguredProject.ProjectConfiguration)) + if (t.IsFaulted) { - activeProjectConfigurationVersionFromConfiguredInputs = configuredInput.ConfiguredProjectVersion; + _nuGetRestoreService.NotifyFaulted(t.Exception); } - } - - if (activeProjectConfigurationVersionFromConfiguredInputs is null || - activeConfiguredProject.ProjectVersion.IsLaterThan( - activeProjectConfigurationVersionFromConfiguredInputs)) - { - return true; - } - - if (configuredInputs.Count == 1) - { - return false; - } - - foreach (ConfiguredProject loadedProject in activeConfiguredProject.UnconfiguredProject.LoadedConfiguredProjects) - { - foreach (PackageRestoreConfiguredInput configuredInput in configuredInputs) + else { - if (loadedProject.ProjectConfiguration.Equals(configuredInput.ProjectConfiguration) && - loadedProject.ProjectVersion.IsLaterThan(configuredInput.ConfiguredProjectVersion)) - { - return true; - } + _nuGetRestoreService.NotifyComplete(); } - } - - return false; + }, TaskScheduler.Default); } - public Task LoadAsync() - { - _enabled = true; - - EnsureInitialized(); - - if (!_wasSourceBlockContinuationSet) - { - _wasSourceBlockContinuationSet = true; - - // Inform the NuGet restore service when there will be no further updates so it can cancel any pending work. - _ = SourceBlock.Completion.ContinueWith(t => - { - if (t.IsFaulted) - { - _nuGetRestoreService.NotifyFaulted(t.Exception); - } - else - { - _nuGetRestoreService.NotifyComplete(); - } - }, TaskScheduler.Default); - } - - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task UnloadAsync() + public Task UnloadAsync() + { + lock (SyncObject) { - lock (SyncObject) - { - _enabled = false; - } - - return Task.CompletedTask; + _enabled = false; } + + return Task.CompletedTask; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.PackageRestoreProgressTrackerInstance.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.PackageRestoreProgressTrackerInstance.cs index 22098583cb..c38a051874 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.PackageRestoreProgressTrackerInstance.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.PackageRestoreProgressTrackerInstance.cs @@ -2,131 +2,130 @@ using StageId = Microsoft.VisualStudio.ProjectSystem.OperationProgress.OperationProgressStageId; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal partial class PackageRestoreProgressTracker { - internal partial class PackageRestoreProgressTracker + internal class PackageRestoreProgressTrackerInstance : OnceInitializedOnceDisposedAsync, IMultiLifetimeInstance, IJoinableProjectValueDataSource, IProgressTrackerOutputDataSource { - internal class PackageRestoreProgressTrackerInstance : OnceInitializedOnceDisposedAsync, IMultiLifetimeInstance, IJoinableProjectValueDataSource, IProgressTrackerOutputDataSource + // The steps leading up to and during restore are the following: + // + // 1) Evaluation + // 2) Design-Time build (call CollectPackageReferences, et all) + // 3) Push package references & restore data to NuGet ("Nominate") + // 4) If assets file updated during restore, repeat above + // + // It can take two cycles of above (during first open, or when assets file is out of + // date) before we have a design-time build that contains all the references that a + // project depends on, up to and including mscorlib/System.Runtime. + // + // We want the "IntelliSense" operation progress stage to only be considered completed + // once we've stopped this cycle, which will let Roslyn, designers and other consumers + // disable commands, or give indicators that the project is still loading. + // + // To figure out when we've finished the cycle, we compare the last write time of the + // assets file during the last evaluation against the timestamp of the file on disk just + // after restore. If they don't match, we know that we're about to repeat the cycle and + // we're still incomplete. Once they match in timestamp, we know that the last design-time + // build ran with the latest assets file and we notify operation progress that we're now + // completed with the results. + + private readonly IProjectFaultHandlerService _projectFaultHandlerService; + private readonly IDataProgressTrackerService _dataProgressTrackerService; + private readonly IPackageRestoreDataSource _dataSource; + private readonly IProjectSubscriptionService _projectSubscriptionService; + + private IDataProgressTrackerServiceRegistration? _progressRegistration; + private IDisposable? _subscription; + private IDisposable? _joinedDataSources; + + public PackageRestoreProgressTrackerInstance( + ConfiguredProject project, + IProjectThreadingService threadingService, + IProjectFaultHandlerService projectFaultHandlerService, + IDataProgressTrackerService dataProgressTrackerService, + IPackageRestoreDataSource dataSource, + IProjectSubscriptionService projectSubscriptionService) + : base(threadingService.JoinableTaskContext) { - // The steps leading up to and during restore are the following: - // - // 1) Evaluation - // 2) Design-Time build (call CollectPackageReferences, et all) - // 3) Push package references & restore data to NuGet ("Nominate") - // 4) If assets file updated during restore, repeat above - // - // It can take two cycles of above (during first open, or when assets file is out of - // date) before we have a design-time build that contains all the references that a - // project depends on, up to and including mscorlib/System.Runtime. - // - // We want the "IntelliSense" operation progress stage to only be considered completed - // once we've stopped this cycle, which will let Roslyn, designers and other consumers - // disable commands, or give indicators that the project is still loading. - // - // To figure out when we've finished the cycle, we compare the last write time of the - // assets file during the last evaluation against the timestamp of the file on disk just - // after restore. If they don't match, we know that we're about to repeat the cycle and - // we're still incomplete. Once they match in timestamp, we know that the last design-time - // build ran with the latest assets file and we notify operation progress that we're now - // completed with the results. - - private readonly IProjectFaultHandlerService _projectFaultHandlerService; - private readonly IDataProgressTrackerService _dataProgressTrackerService; - private readonly IPackageRestoreDataSource _dataSource; - private readonly IProjectSubscriptionService _projectSubscriptionService; - - private IDataProgressTrackerServiceRegistration? _progressRegistration; - private IDisposable? _subscription; - private IDisposable? _joinedDataSources; - - public PackageRestoreProgressTrackerInstance( - ConfiguredProject project, - IProjectThreadingService threadingService, - IProjectFaultHandlerService projectFaultHandlerService, - IDataProgressTrackerService dataProgressTrackerService, - IPackageRestoreDataSource dataSource, - IProjectSubscriptionService projectSubscriptionService) - : base(threadingService.JoinableTaskContext) - { - ConfiguredProject = project; - _projectFaultHandlerService = projectFaultHandlerService; - _dataProgressTrackerService = dataProgressTrackerService; - _dataSource = dataSource; - _projectSubscriptionService = projectSubscriptionService; - } + ConfiguredProject = project; + _projectFaultHandlerService = projectFaultHandlerService; + _dataProgressTrackerService = dataProgressTrackerService; + _dataSource = dataSource; + _projectSubscriptionService = projectSubscriptionService; + } - public ConfiguredProject ConfiguredProject { get; } + public ConfiguredProject ConfiguredProject { get; } - public string? OperationProgressStageId => StageId.IntelliSense; + public string? OperationProgressStageId => StageId.IntelliSense; - public string Name => nameof(PackageRestoreProgressTracker); + public string Name => nameof(PackageRestoreProgressTracker); - public string DisplayMessage => string.Empty; + public string DisplayMessage => string.Empty; - public Task InitializeAsync() - { - return InitializeAsync(CancellationToken.None); - } + public Task InitializeAsync() + { + return InitializeAsync(CancellationToken.None); + } - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) - { - _joinedDataSources = ProjectDataSources.JoinUpstreamDataSources(JoinableFactory, _projectFaultHandlerService, _projectSubscriptionService.ProjectSource, _dataSource); + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + _joinedDataSources = ProjectDataSources.JoinUpstreamDataSources(JoinableFactory, _projectFaultHandlerService, _projectSubscriptionService.ProjectSource, _dataSource); - _progressRegistration = _dataProgressTrackerService.RegisterOutputDataSource(this); + _progressRegistration = _dataProgressTrackerService.RegisterOutputDataSource(this); - Action>> action = OnRestoreCompleted; + Action>> action = OnRestoreCompleted; - _subscription = ProjectDataSources.SyncLinkTo( - _projectSubscriptionService.ProjectSource.SourceBlock.SyncLinkOptions(), - _dataSource.SourceBlock.SyncLinkOptions(), - DataflowBlockFactory.CreateActionBlock(action, ConfiguredProject.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), - linkOptions: DataflowOption.PropagateCompletion, - cancellationToken: cancellationToken); + _subscription = ProjectDataSources.SyncLinkTo( + _projectSubscriptionService.ProjectSource.SourceBlock.SyncLinkOptions(), + _dataSource.SourceBlock.SyncLinkOptions(), + DataflowBlockFactory.CreateActionBlock(action, ConfiguredProject.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), + linkOptions: DataflowOption.PropagateCompletion, + cancellationToken: cancellationToken); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - internal void OnRestoreCompleted(IProjectVersionedValue> value) + internal void OnRestoreCompleted(IProjectVersionedValue> value) + { + if (IsRestoreUpToDate(value.Value.Item1, value.Value.Item2)) { - if (IsRestoreUpToDate(value.Value.Item1, value.Value.Item2)) - { - _progressRegistration!.NotifyOutputDataCalculated(value.DataSourceVersions); - } + _progressRegistration!.NotifyOutputDataCalculated(value.DataSourceVersions); } + } - private static bool IsRestoreUpToDate(IProjectSnapshot projectSnapshot, RestoreData restoreData) - { - // If restore failed, we treat as though it is up-to-date to avoid it forever being stuck out of date. - if (!restoreData.Succeeded) - return true; + private static bool IsRestoreUpToDate(IProjectSnapshot projectSnapshot, RestoreData restoreData) + { + // If restore failed, we treat as though it is up-to-date to avoid it forever being stuck out of date. + if (!restoreData.Succeeded) + return true; - DateTime lastEvaluationWriteTime = GetLastWriteTimeUtc(restoreData.ProjectAssetsFilePath, projectSnapshot); + DateTime lastEvaluationWriteTime = GetLastWriteTimeUtc(restoreData.ProjectAssetsFilePath, projectSnapshot); - return lastEvaluationWriteTime >= restoreData.ProjectAssetsLastWriteTimeUtc; - } + return lastEvaluationWriteTime >= restoreData.ProjectAssetsLastWriteTimeUtc; + } - private static DateTime GetLastWriteTimeUtc(string filePath, IProjectSnapshot projectSnapshot) - { - var projectSnapshot2 = (IProjectSnapshot2)projectSnapshot; + private static DateTime GetLastWriteTimeUtc(string filePath, IProjectSnapshot projectSnapshot) + { + var projectSnapshot2 = (IProjectSnapshot2)projectSnapshot; - // If the assets file wasn't included as part of the item, - // consider it up-to-date by return MaxValue, so that it is not forever stuck out-of-date. - return projectSnapshot2.AdditionalDependentFileTimes.GetValueOrDefault(filePath, DateTime.MaxValue); - } + // If the assets file wasn't included as part of the item, + // consider it up-to-date by return MaxValue, so that it is not forever stuck out-of-date. + return projectSnapshot2.AdditionalDependentFileTimes.GetValueOrDefault(filePath, DateTime.MaxValue); + } - protected override Task DisposeCoreAsync(bool initialized) - { - _subscription?.Dispose(); - _progressRegistration?.Dispose(); - _joinedDataSources?.Dispose(); + protected override Task DisposeCoreAsync(bool initialized) + { + _subscription?.Dispose(); + _progressRegistration?.Dispose(); + _joinedDataSources?.Dispose(); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public IDisposable? Join() - { - return JoinableCollection.Join(); - } + public IDisposable? Join() + { + return JoinableCollection.Join(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.cs index 7b98ea1aed..74a6912cc2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreProgressTracker.cs @@ -1,58 +1,57 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Responsible for reporting package restore progress to operation progress. +/// +[Export(typeof(IImplicitlyActiveConfigurationComponent))] +[AppliesTo(ProjectCapability.PackageReferences)] +internal partial class PackageRestoreProgressTracker : AbstractMultiLifetimeComponent, IImplicitlyActiveConfigurationComponent { - /// - /// Responsible for reporting package restore progress to operation progress. - /// - [Export(typeof(IImplicitlyActiveConfigurationComponent))] - [AppliesTo(ProjectCapability.PackageReferences)] - internal partial class PackageRestoreProgressTracker : AbstractMultiLifetimeComponent, IImplicitlyActiveConfigurationComponent - { - private readonly ConfiguredProject _project; - private readonly IProjectThreadingService _threadingService; - private readonly IProjectFaultHandlerService _projectFaultHandlerService; - private readonly IDataProgressTrackerService _dataProgressTrackerService; - private readonly IPackageRestoreDataSource _dataSource; - private readonly IProjectSubscriptionService _projectSubscriptionService; + private readonly ConfiguredProject _project; + private readonly IProjectThreadingService _threadingService; + private readonly IProjectFaultHandlerService _projectFaultHandlerService; + private readonly IDataProgressTrackerService _dataProgressTrackerService; + private readonly IPackageRestoreDataSource _dataSource; + private readonly IProjectSubscriptionService _projectSubscriptionService; - [ImportingConstructor] - public PackageRestoreProgressTracker( - ConfiguredProject project, - IProjectThreadingService threadingService, - IProjectFaultHandlerService projectFaultHandlerService, - IDataProgressTrackerService dataProgressTrackerService, - IPackageRestoreDataSource dataSource, - IProjectSubscriptionService projectSubscriptionService) - : base(threadingService.JoinableTaskContext) - { - _project = project; - _threadingService = threadingService; - _projectFaultHandlerService = projectFaultHandlerService; - _dataProgressTrackerService = dataProgressTrackerService; - _dataSource = dataSource; - _projectSubscriptionService = projectSubscriptionService; - } + [ImportingConstructor] + public PackageRestoreProgressTracker( + ConfiguredProject project, + IProjectThreadingService threadingService, + IProjectFaultHandlerService projectFaultHandlerService, + IDataProgressTrackerService dataProgressTrackerService, + IPackageRestoreDataSource dataSource, + IProjectSubscriptionService projectSubscriptionService) + : base(threadingService.JoinableTaskContext) + { + _project = project; + _threadingService = threadingService; + _projectFaultHandlerService = projectFaultHandlerService; + _dataProgressTrackerService = dataProgressTrackerService; + _dataSource = dataSource; + _projectSubscriptionService = projectSubscriptionService; + } - public Task ActivateAsync() - { - return LoadAsync(); - } + public Task ActivateAsync() + { + return LoadAsync(); + } - public Task DeactivateAsync() - { - return UnloadAsync(); - } + public Task DeactivateAsync() + { + return UnloadAsync(); + } - protected override PackageRestoreProgressTrackerInstance CreateInstance() - { - return new PackageRestoreProgressTrackerInstance( - _project, - _threadingService, - _projectFaultHandlerService, - _dataProgressTrackerService, - _dataSource, - _projectSubscriptionService); - } + protected override PackageRestoreProgressTrackerInstance CreateInstance() + { + return new PackageRestoreProgressTrackerInstance( + _project, + _threadingService, + _projectFaultHandlerService, + _dataProgressTrackerService, + _dataSource, + _projectSubscriptionService); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreSharedJoinableTaskCollection.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreSharedJoinableTaskCollection.cs index 34d7128b2d..8ccb5f2b71 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreSharedJoinableTaskCollection.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreSharedJoinableTaskCollection.cs @@ -3,22 +3,21 @@ using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +[Export(typeof(PackageRestoreSharedJoinableTaskCollection))] +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal class PackageRestoreSharedJoinableTaskCollection : IJoinableTaskScope { - [Export(typeof(PackageRestoreSharedJoinableTaskCollection))] - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal class PackageRestoreSharedJoinableTaskCollection : IJoinableTaskScope + [ImportingConstructor] + public PackageRestoreSharedJoinableTaskCollection(IProjectThreadingService threadingService) { - [ImportingConstructor] - public PackageRestoreSharedJoinableTaskCollection(IProjectThreadingService threadingService) - { - JoinableTaskCollection = threadingService.JoinableTaskContext.CreateCollection(); - JoinableTaskCollection.DisplayName = nameof(PackageRestoreSharedJoinableTaskCollection); - JoinableTaskFactory = threadingService.JoinableTaskContext.CreateFactory(JoinableTaskCollection); - } + JoinableTaskCollection = threadingService.JoinableTaskContext.CreateCollection(); + JoinableTaskCollection.DisplayName = nameof(PackageRestoreSharedJoinableTaskCollection); + JoinableTaskFactory = threadingService.JoinableTaskContext.CreateFactory(JoinableTaskCollection); + } - public JoinableTaskCollection JoinableTaskCollection { get; } + public JoinableTaskCollection JoinableTaskCollection { get; } - public JoinableTaskFactory JoinableTaskFactory { get; } - } + public JoinableTaskFactory JoinableTaskFactory { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreUnconfiguredInputDataSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreUnconfiguredInputDataSource.cs index 29fdc11277..1390bd95c6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreUnconfiguredInputDataSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/PackageRestoreUnconfiguredInputDataSource.cs @@ -5,219 +5,218 @@ using Microsoft.VisualStudio.ProjectSystem.Utilities; using RestoreInfo = Microsoft.VisualStudio.ProjectSystem.IProjectVersionedValue; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +[Export(typeof(IPackageRestoreUnconfiguredInputDataSource))] +[AppliesTo(ProjectCapability.PackageReferences)] +[method: ImportingConstructor] +internal class PackageRestoreUnconfiguredInputDataSource( + UnconfiguredProject project, + IActiveConfiguredProjectProvider activeConfiguredProjectProvider, + IActiveConfigurationGroupService activeConfigurationGroupService) + : ChainedProjectValueDataSourceBase( + project, + synchronousDisposal: false, + registerDataSource: false), + IPackageRestoreUnconfiguredInputDataSource { - [Export(typeof(IPackageRestoreUnconfiguredInputDataSource))] - [AppliesTo(ProjectCapability.PackageReferences)] - [method: ImportingConstructor] - internal class PackageRestoreUnconfiguredInputDataSource( - UnconfiguredProject project, - IActiveConfiguredProjectProvider activeConfiguredProjectProvider, - IActiveConfigurationGroupService activeConfigurationGroupService) - : ChainedProjectValueDataSourceBase( - project, - synchronousDisposal: false, - registerDataSource: false), - IPackageRestoreUnconfiguredInputDataSource + protected override IDisposable LinkExternalInput(ITargetBlock targetBlock) { - protected override IDisposable LinkExternalInput(ITargetBlock targetBlock) + // At a high-level, we want to combine all implicitly active configurations (ie the active config of each TFM) restore data + // (via ProjectRestoreUpdate) and combine it into a single ProjectRestoreInfo instance and publish that. When a change is + // made to a configuration, such as adding a PackageReference, we should react to it and push a new version of our output. If the + // active configuration changes, we should react to it, and publish data from the new set of implicitly active configurations. + + // Merge across configurations + var joinBlock = new ConfiguredProjectDataSourceJoinBlock( + project => project.Services.ExportProvider.GetExportedValueOrDefault(), + JoinableFactory, + ContainingProject!); + + // Transform all restore data -> combined restore data + IPropagatorBlock, ConfiguredProject)>, RestoreInfo> transformBlock = + DataflowBlockSlim.CreateTransformBlock, ConfiguredProject)>, RestoreInfo>( + transformFunction: update => update.Derive(MergeRestoreInputs)); + + // Sync link in the active configuration + IDisposable syncLink = ProjectDataSources.SyncLinkTo( + joinBlock.SyncLinkOptions(), + activeConfiguredProjectProvider.SourceBlock.SyncLinkOptions(), + target: transformBlock, + linkOptions: DataflowOption.PropagateCompletion); + + JoinUpstreamDataSources(activeConfigurationGroupService.ActiveConfiguredProjectGroupSource, activeConfiguredProjectProvider); + + // Set the link up so that we publish changes to target block + transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + + return new DisposableBag { - // At a high-level, we want to combine all implicitly active configurations (ie the active config of each TFM) restore data - // (via ProjectRestoreUpdate) and combine it into a single ProjectRestoreInfo instance and publish that. When a change is - // made to a configuration, such as adding a PackageReference, we should react to it and push a new version of our output. If the - // active configuration changes, we should react to it, and publish data from the new set of implicitly active configurations. - - // Merge across configurations - var joinBlock = new ConfiguredProjectDataSourceJoinBlock( - project => project.Services.ExportProvider.GetExportedValueOrDefault(), - JoinableFactory, - ContainingProject!); - - // Transform all restore data -> combined restore data - IPropagatorBlock, ConfiguredProject)>, RestoreInfo> transformBlock = - DataflowBlockSlim.CreateTransformBlock, ConfiguredProject)>, RestoreInfo>( - transformFunction: update => update.Derive(MergeRestoreInputs)); - - // Sync link in the active configuration - IDisposable syncLink = ProjectDataSources.SyncLinkTo( - joinBlock.SyncLinkOptions(), - activeConfiguredProjectProvider.SourceBlock.SyncLinkOptions(), - target: transformBlock, - linkOptions: DataflowOption.PropagateCompletion); - - JoinUpstreamDataSources(activeConfigurationGroupService.ActiveConfiguredProjectGroupSource, activeConfiguredProjectProvider); - - // Set the link up so that we publish changes to target block - transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - - return new DisposableBag - { - joinBlock, - syncLink, - - // Link the active configured projects to our join block - activeConfigurationGroupService.ActiveConfiguredProjectGroupSource.SourceBlock.LinkTo(joinBlock, DataflowOption.PropagateCompletion), - }; - } - - private PackageRestoreUnconfiguredInput MergeRestoreInputs((IReadOnlyList Inputs, ConfiguredProject ActiveConfiguredProject) data) - { - (IReadOnlyList inputs, ConfiguredProject activeConfiguredProject) = data; + joinBlock, + syncLink, - // If there are no updates, we have no active configurations - ProjectRestoreInfo? restoreInfo = null; + // Link the active configured projects to our join block + activeConfigurationGroupService.ActiveConfiguredProjectGroupSource.SourceBlock.LinkTo(joinBlock, DataflowOption.PropagateCompletion), + }; + } - if (inputs.Count is not 0) - { - // We need to combine the snapshots from each implicitly active configuration (ie per TFM), - // resolving any conflicts, which we'll report to the user. - string msBuildProjectExtensionsPath = ResolveMSBuildProjectExtensionsPathConflicts(inputs); - string originalTargetFrameworks = ResolveOriginalTargetFrameworksConflicts(inputs); - string projectAssetsFilePath = ResolveProjectAssetsFilePathConflicts(inputs); - ImmutableArray toolReferences = ResolveToolReferenceConflicts(inputs); - ImmutableArray targetFrameworks = GetAllTargetFrameworks(inputs); - - restoreInfo = new ProjectRestoreInfo( - msBuildProjectExtensionsPath, - projectAssetsFilePath, - originalTargetFrameworks, - targetFrameworks, - toolReferences); - } + private PackageRestoreUnconfiguredInput MergeRestoreInputs((IReadOnlyList Inputs, ConfiguredProject ActiveConfiguredProject) data) + { + (IReadOnlyList inputs, ConfiguredProject activeConfiguredProject) = data; - return new PackageRestoreUnconfiguredInput(restoreInfo, inputs, activeConfiguredProject.ProjectConfiguration); - } + // If there are no updates, we have no active configurations + ProjectRestoreInfo? restoreInfo = null; - private string ResolveProjectAssetsFilePathConflicts(IReadOnlyCollection updates) + if (inputs.Count is not 0) { - // All configurations need to agree on where the project-wide asset file is located. - return ResolvePropertyConflicts(updates, u => u.ProjectAssetsFilePath, NuGetRestore.ProjectAssetsFileProperty); + // We need to combine the snapshots from each implicitly active configuration (ie per TFM), + // resolving any conflicts, which we'll report to the user. + string msBuildProjectExtensionsPath = ResolveMSBuildProjectExtensionsPathConflicts(inputs); + string originalTargetFrameworks = ResolveOriginalTargetFrameworksConflicts(inputs); + string projectAssetsFilePath = ResolveProjectAssetsFilePathConflicts(inputs); + ImmutableArray toolReferences = ResolveToolReferenceConflicts(inputs); + ImmutableArray targetFrameworks = GetAllTargetFrameworks(inputs); + + restoreInfo = new ProjectRestoreInfo( + msBuildProjectExtensionsPath, + projectAssetsFilePath, + originalTargetFrameworks, + targetFrameworks, + toolReferences); } - private string ResolveMSBuildProjectExtensionsPathConflicts(IReadOnlyCollection updates) - { - // All configurations need to agree on where the project-wide extensions path is located - return ResolvePropertyConflicts(updates, u => u.MSBuildProjectExtensionsPath, NuGetRestore.MSBuildProjectExtensionsPathProperty); - } + return new PackageRestoreUnconfiguredInput(restoreInfo, inputs, activeConfiguredProject.ProjectConfiguration); + } - private string ResolveOriginalTargetFrameworksConflicts(IReadOnlyCollection updates) - { - // All configurations need to agree on what the overall "user-written" frameworks for the - // project so that conditions in the project-wide 'nuget.g.props' and 'nuget.g.targets' - // are written and evaluated correctly. - return ResolvePropertyConflicts(updates, u => u.OriginalTargetFrameworks, NuGetRestore.TargetFrameworksProperty); - } + private string ResolveProjectAssetsFilePathConflicts(IReadOnlyCollection updates) + { + // All configurations need to agree on where the project-wide asset file is located. + return ResolvePropertyConflicts(updates, u => u.ProjectAssetsFilePath, NuGetRestore.ProjectAssetsFileProperty); + } - private string ResolvePropertyConflicts(IReadOnlyCollection updates, Func propertyGetter, string propertyName) - { - // Always use the first TFM listed in project to provide consistent behavior - PackageRestoreConfiguredInput update = updates.First(); - string propertyValue = propertyGetter(update.RestoreInfo); + private string ResolveMSBuildProjectExtensionsPathConflicts(IReadOnlyCollection updates) + { + // All configurations need to agree on where the project-wide extensions path is located + return ResolvePropertyConflicts(updates, u => u.MSBuildProjectExtensionsPath, NuGetRestore.MSBuildProjectExtensionsPathProperty); + } - // Every config should had same value - bool hasConflicts = updates.Select(u => propertyGetter(u.RestoreInfo)) - .Distinct(StringComparers.PropertyNames) - .Skip(1) - .Any(); + private string ResolveOriginalTargetFrameworksConflicts(IReadOnlyCollection updates) + { + // All configurations need to agree on what the overall "user-written" frameworks for the + // project so that conditions in the project-wide 'nuget.g.props' and 'nuget.g.targets' + // are written and evaluated correctly. + return ResolvePropertyConflicts(updates, u => u.OriginalTargetFrameworks, NuGetRestore.TargetFrameworksProperty); + } - if (hasConflicts) - { - ReportUserFault(string.Format( - CultureInfo.CurrentCulture, - Resources.Restore_PropertyWithInconsistentValues, - propertyName, - propertyValue, - update.ProjectConfiguration)); - } + private string ResolvePropertyConflicts(IReadOnlyCollection updates, Func propertyGetter, string propertyName) + { + // Always use the first TFM listed in project to provide consistent behavior + PackageRestoreConfiguredInput update = updates.First(); + string propertyValue = propertyGetter(update.RestoreInfo); - return propertyValue; - } + // Every config should had same value + bool hasConflicts = updates.Select(u => propertyGetter(u.RestoreInfo)) + .Distinct(StringComparers.PropertyNames) + .Skip(1) + .Any(); - private ImmutableArray ResolveToolReferenceConflicts(IEnumerable updates) + if (hasConflicts) { - var references = new Dictionary(StringComparers.ItemNames); - - foreach (PackageRestoreConfiguredInput update in updates) - { - foreach (ReferenceItem reference in update.RestoreInfo.ToolReferences) - { - if (ValidateToolReference(references, reference)) - { - references.Add(reference.Name, reference); - } - } - } - - return ImmutableArray.CreateRange(references.Values); + ReportUserFault(string.Format( + CultureInfo.CurrentCulture, + Resources.Restore_PropertyWithInconsistentValues, + propertyName, + propertyValue, + update.ProjectConfiguration)); } - private ImmutableArray GetAllTargetFrameworks(IEnumerable updates) - { - var frameworks = ImmutableArray.CreateBuilder(); - foreach (PackageRestoreConfiguredInput update in updates) - { - Assumes.True(update.RestoreInfo.TargetFrameworks.Length == 1); + return propertyValue; + } - TargetFrameworkInfo framework = update.RestoreInfo.TargetFrameworks[0]; + private ImmutableArray ResolveToolReferenceConflicts(IEnumerable updates) + { + var references = new Dictionary(StringComparers.ItemNames); - if (ValidateTargetFramework(update.ProjectConfiguration, framework)) + foreach (PackageRestoreConfiguredInput update in updates) + { + foreach (ReferenceItem reference in update.RestoreInfo.ToolReferences) + { + if (ValidateToolReference(references, reference)) { - frameworks.Add(framework); + references.Add(reference.Name, reference); } } - - return frameworks.ToImmutable(); } - private bool ValidateToolReference(Dictionary existingReferences, ReferenceItem reference) + return ImmutableArray.CreateRange(references.Values); + } + private ImmutableArray GetAllTargetFrameworks(IEnumerable updates) + { + var frameworks = ImmutableArray.CreateBuilder(); + + foreach (PackageRestoreConfiguredInput update in updates) { - if (existingReferences.TryGetValue(reference.Name, out ReferenceItem? existingReference)) - { - // CLI tool references are project-wide, so if they have conflicts in names, - // they must have the same metadata, which avoids from having to condition - // them so that they only appear in one TFM. - if (!RestoreComparer.ReferenceItems.Equals(existingReference, reference)) - { - ReportUserFault(string.Format( - CultureInfo.CurrentCulture, - Resources.Restore_DuplicateToolReferenceItems, - existingReference.Name)); - } + Assumes.True(update.RestoreInfo.TargetFrameworks.Length == 1); - return false; - } + TargetFrameworkInfo framework = update.RestoreInfo.TargetFrameworks[0]; - return true; + if (ValidateTargetFramework(update.ProjectConfiguration, framework)) + { + frameworks.Add(framework); + } } - private bool ValidateTargetFramework(ProjectConfiguration projectConfiguration, TargetFrameworkInfo framework) + return frameworks.ToImmutable(); + } + + private bool ValidateToolReference(Dictionary existingReferences, ReferenceItem reference) + { + if (existingReferences.TryGetValue(reference.Name, out ReferenceItem? existingReference)) { - if (framework.TargetFrameworkMoniker.Length == 0) + // CLI tool references are project-wide, so if they have conflicts in names, + // they must have the same metadata, which avoids from having to condition + // them so that they only appear in one TFM. + if (!RestoreComparer.ReferenceItems.Equals(existingReference, reference)) { ReportUserFault(string.Format( CultureInfo.CurrentCulture, - Resources.Restore_EmptyTargetFrameworkMoniker, - projectConfiguration.Name)); - - return false; + Resources.Restore_DuplicateToolReferenceItems, + existingReference.Name)); } - return true; + return false; } - private void ReportUserFault(string message) + return true; + } + + private bool ValidateTargetFramework(ProjectConfiguration projectConfiguration, TargetFrameworkInfo framework) + { + if (framework.TargetFrameworkMoniker.Length == 0) { - try - { - throw new Exception(message); - } - catch (Exception ex) - { - ReportDataSourceUserFault( - ex, - ProjectFaultSeverity.LimitedFunctionality, - ContainingProject!); - } + ReportUserFault(string.Format( + CultureInfo.CurrentCulture, + Resources.Restore_EmptyTargetFrameworkMoniker, + projectConfiguration.Name)); + + return false; + } + + return true; + } + + private void ReportUserFault(string message) + { + try + { + throw new Exception(message); + } + catch (Exception ex) + { + ReportDataSourceUserFault( + ex, + ProjectFaultSeverity.LimitedFunctionality, + ContainingProject!); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreConfiguredInput.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreConfiguredInput.cs index f254d0e3a7..91d3a2ae57 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreConfiguredInput.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreConfiguredInput.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents restore input data for a single . +/// +internal class PackageRestoreConfiguredInput { - /// - /// Represents restore input data for a single . - /// - internal class PackageRestoreConfiguredInput + public PackageRestoreConfiguredInput(ProjectConfiguration projectConfiguration, ProjectRestoreInfo restoreInfo, IComparable configuredProjectVersion) { - public PackageRestoreConfiguredInput(ProjectConfiguration projectConfiguration, ProjectRestoreInfo restoreInfo, IComparable configuredProjectVersion) - { - ProjectConfiguration = projectConfiguration; - RestoreInfo = restoreInfo; - ConfiguredProjectVersion = configuredProjectVersion; - } + ProjectConfiguration = projectConfiguration; + RestoreInfo = restoreInfo; + ConfiguredProjectVersion = configuredProjectVersion; + } - /// - /// Gets the configuration of the this input was produced from. - /// - public ProjectConfiguration ProjectConfiguration { get; } + /// + /// Gets the configuration of the this input was produced from. + /// + public ProjectConfiguration ProjectConfiguration { get; } - /// - /// Gets the restore information produced in this input. - /// - public ProjectRestoreInfo RestoreInfo { get; } + /// + /// Gets the restore information produced in this input. + /// + public ProjectRestoreInfo RestoreInfo { get; } - /// - /// Get the project version produced in this input - /// - public IComparable ConfiguredProjectVersion { get; } - } + /// + /// Get the project version produced in this input + /// + public IComparable ConfiguredProjectVersion { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreUnconfiguredInput.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreUnconfiguredInput.cs index 5cc744066b..616f9f0b32 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreUnconfiguredInput.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/PackageRestoreUnconfiguredInput.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents restore input data for an . +/// +internal sealed class PackageRestoreUnconfiguredInput( + ProjectRestoreInfo? restoreInfo, + IReadOnlyCollection configuredInputs, + ProjectConfiguration activeConfiguration) { /// - /// Represents restore input data for an . + /// Gets the restore information produced in this input. Can be if + /// the project has no active configurations. /// - internal sealed class PackageRestoreUnconfiguredInput( - ProjectRestoreInfo? restoreInfo, - IReadOnlyCollection configuredInputs, - ProjectConfiguration activeConfiguration) - { - /// - /// Gets the restore information produced in this input. Can be if - /// the project has no active configurations. - /// - public ProjectRestoreInfo? RestoreInfo { get; } = restoreInfo; + public ProjectRestoreInfo? RestoreInfo { get; } = restoreInfo; - /// - /// Gets the instances that contributed to . - /// - public IReadOnlyCollection ConfiguredInputs { get; } = configuredInputs; + /// + /// Gets the instances that contributed to . + /// + public IReadOnlyCollection ConfiguredInputs { get; } = configuredInputs; - /// - /// Gets the active configuration. - /// - public ProjectConfiguration ActiveConfiguration { get; } = activeConfiguration; - } + /// + /// Gets the active configuration. + /// + public ProjectConfiguration ActiveConfiguration { get; } = activeConfiguration; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectProperty.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectProperty.cs index 4d8ce5a459..c6b8b35209 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectProperty.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectProperty.cs @@ -2,24 +2,23 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents a single key/value for a . +/// +[DebuggerDisplay("{Name}: {Value}")] +internal class ProjectProperty { - /// - /// Represents a single key/value for a . - /// - [DebuggerDisplay("{Name}: {Value}")] - internal class ProjectProperty + public ProjectProperty(string name, string value) { - public ProjectProperty(string name, string value) - { - Requires.NotNullOrEmpty(name); + Requires.NotNullOrEmpty(name); - Name = name; - Value = value; - } + Name = name; + Value = value; + } - public string Name { get; } + public string Name { get; } - public string Value { get; } - } + public string Value { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectRestoreInfo.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectRestoreInfo.cs index 37f1e41c08..6bcfe1378b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectRestoreInfo.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ProjectRestoreInfo.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// A complete set of restore data for a project. +/// +internal class ProjectRestoreInfo { - /// - /// A complete set of restore data for a project. - /// - internal class ProjectRestoreInfo - { - // If additional fields/properties are added to this class, please update RestoreHasher + // If additional fields/properties are added to this class, please update RestoreHasher - public ProjectRestoreInfo(string msbuildProjectExtensionsPath, string projectAssetsFilePath, string originalTargetFrameworks, ImmutableArray targetFrameworks, ImmutableArray toolReferences) - { - MSBuildProjectExtensionsPath = msbuildProjectExtensionsPath; - ProjectAssetsFilePath = projectAssetsFilePath; - OriginalTargetFrameworks = originalTargetFrameworks; - TargetFrameworks = targetFrameworks; - ToolReferences = toolReferences; - } + public ProjectRestoreInfo(string msbuildProjectExtensionsPath, string projectAssetsFilePath, string originalTargetFrameworks, ImmutableArray targetFrameworks, ImmutableArray toolReferences) + { + MSBuildProjectExtensionsPath = msbuildProjectExtensionsPath; + ProjectAssetsFilePath = projectAssetsFilePath; + OriginalTargetFrameworks = originalTargetFrameworks; + TargetFrameworks = targetFrameworks; + ToolReferences = toolReferences; + } - public string MSBuildProjectExtensionsPath { get; } + public string MSBuildProjectExtensionsPath { get; } - public string ProjectAssetsFilePath { get; } + public string ProjectAssetsFilePath { get; } - public string OriginalTargetFrameworks { get; } + public string OriginalTargetFrameworks { get; } - public ImmutableArray TargetFrameworks { get; } + public ImmutableArray TargetFrameworks { get; } - public ImmutableArray ToolReferences { get; } - } + public ImmutableArray ToolReferences { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceItem.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceItem.cs index 6b0577e095..73702161f3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceItem.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceItem.cs @@ -2,26 +2,25 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents a single package, tool or project reference. +/// +[DebuggerDisplay("Name = {Name}")] +internal class ReferenceItem { - /// - /// Represents a single package, tool or project reference. - /// - [DebuggerDisplay("Name = {Name}")] - internal class ReferenceItem - { - // If additional fields/properties are added to this class, please update RestoreHasher + // If additional fields/properties are added to this class, please update RestoreHasher - public ReferenceItem(string name, IImmutableDictionary properties) - { - Requires.NotNullOrEmpty(name); + public ReferenceItem(string name, IImmutableDictionary properties) + { + Requires.NotNullOrEmpty(name); - Name = name; - Properties = properties; - } + Name = name; + Properties = properties; + } - public string Name { get; } + public string Name { get; } - public IImmutableDictionary Properties { get; } - } + public IImmutableDictionary Properties { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceProperty.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceProperty.cs index 99c213c723..cdad26abe3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceProperty.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/ReferenceProperty.cs @@ -2,24 +2,23 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents a single key/value for a . +/// +[DebuggerDisplay("{Name}: {Value}")] +internal class ReferenceProperty { - /// - /// Represents a single key/value for a . - /// - [DebuggerDisplay("{Name}: {Value}")] - internal class ReferenceProperty + public ReferenceProperty(string name, string value) { - public ReferenceProperty(string name, string value) - { - Requires.NotNullOrEmpty(name); + Requires.NotNullOrEmpty(name); - Name = name; - Value = value; - } + Name = name; + Value = value; + } - public string Name { get; } + public string Name { get; } - public string Value { get; } - } + public string Value { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreBuilder.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreBuilder.cs index 020e16e1d7..4487562c6f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreBuilder.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreBuilder.cs @@ -2,58 +2,57 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Contains builder methods for creating instances. +/// +internal static class RestoreBuilder { + public static readonly ImmutableArray EmptyTargetFrameworks = ImmutableArray.Empty; + public static readonly ImmutableArray EmptyReferences = ImmutableArray.Empty; + /// - /// Contains builder methods for creating instances. + /// Converts an immutable dictionary of rule snapshot data into an instance. /// - internal static class RestoreBuilder + public static ProjectRestoreInfo ToProjectRestoreInfo(IImmutableDictionary update) { - public static readonly ImmutableArray EmptyTargetFrameworks = ImmutableArray.Empty; - public static readonly ImmutableArray EmptyReferences = ImmutableArray.Empty; + IImmutableDictionary properties = update.GetSnapshotOrEmpty(NuGetRestore.SchemaName).Properties; + IProjectRuleSnapshot frameworkReferences = update.GetSnapshotOrEmpty(CollectedFrameworkReference.SchemaName); + IProjectRuleSnapshot packageDownloads = update.GetSnapshotOrEmpty(CollectedPackageDownload.SchemaName); + IProjectRuleSnapshot projectReferences = update.GetSnapshotOrEmpty(EvaluatedProjectReference.SchemaName); + IProjectRuleSnapshot packageReferences = update.GetSnapshotOrEmpty(CollectedPackageReference.SchemaName); + IProjectRuleSnapshot packageVersions = update.GetSnapshotOrEmpty(CollectedPackageVersion.SchemaName); + IProjectRuleSnapshot nuGetAuditSuppress = update.GetSnapshotOrEmpty(CollectedNuGetAuditSuppressions.SchemaName); + IProjectRuleSnapshot prunePackageReferences = update.GetSnapshotOrEmpty(CollectedPrunePackageReference.SchemaName); + IProjectRuleSnapshot toolReferences = update.GetSnapshotOrEmpty(DotNetCliToolReference.SchemaName); + + // For certain project types such as UWP, "TargetFrameworkMoniker" != the moniker that restore uses + string targetMoniker = properties.GetPropertyOrEmpty(NuGetRestore.NuGetTargetMonikerProperty); + if (targetMoniker.Length == 0) + targetMoniker = properties.GetPropertyOrEmpty(NuGetRestore.TargetFrameworkMonikerProperty); + + TargetFrameworkInfo frameworkInfo = new TargetFrameworkInfo( + targetMoniker, + ToReferenceItems(frameworkReferences.Items), + ToReferenceItems(packageDownloads.Items), + ToReferenceItems(projectReferences.Items), + ToReferenceItems(packageReferences.Items), + ToReferenceItems(packageVersions.Items), + ToReferenceItems(nuGetAuditSuppress.Items), + ToReferenceItems(prunePackageReferences.Items), + properties); + + return new ProjectRestoreInfo( + properties.GetPropertyOrEmpty(NuGetRestore.MSBuildProjectExtensionsPathProperty), + properties.GetPropertyOrEmpty(NuGetRestore.ProjectAssetsFileProperty), + properties.GetPropertyOrEmpty(NuGetRestore.TargetFrameworksProperty), + EmptyTargetFrameworks.Add(frameworkInfo), + ToReferenceItems(toolReferences.Items)); - /// - /// Converts an immutable dictionary of rule snapshot data into an instance. - /// - public static ProjectRestoreInfo ToProjectRestoreInfo(IImmutableDictionary update) + static ImmutableArray ToReferenceItems(IImmutableDictionary> items) { - IImmutableDictionary properties = update.GetSnapshotOrEmpty(NuGetRestore.SchemaName).Properties; - IProjectRuleSnapshot frameworkReferences = update.GetSnapshotOrEmpty(CollectedFrameworkReference.SchemaName); - IProjectRuleSnapshot packageDownloads = update.GetSnapshotOrEmpty(CollectedPackageDownload.SchemaName); - IProjectRuleSnapshot projectReferences = update.GetSnapshotOrEmpty(EvaluatedProjectReference.SchemaName); - IProjectRuleSnapshot packageReferences = update.GetSnapshotOrEmpty(CollectedPackageReference.SchemaName); - IProjectRuleSnapshot packageVersions = update.GetSnapshotOrEmpty(CollectedPackageVersion.SchemaName); - IProjectRuleSnapshot nuGetAuditSuppress = update.GetSnapshotOrEmpty(CollectedNuGetAuditSuppressions.SchemaName); - IProjectRuleSnapshot prunePackageReferences = update.GetSnapshotOrEmpty(CollectedPrunePackageReference.SchemaName); - IProjectRuleSnapshot toolReferences = update.GetSnapshotOrEmpty(DotNetCliToolReference.SchemaName); - - // For certain project types such as UWP, "TargetFrameworkMoniker" != the moniker that restore uses - string targetMoniker = properties.GetPropertyOrEmpty(NuGetRestore.NuGetTargetMonikerProperty); - if (targetMoniker.Length == 0) - targetMoniker = properties.GetPropertyOrEmpty(NuGetRestore.TargetFrameworkMonikerProperty); - - TargetFrameworkInfo frameworkInfo = new TargetFrameworkInfo( - targetMoniker, - ToReferenceItems(frameworkReferences.Items), - ToReferenceItems(packageDownloads.Items), - ToReferenceItems(projectReferences.Items), - ToReferenceItems(packageReferences.Items), - ToReferenceItems(packageVersions.Items), - ToReferenceItems(nuGetAuditSuppress.Items), - ToReferenceItems(prunePackageReferences.Items), - properties); - - return new ProjectRestoreInfo( - properties.GetPropertyOrEmpty(NuGetRestore.MSBuildProjectExtensionsPathProperty), - properties.GetPropertyOrEmpty(NuGetRestore.ProjectAssetsFileProperty), - properties.GetPropertyOrEmpty(NuGetRestore.TargetFrameworksProperty), - EmptyTargetFrameworks.Add(frameworkInfo), - ToReferenceItems(toolReferences.Items)); - - static ImmutableArray ToReferenceItems(IImmutableDictionary> items) - { - return items.ToImmutableArray(static (name, metadata) => new ReferenceItem(name, metadata)); - } + return items.ToImmutableArray(static (name, metadata) => new ReferenceItem(name, metadata)); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.ReferenceItemEqualityComparer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.ReferenceItemEqualityComparer.cs index 99fc937d1a..269a3a81f0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.ReferenceItemEqualityComparer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.ReferenceItemEqualityComparer.cs @@ -1,52 +1,51 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static partial class RestoreComparer { - internal static partial class RestoreComparer + internal class ReferenceItemEqualityComparer : EqualityComparer { - internal class ReferenceItemEqualityComparer : EqualityComparer + public override bool Equals(ReferenceItem? x, ReferenceItem? y) { - public override bool Equals(ReferenceItem? x, ReferenceItem? y) - { - if (x is null || y is null) - return x == y; + if (x is null || y is null) + return x == y; - if (!StringComparers.ItemNames.Equals(x.Name, y.Name)) - return false; + if (!StringComparers.ItemNames.Equals(x.Name, y.Name)) + return false; - if (!PropertiesAreEqual(x.Properties, y.Properties)) - return false; + if (!PropertiesAreEqual(x.Properties, y.Properties)) + return false; - return true; + return true; + + static bool PropertiesAreEqual(IImmutableDictionary x, IImmutableDictionary y) + { + if (x.Count != y.Count) + return false; - static bool PropertiesAreEqual(IImmutableDictionary x, IImmutableDictionary y) + foreach ((string xKey, string xValue) in x) { - if (x.Count != y.Count) + if (!y.TryGetValue(xKey, out string yValue)) + { return false; + } - foreach ((string xKey, string xValue) in x) + if (!StringComparers.PropertyValues.Equals(xValue, yValue)) { - if (!y.TryGetValue(xKey, out string yValue)) - { - return false; - } - - if (!StringComparers.PropertyValues.Equals(xValue, yValue)) - { - return false; - } + return false; } - return true; } + return true; } + } - public override int GetHashCode(ReferenceItem? obj) - { - if (obj is null) - return 0; + public override int GetHashCode(ReferenceItem? obj) + { + if (obj is null) + return 0; - return StringComparers.ItemNames.GetHashCode(obj.Name); - } + return StringComparers.ItemNames.GetHashCode(obj.Name); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.cs index 9c01a6cf35..ad2f0aee2a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreComparer.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static partial class RestoreComparer { - internal static partial class RestoreComparer - { - public static readonly IEqualityComparer ReferenceItems = new ReferenceItemEqualityComparer(); - } + public static readonly IEqualityComparer ReferenceItems = new ReferenceItemEqualityComparer(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreData.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreData.cs index 0da5ee296f..99f40e3c5b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreData.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreData.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents restore data for a package restore operation. +/// +internal class RestoreData { - /// - /// Represents restore data for a package restore operation. - /// - internal class RestoreData + public RestoreData(string projectAssetsFilePath, DateTime projectAssetsLastWriteTimeUtc, bool succeeded = true) { - public RestoreData(string projectAssetsFilePath, DateTime projectAssetsLastWriteTimeUtc, bool succeeded = true) - { - ProjectAssetsFilePath = projectAssetsFilePath; - ProjectAssetsLastWriteTimeUtc = projectAssetsLastWriteTimeUtc; - Succeeded = succeeded; - } + ProjectAssetsFilePath = projectAssetsFilePath; + ProjectAssetsLastWriteTimeUtc = projectAssetsLastWriteTimeUtc; + Succeeded = succeeded; + } - /// - /// Gets the last write time of the assets file at the end of the last restore - /// or if the file did not exist. - /// - public DateTime ProjectAssetsLastWriteTimeUtc { get; } + /// + /// Gets the last write time of the assets file at the end of the last restore + /// or if the file did not exist. + /// + public DateTime ProjectAssetsLastWriteTimeUtc { get; } - /// - /// Gets the file path of the assets file. - /// - public string ProjectAssetsFilePath { get; } + /// + /// Gets the file path of the assets file. + /// + public string ProjectAssetsFilePath { get; } - /// - /// Gets an indication if the restore was successful. - /// - public bool Succeeded { get; } - } + /// + /// Gets an indication if the restore was successful. + /// + public bool Succeeded { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreHasher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreHasher.cs index 0654c41b94..c20df0ae62 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreHasher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreHasher.cs @@ -2,67 +2,66 @@ using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static class RestoreHasher { - internal static class RestoreHasher + public static Hash CalculateHash(ProjectRestoreInfo restoreInfo) { - public static Hash CalculateHash(ProjectRestoreInfo restoreInfo) - { - Requires.NotNull(restoreInfo); + Requires.NotNull(restoreInfo); - using var hasher = new IncrementalHasher(); + using var hasher = new IncrementalHasher(); - hasher.AppendProperty(nameof(restoreInfo.ProjectAssetsFilePath), restoreInfo.ProjectAssetsFilePath); - hasher.AppendProperty(nameof(restoreInfo.MSBuildProjectExtensionsPath), restoreInfo.MSBuildProjectExtensionsPath); - hasher.AppendProperty(nameof(restoreInfo.OriginalTargetFrameworks), restoreInfo.OriginalTargetFrameworks); + hasher.AppendProperty(nameof(restoreInfo.ProjectAssetsFilePath), restoreInfo.ProjectAssetsFilePath); + hasher.AppendProperty(nameof(restoreInfo.MSBuildProjectExtensionsPath), restoreInfo.MSBuildProjectExtensionsPath); + hasher.AppendProperty(nameof(restoreInfo.OriginalTargetFrameworks), restoreInfo.OriginalTargetFrameworks); - foreach (TargetFrameworkInfo framework in restoreInfo.TargetFrameworks) - { - hasher.AppendProperty(nameof(framework.TargetFrameworkMoniker), framework.TargetFrameworkMoniker); - hasher.AppendFrameworkProperties(framework); - hasher.AppendReferences(framework.ProjectReferences); - hasher.AppendReferences(framework.PackageReferences); - hasher.AppendReferences(framework.FrameworkReferences); - hasher.AppendReferences(framework.PackageDownloads); - hasher.AppendReferences(framework.CentralPackageVersions); - hasher.AppendReferences(framework.NuGetAuditSuppress); - } + foreach (TargetFrameworkInfo framework in restoreInfo.TargetFrameworks) + { + hasher.AppendProperty(nameof(framework.TargetFrameworkMoniker), framework.TargetFrameworkMoniker); + hasher.AppendFrameworkProperties(framework); + hasher.AppendReferences(framework.ProjectReferences); + hasher.AppendReferences(framework.PackageReferences); + hasher.AppendReferences(framework.FrameworkReferences); + hasher.AppendReferences(framework.PackageDownloads); + hasher.AppendReferences(framework.CentralPackageVersions); + hasher.AppendReferences(framework.NuGetAuditSuppress); + } - AppendReferences(hasher, restoreInfo.ToolReferences); + AppendReferences(hasher, restoreInfo.ToolReferences); - return hasher.GetHashAndReset(); - } + return hasher.GetHashAndReset(); + } - private static void AppendFrameworkProperties(this IncrementalHasher hasher, TargetFrameworkInfo framework) + private static void AppendFrameworkProperties(this IncrementalHasher hasher, TargetFrameworkInfo framework) + { + foreach ((string key, string value) in framework.Properties) { - foreach ((string key, string value) in framework.Properties) - { - AppendProperty(hasher, key, value); - } + AppendProperty(hasher, key, value); } + } - private static void AppendReferences(this IncrementalHasher hasher, ImmutableArray references) + private static void AppendReferences(this IncrementalHasher hasher, ImmutableArray references) + { + foreach (ReferenceItem reference in references) { - foreach (ReferenceItem reference in references) - { - AppendProperty(hasher, nameof(reference.Name), reference.Name); - AppendReferenceProperties(hasher, reference); - } + AppendProperty(hasher, nameof(reference.Name), reference.Name); + AppendReferenceProperties(hasher, reference); } + } - private static void AppendReferenceProperties(this IncrementalHasher hasher, ReferenceItem reference) + private static void AppendReferenceProperties(this IncrementalHasher hasher, ReferenceItem reference) + { + foreach ((string key, string value) in reference.Properties) { - foreach ((string key, string value) in reference.Properties) - { - AppendProperty(hasher, key, value); - } + AppendProperty(hasher, key, value); } + } - private static void AppendProperty(this IncrementalHasher hasher, string name, string value) - { - hasher.Append(name); - hasher.Append("|"); - hasher.Append(value); - } + private static void AppendProperty(this IncrementalHasher hasher, string name, string value) + { + hasher.Append(name); + hasher.Append("|"); + hasher.Append(value); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreLogger.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreLogger.cs index 4a73a27afa..7169f3d054 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreLogger.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/RestoreLogger.cs @@ -2,96 +2,95 @@ using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static class RestoreLogger { - internal static class RestoreLogger + public static void BeginNominateRestore(IManagedProjectDiagnosticOutputService logger, string fullPath, ProjectRestoreInfo projectRestoreInfo) { - public static void BeginNominateRestore(IManagedProjectDiagnosticOutputService logger, string fullPath, ProjectRestoreInfo projectRestoreInfo) + if (logger.IsEnabled) { - if (logger.IsEnabled) - { - using var batch = new BatchLogger(logger); - batch.WriteLine(); - batch.WriteLine("------------------------------------------"); - batch.WriteLine($"BEGIN Nominate Restore for {fullPath}"); - batch.IndentLevel++; - - batch.WriteLine($"MSBuildProjectExtensionsPath: {projectRestoreInfo.MSBuildProjectExtensionsPath}"); - batch.WriteLine($"OriginalTargetFrameworks: {projectRestoreInfo.OriginalTargetFrameworks}"); - LogTargetFrameworks(batch, projectRestoreInfo.TargetFrameworks); - LogReferenceItems(batch, "Tool References", projectRestoreInfo.ToolReferences); - - batch.IndentLevel--; - batch.WriteLine(); - } + using var batch = new BatchLogger(logger); + batch.WriteLine(); + batch.WriteLine("------------------------------------------"); + batch.WriteLine($"BEGIN Nominate Restore for {fullPath}"); + batch.IndentLevel++; + + batch.WriteLine($"MSBuildProjectExtensionsPath: {projectRestoreInfo.MSBuildProjectExtensionsPath}"); + batch.WriteLine($"OriginalTargetFrameworks: {projectRestoreInfo.OriginalTargetFrameworks}"); + LogTargetFrameworks(batch, projectRestoreInfo.TargetFrameworks); + LogReferenceItems(batch, "Tool References", projectRestoreInfo.ToolReferences); + + batch.IndentLevel--; + batch.WriteLine(); } + } - public static void EndNominateRestore(IManagedProjectDiagnosticOutputService logger, string fullPath) + public static void EndNominateRestore(IManagedProjectDiagnosticOutputService logger, string fullPath) + { + if (logger.IsEnabled) { - if (logger.IsEnabled) - { - using var batch = new BatchLogger(logger); - batch.WriteLine(); - batch.WriteLine("------------------------------------------"); - batch.WriteLine($"COMPLETED Nominate Restore for {fullPath}"); - batch.WriteLine(); - } + using var batch = new BatchLogger(logger); + batch.WriteLine(); + batch.WriteLine("------------------------------------------"); + batch.WriteLine($"COMPLETED Nominate Restore for {fullPath}"); + batch.WriteLine(); } + } - private static void LogTargetFrameworks(BatchLogger logger, ImmutableArray targetFrameworks) - { - logger.WriteLine($"Target Frameworks ({targetFrameworks.Length})"); - logger.IndentLevel++; - - foreach (TargetFrameworkInfo tf in targetFrameworks) - { - LogTargetFramework(logger, tf); - } + private static void LogTargetFrameworks(BatchLogger logger, ImmutableArray targetFrameworks) + { + logger.WriteLine($"Target Frameworks ({targetFrameworks.Length})"); + logger.IndentLevel++; - logger.IndentLevel--; + foreach (TargetFrameworkInfo tf in targetFrameworks) + { + LogTargetFramework(logger, tf); } - private static void LogTargetFramework(BatchLogger logger, TargetFrameworkInfo targetFrameworkInfo) - { - logger.WriteLine(targetFrameworkInfo.TargetFrameworkMoniker); - logger.IndentLevel++; + logger.IndentLevel--; + } - LogReferenceItems(logger, "Framework References", targetFrameworkInfo.FrameworkReferences); - LogReferenceItems(logger, "Package Downloads", targetFrameworkInfo.PackageDownloads); - LogReferenceItems(logger, "Project References", targetFrameworkInfo.ProjectReferences); - LogReferenceItems(logger, "Package References", targetFrameworkInfo.PackageReferences); - LogReferenceItems(logger, "NuGet Audit Suppressions", targetFrameworkInfo.NuGetAuditSuppress); - LogReferenceItems(logger, "Prune Package References", targetFrameworkInfo.PrunePackageReferences); + private static void LogTargetFramework(BatchLogger logger, TargetFrameworkInfo targetFrameworkInfo) + { + logger.WriteLine(targetFrameworkInfo.TargetFrameworkMoniker); + logger.IndentLevel++; - // CPM typically adds a lot of items, normally the same set for all projects in the solution, and for individual projects - // many of the items aren't referenced by the project. While this is diagnostic logging, PackageVersions are particularly - // spammy, so we'll only output the count for basic problem investigation, rather than the full list. - logger.WriteLine($"Package Versions -- {targetFrameworkInfo.CentralPackageVersions.Length} (count only)"); + LogReferenceItems(logger, "Framework References", targetFrameworkInfo.FrameworkReferences); + LogReferenceItems(logger, "Package Downloads", targetFrameworkInfo.PackageDownloads); + LogReferenceItems(logger, "Project References", targetFrameworkInfo.ProjectReferences); + LogReferenceItems(logger, "Package References", targetFrameworkInfo.PackageReferences); + LogReferenceItems(logger, "NuGet Audit Suppressions", targetFrameworkInfo.NuGetAuditSuppress); + LogReferenceItems(logger, "Prune Package References", targetFrameworkInfo.PrunePackageReferences); - LogProperties(logger, "Target Framework Properties", targetFrameworkInfo.Properties); + // CPM typically adds a lot of items, normally the same set for all projects in the solution, and for individual projects + // many of the items aren't referenced by the project. While this is diagnostic logging, PackageVersions are particularly + // spammy, so we'll only output the count for basic problem investigation, rather than the full list. + logger.WriteLine($"Package Versions -- {targetFrameworkInfo.CentralPackageVersions.Length} (count only)"); - logger.IndentLevel--; - } + LogProperties(logger, "Target Framework Properties", targetFrameworkInfo.Properties); - private static void LogProperties(BatchLogger logger, string heading, IImmutableDictionary projectProperties) - { - IEnumerable properties = projectProperties.Select(prop => $"{prop.Key}:{prop.Value}"); - logger.WriteLine($"{heading} -- ({string.Join(" | ", properties)})"); - } + logger.IndentLevel--; + } - private static void LogReferenceItems(BatchLogger logger, string heading, ImmutableArray references) - { - logger.WriteLine(heading); - logger.IndentLevel++; + private static void LogProperties(BatchLogger logger, string heading, IImmutableDictionary projectProperties) + { + IEnumerable properties = projectProperties.Select(prop => $"{prop.Key}:{prop.Value}"); + logger.WriteLine($"{heading} -- ({string.Join(" | ", properties)})"); + } - foreach (ReferenceItem reference in references) - { - IEnumerable properties = reference.Properties.Select(prop => $"{prop.Key}:{prop.Value}"); + private static void LogReferenceItems(BatchLogger logger, string heading, ImmutableArray references) + { + logger.WriteLine(heading); + logger.IndentLevel++; - logger.WriteLine($"{reference.Name} -- ({string.Join(" | ", properties)})"); - } + foreach (ReferenceItem reference in references) + { + IEnumerable properties = reference.Properties.Select(prop => $"{prop.Key}:{prop.Value}"); - logger.IndentLevel--; + logger.WriteLine($"{reference.Name} -- ({string.Join(" | ", properties)})"); } + + logger.IndentLevel--; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/TargetFrameworkInfo.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/TargetFrameworkInfo.cs index 9c181eb8e9..98b1869815 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/TargetFrameworkInfo.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PackageRestore/Snapshots/TargetFrameworkInfo.cs @@ -2,53 +2,52 @@ using System.Diagnostics; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +/// +/// Represents the restore data for a single target framework in . +/// +[DebuggerDisplay("TargetFrameworkMoniker = {TargetFrameworkMoniker}")] +internal class TargetFrameworkInfo { - /// - /// Represents the restore data for a single target framework in . - /// - [DebuggerDisplay("TargetFrameworkMoniker = {TargetFrameworkMoniker}")] - internal class TargetFrameworkInfo + // If additional fields/properties are added to this class, please update RestoreHasher + public TargetFrameworkInfo( + string targetFrameworkMoniker, + ImmutableArray frameworkReferences, + ImmutableArray packageDownloads, + ImmutableArray projectReferences, + ImmutableArray packageReferences, + ImmutableArray centralPackageVersions, + ImmutableArray nuGetAuditSuppress, + ImmutableArray prunePackageReferences, + IImmutableDictionary properties) { - // If additional fields/properties are added to this class, please update RestoreHasher - public TargetFrameworkInfo( - string targetFrameworkMoniker, - ImmutableArray frameworkReferences, - ImmutableArray packageDownloads, - ImmutableArray projectReferences, - ImmutableArray packageReferences, - ImmutableArray centralPackageVersions, - ImmutableArray nuGetAuditSuppress, - ImmutableArray prunePackageReferences, - IImmutableDictionary properties) - { - TargetFrameworkMoniker = targetFrameworkMoniker; - FrameworkReferences = frameworkReferences; - PackageDownloads = packageDownloads; - ProjectReferences = projectReferences; - PackageReferences = packageReferences; - CentralPackageVersions = centralPackageVersions; - NuGetAuditSuppress = nuGetAuditSuppress; - PrunePackageReferences = prunePackageReferences; - Properties = properties; - } - - public string TargetFrameworkMoniker { get; } - - public ImmutableArray FrameworkReferences { get; } - - public ImmutableArray PackageDownloads { get; } - - public ImmutableArray PackageReferences { get; } - - public ImmutableArray ProjectReferences { get; } - - public ImmutableArray CentralPackageVersions { get; } - - public ImmutableArray NuGetAuditSuppress { get; } - - public ImmutableArray PrunePackageReferences { get; } - - public IImmutableDictionary Properties { get; } + TargetFrameworkMoniker = targetFrameworkMoniker; + FrameworkReferences = frameworkReferences; + PackageDownloads = packageDownloads; + ProjectReferences = projectReferences; + PackageReferences = packageReferences; + CentralPackageVersions = centralPackageVersions; + NuGetAuditSuppress = nuGetAuditSuppress; + PrunePackageReferences = prunePackageReferences; + Properties = properties; } + + public string TargetFrameworkMoniker { get; } + + public ImmutableArray FrameworkReferences { get; } + + public ImmutableArray PackageDownloads { get; } + + public ImmutableArray PackageReferences { get; } + + public ImmutableArray ProjectReferences { get; } + + public ImmutableArray CentralPackageVersions { get; } + + public ImmutableArray NuGetAuditSuppress { get; } + + public ImmutableArray PrunePackageReferences { get; } + + public IImmutableDictionary Properties { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTree.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTree.cs index 53cac2273f..5ff62129c3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTree.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTree.cs @@ -1,42 +1,41 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Export(typeof(IPhysicalProjectTree))] +internal class PhysicalProjectTree : IPhysicalProjectTree { - [Export(typeof(IPhysicalProjectTree))] - internal class PhysicalProjectTree : IPhysicalProjectTree - { - private readonly Lazy _treeService; - private readonly Lazy _treeProvider; - private readonly Lazy _treeStorage; + private readonly Lazy _treeService; + private readonly Lazy _treeProvider; + private readonly Lazy _treeStorage; - [ImportingConstructor] - public PhysicalProjectTree([Import(ExportContractNames.ProjectTreeProviders.PhysicalProjectTreeService)]Lazy treeService, - [Import(ExportContractNames.ProjectTreeProviders.PhysicalViewTree)]Lazy treeProvider, - Lazy treeStorage) - { - _treeService = treeService; - _treeProvider = treeProvider; - _treeStorage = treeStorage; - } + [ImportingConstructor] + public PhysicalProjectTree([Import(ExportContractNames.ProjectTreeProviders.PhysicalProjectTreeService)]Lazy treeService, + [Import(ExportContractNames.ProjectTreeProviders.PhysicalViewTree)]Lazy treeProvider, + Lazy treeStorage) + { + _treeService = treeService; + _treeProvider = treeProvider; + _treeStorage = treeStorage; + } - public IProjectTree? CurrentTree - { - get { return _treeService.Value.CurrentTree?.Tree; } - } + public IProjectTree? CurrentTree + { + get { return _treeService.Value.CurrentTree?.Tree; } + } - public IProjectTreeService TreeService - { - get { return _treeService.Value; } - } + public IProjectTreeService TreeService + { + get { return _treeService.Value; } + } - public IProjectTreeProvider TreeProvider - { - get { return _treeProvider.Value; } - } + public IProjectTreeProvider TreeProvider + { + get { return _treeProvider.Value; } + } - public IPhysicalProjectTreeStorage TreeStorage - { - get { return _treeStorage.Value; } - } + public IPhysicalProjectTreeStorage TreeStorage + { + get { return _treeStorage.Value; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.ConfiguredImports.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.ConfiguredImports.cs index ce27fb4261..16b68ee474 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.ConfiguredImports.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.ConfiguredImports.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class PhysicalProjectTreeStorage { - internal partial class PhysicalProjectTreeStorage + [Export] + internal class ConfiguredImports { - [Export] - internal class ConfiguredImports - { - public readonly IFolderManager FolderManager; - public readonly IProjectItemProvider SourceItemsProvider; + public readonly IFolderManager FolderManager; + public readonly IProjectItemProvider SourceItemsProvider; - [ImportingConstructor] - public ConfiguredImports(IFolderManager folderManager, [Import(ExportContractNames.ProjectItemProviders.SourceFiles)]IProjectItemProvider sourceItemsProvider) - { - FolderManager = folderManager; - SourceItemsProvider = sourceItemsProvider; - } + [ImportingConstructor] + public ConfiguredImports(IFolderManager folderManager, [Import(ExportContractNames.ProjectItemProviders.SourceFiles)]IProjectItemProvider sourceItemsProvider) + { + FolderManager = folderManager; + SourceItemsProvider = sourceItemsProvider; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.cs index 2e7f5847a6..bd35071956 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/PhysicalProjectTreeStorage.cs @@ -2,73 +2,72 @@ using Microsoft.VisualStudio.IO; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Export(typeof(IPhysicalProjectTreeStorage))] +internal partial class PhysicalProjectTreeStorage : IPhysicalProjectTreeStorage { - [Export(typeof(IPhysicalProjectTreeStorage))] - internal partial class PhysicalProjectTreeStorage : IPhysicalProjectTreeStorage + private readonly UnconfiguredProject _project; + private readonly IProjectTreeService _treeService; + private readonly Lazy _fileSystem; + private readonly IActiveConfiguredValue _configuredImports; + + [ImportingConstructor] + public PhysicalProjectTreeStorage( + UnconfiguredProject project, + [Import(ExportContractNames.ProjectTreeProviders.PhysicalProjectTreeService)]IProjectTreeService treeService, + Lazy fileSystem, + IActiveConfiguredValue configuredImports) { - private readonly UnconfiguredProject _project; - private readonly IProjectTreeService _treeService; - private readonly Lazy _fileSystem; - private readonly IActiveConfiguredValue _configuredImports; - - [ImportingConstructor] - public PhysicalProjectTreeStorage( - UnconfiguredProject project, - [Import(ExportContractNames.ProjectTreeProviders.PhysicalProjectTreeService)]IProjectTreeService treeService, - Lazy fileSystem, - IActiveConfiguredValue configuredImports) - { - _project = project; - _treeService = treeService; - _fileSystem = fileSystem; - _configuredImports = configuredImports; - } + _project = project; + _treeService = treeService; + _fileSystem = fileSystem; + _configuredImports = configuredImports; + } - public async Task AddFileAsync(string path) - { - Requires.NotNullOrEmpty(path); + public async Task AddFileAsync(string path) + { + Requires.NotNullOrEmpty(path); - string fullPath = _project.MakeRooted(path); + string fullPath = _project.MakeRooted(path); - await _configuredImports.Value.SourceItemsProvider.AddAsync(fullPath); + await _configuredImports.Value.SourceItemsProvider.AddAsync(fullPath); - await _treeService.PublishLatestTreeAsync(waitForFileSystemUpdates: false); - } + await _treeService.PublishLatestTreeAsync(waitForFileSystemUpdates: false); + } - public async Task CreateEmptyFileAsync(string path) - { - Requires.NotNullOrEmpty(path); + public async Task CreateEmptyFileAsync(string path) + { + Requires.NotNullOrEmpty(path); - string fullPath = _project.MakeRooted(path); + string fullPath = _project.MakeRooted(path); - _fileSystem.Value.Create(fullPath); + _fileSystem.Value.Create(fullPath); - await _configuredImports.Value.SourceItemsProvider.AddAsync(fullPath); + await _configuredImports.Value.SourceItemsProvider.AddAsync(fullPath); - await _treeService.PublishLatestTreeAsync(waitForFileSystemUpdates: true); - } + await _treeService.PublishLatestTreeAsync(waitForFileSystemUpdates: true); + } - public Task CreateFolderAsync(string path) - { - Requires.NotNullOrEmpty(path); + public Task CreateFolderAsync(string path) + { + Requires.NotNullOrEmpty(path); - string fullPath = _project.MakeRooted(path); + string fullPath = _project.MakeRooted(path); - _fileSystem.Value.CreateDirectory(fullPath); + _fileSystem.Value.CreateDirectory(fullPath); - return AddFolderAsync(fullPath); - } + return AddFolderAsync(fullPath); + } - public async Task AddFolderAsync(string path) - { - Requires.NotNullOrEmpty(path); + public async Task AddFolderAsync(string path) + { + Requires.NotNullOrEmpty(path); - string fullPath = _project.MakeRooted(path); + string fullPath = _project.MakeRooted(path); - await _configuredImports.Value.FolderManager.IncludeFolderInProjectAsync(fullPath, recursive: false); + await _configuredImports.Value.FolderManager.IncludeFolderInProjectAsync(fullPath, recursive: false); - await _treeService.PublishLatestTreeAsync(waitForFileSystemUpdates: false); - } + await _treeService.PublishLatestTreeAsync(waitForFileSystemUpdates: false); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectAccessor.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectAccessor.cs index d546a37cb2..13d5018da4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectAccessor.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectAccessor.cs @@ -5,138 +5,137 @@ #pragma warning disable RS0030 // symbol IProjectLockService is banned -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides an implementation of that delegates onto +/// the . +/// +[Export(typeof(IProjectAccessor))] +internal class ProjectAccessor : IProjectAccessor { - /// - /// Provides an implementation of that delegates onto - /// the . - /// - [Export(typeof(IProjectAccessor))] - internal class ProjectAccessor : IProjectAccessor + private readonly IProjectLockService _projectLockService; + + [ImportingConstructor] + public ProjectAccessor(IProjectLockService projectLockService) { - private readonly IProjectLockService _projectLockService; + _projectLockService = projectLockService; + } - [ImportingConstructor] - public ProjectAccessor(IProjectLockService projectLockService) - { - _projectLockService = projectLockService; - } + public async Task EnterWriteLockAsync(Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(action); - public async Task EnterWriteLockAsync(Func action, CancellationToken cancellationToken = default) + await _projectLockService.WriteLockAsync(async access => { - Requires.NotNull(action); + // Only async to let the caller call one of the other project accessor methods + await action(access.ProjectCollection, cancellationToken); - await _projectLockService.WriteLockAsync(async access => - { - // Only async to let the caller call one of the other project accessor methods - await action(access.ProjectCollection, cancellationToken); + // Avoid blocking thread on Dispose + await access.ReleaseAsync(); + }, cancellationToken); + } - // Avoid blocking thread on Dispose - await access.ReleaseAsync(); - }, cancellationToken); - } + public Task OpenProjectForReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(project); + Requires.NotNull(action); - public Task OpenProjectForReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) + return _projectLockService.ReadLockAsync(async access => { - Requires.NotNull(project); - Requires.NotNull(action); + Project evaluatedProject = await access.GetProjectAsync(project, cancellationToken); - return _projectLockService.ReadLockAsync(async access => - { - Project evaluatedProject = await access.GetProjectAsync(project, cancellationToken); + // Deliberately not async to reduce the type of + // code you can run while holding the lock. + return action(evaluatedProject); + }, cancellationToken); + } - // Deliberately not async to reduce the type of - // code you can run while holding the lock. - return action(evaluatedProject); - }, cancellationToken); - } + public Task OpenProjectForUpgradeableReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(project); + Requires.NotNull(action); - public Task OpenProjectForUpgradeableReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) + return _projectLockService.UpgradeableReadLockAsync(async access => { - Requires.NotNull(project); - Requires.NotNull(action); + Project evaluatedProject = await access.GetProjectAsync(project, cancellationToken); - return _projectLockService.UpgradeableReadLockAsync(async access => - { - Project evaluatedProject = await access.GetProjectAsync(project, cancellationToken); + // Deliberately not async to reduce the type of + // code you can run while holding the lock. + return action(evaluatedProject); + }, cancellationToken); + } - // Deliberately not async to reduce the type of - // code you can run while holding the lock. - return action(evaluatedProject); - }, cancellationToken); - } + public Task OpenProjectXmlForReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(project); + Requires.NotNull(action); - public Task OpenProjectXmlForReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) + return _projectLockService.ReadLockAsync(async access => { - Requires.NotNull(project); - Requires.NotNull(action); + ProjectRootElement rootElement = await access.GetProjectXmlAsync(project.FullPath, cancellationToken); - return _projectLockService.ReadLockAsync(async access => - { - ProjectRootElement rootElement = await access.GetProjectXmlAsync(project.FullPath, cancellationToken); + // Deliberately not async to reduce the type of + // code you can run while holding the lock. + return action(rootElement); + }, cancellationToken); + } - // Deliberately not async to reduce the type of - // code you can run while holding the lock. - return action(rootElement); - }, cancellationToken); - } + public async Task OpenProjectXmlForUpgradeableReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + Requires.NotNull(project); + Requires.NotNull(action); - public async Task OpenProjectXmlForUpgradeableReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) + await _projectLockService.UpgradeableReadLockAsync(async access => { - Requires.NotNull(project); - Requires.NotNull(action); + ProjectRootElement rootElement = await access.GetProjectXmlAsync(project.FullPath, cancellationToken); - await _projectLockService.UpgradeableReadLockAsync(async access => - { - ProjectRootElement rootElement = await access.GetProjectXmlAsync(project.FullPath, cancellationToken); + // Only async to let the caller upgrade to a + // write lock via OpenProjectXmlForWriteAsync + await action(rootElement, cancellationToken); + }, cancellationToken); + } - // Only async to let the caller upgrade to a - // write lock via OpenProjectXmlForWriteAsync - await action(rootElement, cancellationToken); - }, cancellationToken); - } + public async Task OpenProjectXmlForWriteAsync(UnconfiguredProject project, Action action, CancellationToken cancellationToken = default) + { + Requires.NotNull(project); + Requires.NotNull(action); - public async Task OpenProjectXmlForWriteAsync(UnconfiguredProject project, Action action, CancellationToken cancellationToken = default) + await _projectLockService.WriteLockAsync(async access => { - Requires.NotNull(project); - Requires.NotNull(action); + await access.CheckoutAsync(project.FullPath); - await _projectLockService.WriteLockAsync(async access => - { - await access.CheckoutAsync(project.FullPath); + ProjectRootElement rootElement = await access.GetProjectXmlAsync(project.FullPath, cancellationToken); - ProjectRootElement rootElement = await access.GetProjectXmlAsync(project.FullPath, cancellationToken); + // Deliberately not async to reduce the type of + // code you can run while holding the lock. + action(rootElement); - // Deliberately not async to reduce the type of - // code you can run while holding the lock. - action(rootElement); + // Avoid blocking thread on Dispose + await access.ReleaseAsync(); + }, cancellationToken); + } - // Avoid blocking thread on Dispose - await access.ReleaseAsync(); - }, cancellationToken); - } + public async Task OpenProjectForWriteAsync(ConfiguredProject project, Action action, ProjectCheckoutOption option = ProjectCheckoutOption.Checkout, CancellationToken cancellationToken = default) + { + Requires.NotNull(project); + Requires.NotNull(action); - public async Task OpenProjectForWriteAsync(ConfiguredProject project, Action action, ProjectCheckoutOption option = ProjectCheckoutOption.Checkout, CancellationToken cancellationToken = default) + await _projectLockService.WriteLockAsync(async access => { - Requires.NotNull(project); - Requires.NotNull(action); - - await _projectLockService.WriteLockAsync(async access => + if (option == ProjectCheckoutOption.Checkout) { - if (option == ProjectCheckoutOption.Checkout) - { - await access.CheckoutAsync(project.UnconfiguredProject.FullPath); - } + await access.CheckoutAsync(project.UnconfiguredProject.FullPath); + } - Project evaluatedProject = await access.GetProjectAsync(project, cancellationToken); + Project evaluatedProject = await access.GetProjectAsync(project, cancellationToken); - // Deliberately not async to reduce the type of - // code you can run while holding the lock. - action(evaluatedProject); + // Deliberately not async to reduce the type of + // code you can run while holding the lock. + action(evaluatedProject); - // Avoid blocking thread on Dispose - await access.ReleaseAsync(); - }, cancellationToken); - } + // Avoid blocking thread on Dispose + await access.ReleaseAsync(); + }, cancellationToken); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapabilitiesService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapabilitiesService.cs index f4467a2f3c..64b17ef47b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapabilitiesService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapabilitiesService.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides an implementation of that simply delegates +/// onto the method. +/// +[Export(typeof(IProjectCapabilitiesService))] +internal class ProjectCapabilitiesService : IProjectCapabilitiesService { - /// - /// Provides an implementation of that simply delegates - /// onto the method. - /// - [Export(typeof(IProjectCapabilitiesService))] - internal class ProjectCapabilitiesService : IProjectCapabilitiesService - { - private readonly UnconfiguredProject _project; + private readonly UnconfiguredProject _project; - [ImportingConstructor] - public ProjectCapabilitiesService(UnconfiguredProject project) - { - _project = project; - } + [ImportingConstructor] + public ProjectCapabilitiesService(UnconfiguredProject project) + { + _project = project; + } - public bool Contains(string capability) - { - Requires.NotNullOrEmpty(capability); + public bool Contains(string capability) + { + Requires.NotNullOrEmpty(capability); - // Just to check capabilities, requires static state and call context that we cannot influence - return _project.Capabilities.Contains(capability); - } + // Just to check capabilities, requires static state and call context that we cannot influence + return _project.Capabilities.Contains(capability); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapability.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapability.cs index 885ed49fcc..8930aa185e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapability.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCapability.cs @@ -1,59 +1,58 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides common well-known project flags. +/// +internal static class ProjectCapability { - /// - /// Provides common well-known project flags. - /// - internal static class ProjectCapability - { - public const string CSharp = ProjectCapabilities.CSharp; - public const string VisualBasic = ProjectCapabilities.VB; - public const string FSharp = nameof(FSharp); - public const string VisualBasicAppDesigner = ProjectCapabilities.VB + " & " + AppDesigner; - public const string CSharpAppDesigner = ProjectCapabilities.CSharp + " & " + AppDesigner; - public const string FSharpAppDesigner = FSharp + " & " + AppDesigner; - public const string CSharpOrFSharp = "(" + CSharp + " | " + FSharp + ")"; - public const string CSharpOrVisualBasic = "(" + ProjectCapabilities.CSharp + " | " + ProjectCapabilities.VB + ")"; - public const string CSharpOrVisualBasicLanguageService = CSharpOrVisualBasic + " & " + LanguageService; + public const string CSharp = ProjectCapabilities.CSharp; + public const string VisualBasic = ProjectCapabilities.VB; + public const string FSharp = nameof(FSharp); + public const string VisualBasicAppDesigner = ProjectCapabilities.VB + " & " + AppDesigner; + public const string CSharpAppDesigner = ProjectCapabilities.CSharp + " & " + AppDesigner; + public const string FSharpAppDesigner = FSharp + " & " + AppDesigner; + public const string CSharpOrFSharp = "(" + CSharp + " | " + FSharp + ")"; + public const string CSharpOrVisualBasic = "(" + ProjectCapabilities.CSharp + " | " + ProjectCapabilities.VB + ")"; + public const string CSharpOrVisualBasicLanguageService = CSharpOrVisualBasic + " & " + LanguageService; - public const string AppDesigner = nameof(AppDesigner); - public const string AppSettings = nameof(AppSettings); - public const string DependenciesTree = nameof(DependenciesTree); - public const string ProjectImportsTree = nameof(ProjectImportsTree); - public const string EditAndContinue = nameof(EditAndContinue); - public const string LaunchProfiles = nameof(LaunchProfiles); - public const string OpenProjectFile = nameof(OpenProjectFile); - public const string HandlesOwnReload = ProjectCapabilities.HandlesOwnReload; - public const string Pack = nameof(Pack); // Keep this in sync with Microsoft.VisualStudio.Editors.ProjectCapability.Pack - public const string PackageReferences = ProjectCapabilities.PackageReferences; - public const string PreserveFormatting = nameof(PreserveFormatting); - public const string ProjectConfigurationsDeclaredDimensions = ProjectCapabilities.ProjectConfigurationsDeclaredDimensions; - public const string LanguageService = nameof(LanguageService); - public const string DotNetLanguageService = DotNet + " & " + LanguageService; - public const string UseProjectEvaluationCache = ProjectCapabilities.UseProjectEvaluationCache; - public const string SingleTargetBuildForStartupProjects = nameof(SingleTargetBuildForStartupProjects); - public const string ProjectPropertyInterception = nameof(ProjectPropertyInterception); - public const string WindowsForms = nameof(WindowsForms); - public const string WPF = nameof(WPF); - public const string SupportUniversalAuthentication = nameof(SupportUniversalAuthentication); - public const string DiagnoseVisualStudioComponents = nameof(DiagnoseVisualStudioComponents); + public const string AppDesigner = nameof(AppDesigner); + public const string AppSettings = nameof(AppSettings); + public const string DependenciesTree = nameof(DependenciesTree); + public const string ProjectImportsTree = nameof(ProjectImportsTree); + public const string EditAndContinue = nameof(EditAndContinue); + public const string LaunchProfiles = nameof(LaunchProfiles); + public const string OpenProjectFile = nameof(OpenProjectFile); + public const string HandlesOwnReload = ProjectCapabilities.HandlesOwnReload; + public const string Pack = nameof(Pack); // Keep this in sync with Microsoft.VisualStudio.Editors.ProjectCapability.Pack + public const string PackageReferences = ProjectCapabilities.PackageReferences; + public const string PreserveFormatting = nameof(PreserveFormatting); + public const string ProjectConfigurationsDeclaredDimensions = ProjectCapabilities.ProjectConfigurationsDeclaredDimensions; + public const string LanguageService = nameof(LanguageService); + public const string DotNetLanguageService = DotNet + " & " + LanguageService; + public const string UseProjectEvaluationCache = ProjectCapabilities.UseProjectEvaluationCache; + public const string SingleTargetBuildForStartupProjects = nameof(SingleTargetBuildForStartupProjects); + public const string ProjectPropertyInterception = nameof(ProjectPropertyInterception); + public const string WindowsForms = nameof(WindowsForms); + public const string WPF = nameof(WPF); + public const string SupportUniversalAuthentication = nameof(SupportUniversalAuthentication); + public const string DiagnoseVisualStudioComponents = nameof(DiagnoseVisualStudioComponents); - public const string DotNet = ".NET"; - public const string DotNetCoreRazor = "DotNetCoreRazor"; + public const string DotNet = ".NET"; + public const string DotNetCoreRazor = "DotNetCoreRazor"; - /// - /// Instructs CPS to order tree items according to the property first. - /// This is in addition to the default ordering by , then by - /// or , and finally - /// alphabetical. - /// - public const string SortByDisplayOrder = ProjectCapabilities.SortByDisplayOrder; + /// + /// Instructs CPS to order tree items according to the property first. + /// This is in addition to the default ordering by , then by + /// or , and finally + /// alphabetical. + /// + public const string SortByDisplayOrder = ProjectCapabilities.SortByDisplayOrder; - /// - /// Enables commands and behaviour that allows reordering items in the tree. - /// Used by F# projects, for which item order is significant to compilation. - /// - public const string EditableDisplayOrder = nameof(EditableDisplayOrder); - } + /// + /// Enables commands and behaviour that allows reordering items in the tree. + /// Used by F# projects, for which item order is significant to compilation. + /// + public const string EditableDisplayOrder = nameof(EditableDisplayOrder); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectChangeDiff.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectChangeDiff.cs index 7b11da5099..d143bf07f1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectChangeDiff.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectChangeDiff.cs @@ -1,40 +1,39 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides a concrete implementation of +/// +internal class ProjectChangeDiff : IProjectChangeDiff { - /// - /// Provides a concrete implementation of - /// - internal class ProjectChangeDiff : IProjectChangeDiff + public ProjectChangeDiff( + IImmutableSet? addedItems = null, + IImmutableSet? removedItems = null, + IImmutableSet? changedItems = null, + IImmutableDictionary? renamedItems = null) + { + AddedItems = addedItems ?? ImmutableStringHashSet.EmptyOrdinal; + RemovedItems = removedItems ?? ImmutableStringHashSet.EmptyOrdinal; + ChangedItems = changedItems ?? ImmutableStringHashSet.EmptyOrdinal; + RenamedItems = renamedItems ?? ImmutableStringDictionary.EmptyOrdinal; + } + + public IImmutableSet AddedItems { get; } + + public IImmutableSet RemovedItems { get; } + + public IImmutableSet ChangedItems { get; } + + public IImmutableDictionary RenamedItems { get; } + + public IImmutableSet ChangedProperties + { + get { return ImmutableStringHashSet.EmptyOrdinal; } + } + + public bool AnyChanges { - public ProjectChangeDiff( - IImmutableSet? addedItems = null, - IImmutableSet? removedItems = null, - IImmutableSet? changedItems = null, - IImmutableDictionary? renamedItems = null) - { - AddedItems = addedItems ?? ImmutableStringHashSet.EmptyOrdinal; - RemovedItems = removedItems ?? ImmutableStringHashSet.EmptyOrdinal; - ChangedItems = changedItems ?? ImmutableStringHashSet.EmptyOrdinal; - RenamedItems = renamedItems ?? ImmutableStringDictionary.EmptyOrdinal; - } - - public IImmutableSet AddedItems { get; } - - public IImmutableSet RemovedItems { get; } - - public IImmutableSet ChangedItems { get; } - - public IImmutableDictionary RenamedItems { get; } - - public IImmutableSet ChangedProperties - { - get { return ImmutableStringHashSet.EmptyOrdinal; } - } - - public bool AnyChanges - { - get { return AddedItems.Count > 0 || RemovedItems.Count > 0 || ChangedItems.Count > 0 || RenamedItems.Count > 0; } - } + get { return AddedItems.Count > 0 || RemovedItems.Count > 0 || ChangedItems.Count > 0 || RenamedItems.Count > 0; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCheckoutOption.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCheckoutOption.cs index 3d08d56971..61c12310b7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCheckoutOption.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectCheckoutOption.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal enum ProjectCheckoutOption { - internal enum ProjectCheckoutOption - { - /// - /// Do not checkout the project from source control. - /// - DoNotCheckout, + /// + /// Do not checkout the project from source control. + /// + DoNotCheckout, - /// - /// Checkout the project from source control. - /// - Checkout, - } + /// + /// Checkout the project from source control. + /// + Checkout, } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectConfigurationExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectConfigurationExtensions.cs index 5aa8db8608..3c4dbdaf45 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectConfigurationExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectConfigurationExtensions.cs @@ -2,101 +2,100 @@ using System.Text; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ProjectConfigurationExtensions { - internal static class ProjectConfigurationExtensions + /// + /// Returns true if this is a cross-targeting project configuration with a "TargetFramework" dimension. + /// This is true for a project with an explicit "TargetFrameworks" property, but no "TargetFrameworkMoniker" or "TargetFramework" property. + /// + /// + internal static bool IsCrossTargeting(this ProjectConfiguration projectConfiguration) + { + return projectConfiguration.Dimensions.ContainsKey(ConfigurationGeneral.TargetFrameworkProperty); + } + + /// + /// Returns true if the given project configurations are equal ignoring the "TargetFramework" dimension. + /// + internal static bool EqualIgnoringTargetFramework(this ProjectConfiguration projectConfiguration1, ProjectConfiguration projectConfiguration2) { - /// - /// Returns true if this is a cross-targeting project configuration with a "TargetFramework" dimension. - /// This is true for a project with an explicit "TargetFrameworks" property, but no "TargetFrameworkMoniker" or "TargetFramework" property. - /// - /// - internal static bool IsCrossTargeting(this ProjectConfiguration projectConfiguration) + Requires.NotNull(projectConfiguration1); + Requires.NotNull(projectConfiguration2); + + if (projectConfiguration1.Dimensions.Count != projectConfiguration2.Dimensions.Count) { - return projectConfiguration.Dimensions.ContainsKey(ConfigurationGeneral.TargetFrameworkProperty); + return false; } - /// - /// Returns true if the given project configurations are equal ignoring the "TargetFramework" dimension. - /// - internal static bool EqualIgnoringTargetFramework(this ProjectConfiguration projectConfiguration1, ProjectConfiguration projectConfiguration2) + if (!projectConfiguration1.IsCrossTargeting() || !projectConfiguration2.IsCrossTargeting()) { - Requires.NotNull(projectConfiguration1); - Requires.NotNull(projectConfiguration2); - - if (projectConfiguration1.Dimensions.Count != projectConfiguration2.Dimensions.Count) - { - return false; - } + return projectConfiguration1.Equals(projectConfiguration2); + } - if (!projectConfiguration1.IsCrossTargeting() || !projectConfiguration2.IsCrossTargeting()) + foreach ((string dimensionName, string dimensionValue) in projectConfiguration1.Dimensions) + { + // Ignore the TargetFramework. + if (StringComparers.ConfigurationDimensionNames.Equals(dimensionName, ConfigurationGeneral.TargetFrameworkProperty)) { - return projectConfiguration1.Equals(projectConfiguration2); + continue; } - foreach ((string dimensionName, string dimensionValue) in projectConfiguration1.Dimensions) + // Dimension values must be compared in a case-sensitive manner. + if (!projectConfiguration2.Dimensions.TryGetValue(dimensionName, out string? activeValue) || + !string.Equals(dimensionValue, activeValue, StringComparisons.ConfigurationDimensionNames)) { - // Ignore the TargetFramework. - if (StringComparers.ConfigurationDimensionNames.Equals(dimensionName, ConfigurationGeneral.TargetFrameworkProperty)) - { - continue; - } - - // Dimension values must be compared in a case-sensitive manner. - if (!projectConfiguration2.Dimensions.TryGetValue(dimensionName, out string? activeValue) || - !string.Equals(dimensionValue, activeValue, StringComparisons.ConfigurationDimensionNames)) - { - return false; - } + return false; } - - return true; } - /// - /// Produces a string containing each dimension's value, in idiomatic order (configuration, platform, then any targets), - /// such as Debug|AnyCPU|net7.0. - /// - /// - /// Calling ToString on excludes the target framework from the first configuration, - /// meaning it is not suitable when the full set of dimensions is needed. - /// - /// - /// - internal static string GetDisplayString(this ProjectConfiguration projectConfiguration) - { - // ImmutableDictionary does not preserve order, so we manually produce the idiomatic order + return true; + } - IImmutableDictionary dims = projectConfiguration.Dimensions; + /// + /// Produces a string containing each dimension's value, in idiomatic order (configuration, platform, then any targets), + /// such as Debug|AnyCPU|net7.0. + /// + /// + /// Calling ToString on excludes the target framework from the first configuration, + /// meaning it is not suitable when the full set of dimensions is needed. + /// + /// + /// + internal static string GetDisplayString(this ProjectConfiguration projectConfiguration) + { + // ImmutableDictionary does not preserve order, so we manually produce the idiomatic order - var sb = new StringBuilder(); + IImmutableDictionary dims = projectConfiguration.Dimensions; - // Configuration and platform are always first - if (dims.TryGetValue(ConfigurationGeneral.ConfigurationProperty, out string? configuration)) - Append(configuration); - if (dims.TryGetValue(ConfigurationGeneral.PlatformProperty, out string? platform)) - Append(platform); + var sb = new StringBuilder(); - // Any other dimensions later - foreach ((string name, string value) in dims) - { - if (StringComparers.ConfigurationDimensionNames.Equals(name, ConfigurationGeneral.ConfigurationProperty) || - StringComparers.ConfigurationDimensionNames.Equals(name, ConfigurationGeneral.PlatformProperty)) - { - continue; - } + // Configuration and platform are always first + if (dims.TryGetValue(ConfigurationGeneral.ConfigurationProperty, out string? configuration)) + Append(configuration); + if (dims.TryGetValue(ConfigurationGeneral.PlatformProperty, out string? platform)) + Append(platform); - Append(value); + // Any other dimensions later + foreach ((string name, string value) in dims) + { + if (StringComparers.ConfigurationDimensionNames.Equals(name, ConfigurationGeneral.ConfigurationProperty) || + StringComparers.ConfigurationDimensionNames.Equals(name, ConfigurationGeneral.PlatformProperty)) + { + continue; } - return sb.ToString(); + Append(value); + } - void Append(string s) - { - if (sb.Length != 0) - sb.Append('|'); - sb.Append(s); - } + return sb.ToString(); + + void Append(string s) + { + if (sb.Length != 0) + sb.Append('|'); + sb.Append(s); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectFileClassifier.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectFileClassifier.cs index 1f68bf9303..c3321bd128 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectFileClassifier.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectFileClassifier.cs @@ -2,188 +2,187 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Attempts to classify project files for various purposes such as safety and performance. +/// +/// +/// +/// The term "project files" refers to the root project file (e.g. MyProject.csproj) and +/// any other .props and .targets files it imports. +/// +/// +/// Classifications provided are: +/// +/// +/// which indicates the file is not intended to be edited by the +/// user. It may be generated, or ship as part of Visual Studio, MSBuild, a NuGet package, etc. +/// +/// +/// which indicates the file is not expected to change over time, +/// other than when it is first created. This is a subset of non-user-editable files and +/// generally excludes generated files which can be regenerated in response to user actions. +/// +/// +/// +/// +internal sealed class ProjectFileClassifier { + private static readonly string? s_windows; + private static readonly string? s_programFiles86; + private static readonly string? s_programFiles64; + private static readonly string? s_dotNetSdk86; + private static readonly string? s_dotNetSdk64; + private static readonly string? s_vsInstallationDirectory; + + private string[] _nuGetPackageFolders = []; + private string _nuGetPackageFoldersString = ""; + private string? _projectExtensionsPath; + /// - /// Attempts to classify project files for various purposes such as safety and performance. + /// Gets and sets the MSBuildProjectExtensionsPath property value for this project. + /// Project files under this folder are considered non-modifiable. /// /// - /// - /// The term "project files" refers to the root project file (e.g. MyProject.csproj) and - /// any other .props and .targets files it imports. - /// - /// - /// Classifications provided are: - /// - /// - /// which indicates the file is not intended to be edited by the - /// user. It may be generated, or ship as part of Visual Studio, MSBuild, a NuGet package, etc. - /// - /// - /// which indicates the file is not expected to change over time, - /// other than when it is first created. This is a subset of non-user-editable files and - /// generally excludes generated files which can be regenerated in response to user actions. - /// - /// - /// + /// This value is only needed for . Files under this path + /// are changed over time by tooling, so do not satisfy . + /// + /// + /// Example value: "C:\repos\MySolution\MyProject\obj\" /// - internal sealed class ProjectFileClassifier + public string? ProjectExtensionsPath { - private static readonly string? s_windows; - private static readonly string? s_programFiles86; - private static readonly string? s_programFiles64; - private static readonly string? s_dotNetSdk86; - private static readonly string? s_dotNetSdk64; - private static readonly string? s_vsInstallationDirectory; - - private string[] _nuGetPackageFolders = []; - private string _nuGetPackageFoldersString = ""; - private string? _projectExtensionsPath; - - /// - /// Gets and sets the MSBuildProjectExtensionsPath property value for this project. - /// Project files under this folder are considered non-modifiable. - /// - /// - /// This value is only needed for . Files under this path - /// are changed over time by tooling, so do not satisfy . - /// - /// - /// Example value: "C:\repos\MySolution\MyProject\obj\" - /// - public string? ProjectExtensionsPath + get => _projectExtensionsPath; + set { - get => _projectExtensionsPath; - set - { - _projectExtensionsPath = value; - EnsureTrailingSlash(ref _projectExtensionsPath); - } + _projectExtensionsPath = value; + EnsureTrailingSlash(ref _projectExtensionsPath); } + } - /// - /// Gets and sets the paths found in the NuGetPackageFolders property value for this project. - /// Project files under any of these folders are considered non-modifiable. May be empty. - /// - /// - /// This value is used by both and . - /// Files in the NuGet package cache are not expected to change over time, once they are created. - /// - /// - /// Example value: "C:\Users\myusername\.nuget\;D:\LocalNuGetCache\" - /// - public string NuGetPackageFolders + /// + /// Gets and sets the paths found in the NuGetPackageFolders property value for this project. + /// Project files under any of these folders are considered non-modifiable. May be empty. + /// + /// + /// This value is used by both and . + /// Files in the NuGet package cache are not expected to change over time, once they are created. + /// + /// + /// Example value: "C:\Users\myusername\.nuget\;D:\LocalNuGetCache\" + /// + public string NuGetPackageFolders + { + get => _nuGetPackageFoldersString; + set { - get => _nuGetPackageFoldersString; - set + Requires.NotNull(value); + + if (!string.Equals(_nuGetPackageFoldersString, value, StringComparisons.Paths)) { - Requires.NotNull(value); + _nuGetPackageFoldersString = value; + _nuGetPackageFolders = value.Split(Delimiter.Semicolon, StringSplitOptions.RemoveEmptyEntries); - if (!string.Equals(_nuGetPackageFoldersString, value, StringComparisons.Paths)) + for (int i = 0; i < _nuGetPackageFolders.Length; i++) { - _nuGetPackageFoldersString = value; - _nuGetPackageFolders = value.Split(Delimiter.Semicolon, StringSplitOptions.RemoveEmptyEntries); - - for (int i = 0; i < _nuGetPackageFolders.Length; i++) - { - EnsureTrailingSlash(ref _nuGetPackageFolders[i]); - } + EnsureTrailingSlash(ref _nuGetPackageFolders[i]); } } } + } - static ProjectFileClassifier() - { + static ProjectFileClassifier() + { #if NET - if (!OperatingSystem.IsWindows()) - { - return; - } + if (!OperatingSystem.IsWindows()) + { + return; + } #endif - s_windows = Environment.GetFolderPath(Environment.SpecialFolder.Windows); - s_programFiles86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + s_windows = Environment.GetFolderPath(Environment.SpecialFolder.Windows); + s_programFiles86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); - // In a 32-bit process, SpecialFolder.ProgramFiles returns the 32-bit path. - // The 64-bit path is available via an environment variable however. - s_programFiles64 = Environment.GetEnvironmentVariable("ProgramW6432"); + // In a 32-bit process, SpecialFolder.ProgramFiles returns the 32-bit path. + // The 64-bit path is available via an environment variable however. + s_programFiles64 = Environment.GetEnvironmentVariable("ProgramW6432"); - s_dotNetSdk86 = Path.Combine(s_programFiles86, "dotnet", "sdk"); - s_dotNetSdk64 = Path.Combine(s_programFiles64, "dotnet", "sdk"); + s_dotNetSdk86 = Path.Combine(s_programFiles86, "dotnet", "sdk"); + s_dotNetSdk64 = Path.Combine(s_programFiles64, "dotnet", "sdk"); - s_vsInstallationDirectory = GetVSInstallationDirectory(); - - EnsureTrailingSlash(ref s_windows); - EnsureTrailingSlash(ref s_programFiles86); - EnsureTrailingSlash(ref s_programFiles64); - EnsureTrailingSlash(ref s_vsInstallationDirectory); - EnsureTrailingSlash(ref s_dotNetSdk86); - EnsureTrailingSlash(ref s_dotNetSdk64); - - return; + s_vsInstallationDirectory = GetVSInstallationDirectory(); - static string? GetVSInstallationDirectory() - { - string? dir = Environment.GetEnvironmentVariable("VSAPPIDDIR"); + EnsureTrailingSlash(ref s_windows); + EnsureTrailingSlash(ref s_programFiles86); + EnsureTrailingSlash(ref s_programFiles64); + EnsureTrailingSlash(ref s_vsInstallationDirectory); + EnsureTrailingSlash(ref s_dotNetSdk86); + EnsureTrailingSlash(ref s_dotNetSdk64); - // The path provided is not the installation root, but rather the location of devenv.exe. - // __VSSPROPID.VSSPROPID_InstallDirectory has the same value. - // Failing a better way to obtain the installation root, remove that suffix. - // Obviously this is brittle against changes to the relative path of devenv.exe, however that seems - // unlikely and should be easy to work around if ever needed. - const string DevEnvExeRelativePath = "Common7\\IDE\\"; + return; - if (dir?.EndsWith(DevEnvExeRelativePath, StringComparisons.Paths) == true) - { - dir = dir.Substring(0, dir.Length - DevEnvExeRelativePath.Length); - } + static string? GetVSInstallationDirectory() + { + string? dir = Environment.GetEnvironmentVariable("VSAPPIDDIR"); - return dir; - } - } + // The path provided is not the installation root, but rather the location of devenv.exe. + // __VSSPROPID.VSSPROPID_InstallDirectory has the same value. + // Failing a better way to obtain the installation root, remove that suffix. + // Obviously this is brittle against changes to the relative path of devenv.exe, however that seems + // unlikely and should be easy to work around if ever needed. + const string DevEnvExeRelativePath = "Common7\\IDE\\"; - private static void EnsureTrailingSlash([AllowNull] ref string path) - { - if (!Strings.IsNullOrEmpty(path)) + if (dir?.EndsWith(DevEnvExeRelativePath, StringComparisons.Paths) == true) { - path = PathHelper.EnsureTrailingSlash(path); + dir = dir.Substring(0, dir.Length - DevEnvExeRelativePath.Length); } - } - /// - /// Gets whether this file is not intended to be edited by the user. - /// - /// The path to the file to test. - /// if the file is non-user-editable, otherwise . - public bool IsNonUserEditable(string filePath) - { - return IsNonModifiable(filePath) - || (_projectExtensionsPath is not null && filePath.StartsWith(_projectExtensionsPath, StringComparisons.Paths)); + return dir; } + } - /// - /// Gets whether a file is expected to not be modified in place on disk once it has been created. - /// - /// The path to the file to test. - /// if the file is non-modifiable, otherwise . - public bool IsNonModifiable(string filePath) + private static void EnsureTrailingSlash([AllowNull] ref string path) + { + if (!Strings.IsNullOrEmpty(path)) { - return (s_programFiles64 is not null && filePath.StartsWith(s_programFiles64, StringComparisons.Paths)) - || (s_programFiles86 is not null && filePath.StartsWith(s_programFiles86, StringComparisons.Paths)) - || (s_windows is not null && filePath.StartsWith(s_windows, StringComparisons.Paths)) - || _nuGetPackageFolders.Any(static (nuGetFolder, filePath) => filePath.StartsWith(nuGetFolder, StringComparisons.Paths), filePath) - || (s_vsInstallationDirectory is not null && filePath.StartsWith(s_vsInstallationDirectory, StringComparisons.Paths)); + path = PathHelper.EnsureTrailingSlash(path); } + } - /// - /// Gets whether the file exists in one of a few well-known folders that are shipped by Microsoft. - /// - /// The path to the file to test. - /// if the file is known to have been shipped by Microsoft, otherwise . - public static bool IsShippedByMicrosoft(string filePath) - { - return (s_dotNetSdk86 is not null && filePath.StartsWith(s_dotNetSdk86, StringComparisons.Paths)) - || (s_dotNetSdk64 is not null && filePath.StartsWith(s_dotNetSdk64, StringComparisons.Paths)) - || (s_vsInstallationDirectory is not null && filePath.StartsWith(s_vsInstallationDirectory, StringComparisons.Paths)); - } + /// + /// Gets whether this file is not intended to be edited by the user. + /// + /// The path to the file to test. + /// if the file is non-user-editable, otherwise . + public bool IsNonUserEditable(string filePath) + { + return IsNonModifiable(filePath) + || (_projectExtensionsPath is not null && filePath.StartsWith(_projectExtensionsPath, StringComparisons.Paths)); + } + + /// + /// Gets whether a file is expected to not be modified in place on disk once it has been created. + /// + /// The path to the file to test. + /// if the file is non-modifiable, otherwise . + public bool IsNonModifiable(string filePath) + { + return (s_programFiles64 is not null && filePath.StartsWith(s_programFiles64, StringComparisons.Paths)) + || (s_programFiles86 is not null && filePath.StartsWith(s_programFiles86, StringComparisons.Paths)) + || (s_windows is not null && filePath.StartsWith(s_windows, StringComparisons.Paths)) + || _nuGetPackageFolders.Any(static (nuGetFolder, filePath) => filePath.StartsWith(nuGetFolder, StringComparisons.Paths), filePath) + || (s_vsInstallationDirectory is not null && filePath.StartsWith(s_vsInstallationDirectory, StringComparisons.Paths)); + } + + /// + /// Gets whether the file exists in one of a few well-known folders that are shipped by Microsoft. + /// + /// The path to the file to test. + /// if the file is known to have been shipped by Microsoft, otherwise . + public static bool IsShippedByMicrosoft(string filePath) + { + return (s_dotNetSdk86 is not null && filePath.StartsWith(s_dotNetSdk86, StringComparisons.Paths)) + || (s_dotNetSdk64 is not null && filePath.StartsWith(s_dotNetSdk64, StringComparisons.Paths)) + || (s_vsInstallationDirectory is not null && filePath.StartsWith(s_vsInstallationDirectory, StringComparisons.Paths)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeFlagsExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeFlagsExtensions.cs index 30cd6a9c7a..44256c592c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeFlagsExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeFlagsExtensions.cs @@ -1,53 +1,52 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides extension methods for . +/// +internal static class ProjectTreeFlagsExtensions { /// - /// Provides extension methods for . + /// Returns a value indicating whether the specified flags has the flag . /// - internal static class ProjectTreeFlagsExtensions + public static bool IsProjectRoot(this ProjectTreeFlags flags) { - /// - /// Returns a value indicating whether the specified flags has the flag . - /// - public static bool IsProjectRoot(this ProjectTreeFlags flags) - { - return flags.HasFlag(ProjectTreeFlags.Common.ProjectRoot); - } + return flags.HasFlag(ProjectTreeFlags.Common.ProjectRoot); + } - /// - /// Returns a value indicating whether the specified flags has the specified flag. - /// - public static bool HasFlag(this ProjectTreeFlags flags, ProjectTreeFlags.Common flag) - { - return flags.Contains(flag); - } + /// + /// Returns a value indicating whether the specified flags has the specified flag. + /// + public static bool HasFlag(this ProjectTreeFlags flags, ProjectTreeFlags.Common flag) + { + return flags.Contains(flag); + } - /// - /// Returns a value indicating whether the specified flags indicates that - /// the node is included as part of the project. - /// - public static bool IsIncludedInProject(this ProjectTreeFlags flags) - { - return !flags.HasFlag(ProjectTreeFlags.Common.IncludeInProjectCandidate); - } + /// + /// Returns a value indicating whether the specified flags indicates that + /// the node is included as part of the project. + /// + public static bool IsIncludedInProject(this ProjectTreeFlags flags) + { + return !flags.HasFlag(ProjectTreeFlags.Common.IncludeInProjectCandidate); + } - /// - /// Returns a value indicating whether the specified flags indicates that - /// the file or folder is missing on disk. - /// - public static bool IsMissingOnDisk(this ProjectTreeFlags flags) - { - return !flags.HasFlag(ProjectTreeFlags.Common.FileSystemEntity); - } + /// + /// Returns a value indicating whether the specified flags indicates that + /// the file or folder is missing on disk. + /// + public static bool IsMissingOnDisk(this ProjectTreeFlags flags) + { + return !flags.HasFlag(ProjectTreeFlags.Common.FileSystemEntity); + } - /// - /// Returns a value indicating whether the specified flags indicates that - /// the node is a folder. - /// - public static bool IsFolder(this ProjectTreeFlags flags) - { - return flags.HasFlag(ProjectTreeFlags.Common.Folder); - } + /// + /// Returns a value indicating whether the specified flags indicates that + /// the node is a folder. + /// + public static bool IsFolder(this ProjectTreeFlags flags) + { + return flags.HasFlag(ProjectTreeFlags.Common.Folder); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeProviderExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeProviderExtensions.cs index 9ab8c765d4..7491a59a93 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeProviderExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ProjectTreeProviderExtensions.cs @@ -1,48 +1,47 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides extensions for instances. +/// +internal static class ProjectTreeProviderExtensions { /// - /// Provides extensions for instances. + /// Returns a rooted directory that new files should added to the project under when the user + /// initiates an Add New Item operation on a particular node in the tree. /// - internal static class ProjectTreeProviderExtensions + /// + /// The that provides the directory. + /// + /// + /// The in the tree that is the receiver of the Add New Item operation. + /// + /// + /// A containing the path under which to save the new items, or + /// if does not support adding new items. + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + public static string? GetRootedAddNewItemDirectory(this IProjectTreeProvider provider, IProjectTree target) { - /// - /// Returns a rooted directory that new files should added to the project under when the user - /// initiates an Add New Item operation on a particular node in the tree. - /// - /// - /// The that provides the directory. - /// - /// - /// The in the tree that is the receiver of the Add New Item operation. - /// - /// - /// A containing the path under which to save the new items, or - /// if does not support adding new items. - /// - /// - /// is . - /// - /// -or- - /// - /// is . - /// - public static string? GetRootedAddNewItemDirectory(this IProjectTreeProvider provider, IProjectTree target) - { - Requires.NotNull(provider); - Requires.NotNull(target); + Requires.NotNull(provider); + Requires.NotNull(target); - string? relativePath = provider.GetAddNewItemDirectory(target); + string? relativePath = provider.GetAddNewItemDirectory(target); - if (relativePath is null) - return null; + if (relativePath is null) + return null; - string? projectFilePath = provider.GetPath(target.Root); + string? projectFilePath = provider.GetPath(target.Root); - string rootPath = Path.GetDirectoryName(projectFilePath); + string rootPath = Path.GetDirectoryName(projectFilePath); - return Path.Combine(rootPath, relativePath); - } + return Path.Combine(rootPath, relativePath); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AbstractProjectFileOrAssemblyInfoPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AbstractProjectFileOrAssemblyInfoPropertiesProvider.cs index 6c21135383..c6fac66748 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AbstractProjectFileOrAssemblyInfoPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AbstractProjectFileOrAssemblyInfoPropertiesProvider.cs @@ -2,48 +2,47 @@ using Microsoft.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// A provider for assembly info properties that are stored either in the project file OR the source code of the project. +/// +internal abstract class AbstractProjectFileOrAssemblyInfoPropertiesProvider : InterceptedPropertiesProviderBase { - /// - /// A provider for assembly info properties that are stored either in the project file OR the source code of the project. - /// - internal abstract class AbstractProjectFileOrAssemblyInfoPropertiesProvider : InterceptedPropertiesProviderBase - { - private readonly UnconfiguredProject _project; - private readonly Func _getActiveProjectId; - private readonly Workspace _workspace; - private readonly IProjectThreadingService _threadingService; + private readonly UnconfiguredProject _project; + private readonly Func _getActiveProjectId; + private readonly Workspace _workspace; + private readonly IProjectThreadingService _threadingService; - protected AbstractProjectFileOrAssemblyInfoPropertiesProvider( - IProjectPropertiesProvider delegatedProvider, - IProjectInstancePropertiesProvider instanceProvider, - IEnumerable> interceptingValueProviders, - UnconfiguredProject project, - Func getActiveProjectId, - Workspace workspace, - IProjectThreadingService threadingService) - : base(delegatedProvider, instanceProvider, project, interceptingValueProviders) - { - Requires.NotNull(getActiveProjectId); - Requires.NotNull(workspace); - Requires.NotNull(threadingService); + protected AbstractProjectFileOrAssemblyInfoPropertiesProvider( + IProjectPropertiesProvider delegatedProvider, + IProjectInstancePropertiesProvider instanceProvider, + IEnumerable> interceptingValueProviders, + UnconfiguredProject project, + Func getActiveProjectId, + Workspace workspace, + IProjectThreadingService threadingService) + : base(delegatedProvider, instanceProvider, project, interceptingValueProviders) + { + Requires.NotNull(getActiveProjectId); + Requires.NotNull(workspace); + Requires.NotNull(threadingService); - _project = project; - _getActiveProjectId = getActiveProjectId; - _workspace = workspace; - _threadingService = threadingService; - } + _project = project; + _getActiveProjectId = getActiveProjectId; + _workspace = workspace; + _threadingService = threadingService; + } - /// - /// Gets the properties for a property or item. - /// - public override IProjectProperties GetProperties(string file, string? itemType, string? item) - { - IProjectProperties delegatedProperties = base.GetProperties(file, itemType, item); - IProjectProperties assemblyInfoProperties = new AssemblyInfoProperties(delegatedProperties, _getActiveProjectId, _workspace, _threadingService); - return HasInterceptingValueProvider - ? new InterceptedProjectProperties(this, assemblyInfoProperties, _project) - : assemblyInfoProperties; - } + /// + /// Gets the properties for a property or item. + /// + public override IProjectProperties GetProperties(string file, string? itemType, string? item) + { + IProjectProperties delegatedProperties = base.GetProperties(file, itemType, item); + IProjectProperties assemblyInfoProperties = new AssemblyInfoProperties(delegatedProperties, _getActiveProjectId, _workspace, _threadingService); + return HasInterceptingValueProvider + ? new InterceptedProjectProperties(this, assemblyInfoProperties, _project) + : assemblyInfoProperties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AssemblyInfoProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AssemblyInfoProperties.cs index 2e3fa644cc..8509fb07f9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AssemblyInfoProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/AssemblyInfoProperties.cs @@ -3,125 +3,124 @@ using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Buffers.PooledObjects; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// This class represents assembly attribute properties that are stored in the project file OR the source code of the project. +/// +internal class AssemblyInfoProperties : DelegatedProjectPropertiesBase { - /// - /// This class represents assembly attribute properties that are stored in the project file OR the source code of the project. - /// - internal class AssemblyInfoProperties : DelegatedProjectPropertiesBase + private readonly ImmutableDictionary _attributeValueProviderMap; + + // See https://github.com/dotnet/sdk/blob/master/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets + internal static readonly ImmutableDictionary AssemblyPropertyInfoMap = new Dictionary + { + { "Description", ( AttributeName: "System.Reflection.AssemblyDescriptionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyDescriptionAttribute" ) }, + { "Company", ( AttributeName: "System.Reflection.AssemblyCompanyAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyCompanyAttribute" ) }, + { "Product", ( AttributeName: "System.Reflection.AssemblyProductAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyProductAttribute" ) }, + { "Copyright", ( AttributeName: "System.Reflection.AssemblyCopyrightAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyCopyrightAttribute" ) }, + { "AssemblyVersion", ( AttributeName: "System.Reflection.AssemblyVersionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyVersionAttribute" ) }, + { "Version", ( AttributeName: "System.Reflection.AssemblyInformationalVersionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyInformationalVersionAttribute" ) }, + { "FileVersion", ( AttributeName: "System.Reflection.AssemblyFileVersionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyFileVersionAttribute" ) }, + { "NeutralLanguage", ( AttributeName: "System.Resources.NeutralResourcesLanguageAttribute", GeneratePropertyInProjectFileName: "GenerateNeutralResourcesLanguageAttribute" ) }, + }.ToImmutableDictionary(); + + public AssemblyInfoProperties( + IProjectProperties delegatedProjectProperties, + Func getActiveProjectId, + Workspace workspace, + IProjectThreadingService threadingService) + : base(delegatedProjectProperties) { - private readonly ImmutableDictionary _attributeValueProviderMap; + _attributeValueProviderMap = CreateAttributeValueProviderMap(getActiveProjectId, workspace, threadingService); + } - // See https://github.com/dotnet/sdk/blob/master/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets - internal static readonly ImmutableDictionary AssemblyPropertyInfoMap = new Dictionary - { - { "Description", ( AttributeName: "System.Reflection.AssemblyDescriptionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyDescriptionAttribute" ) }, - { "Company", ( AttributeName: "System.Reflection.AssemblyCompanyAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyCompanyAttribute" ) }, - { "Product", ( AttributeName: "System.Reflection.AssemblyProductAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyProductAttribute" ) }, - { "Copyright", ( AttributeName: "System.Reflection.AssemblyCopyrightAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyCopyrightAttribute" ) }, - { "AssemblyVersion", ( AttributeName: "System.Reflection.AssemblyVersionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyVersionAttribute" ) }, - { "Version", ( AttributeName: "System.Reflection.AssemblyInformationalVersionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyInformationalVersionAttribute" ) }, - { "FileVersion", ( AttributeName: "System.Reflection.AssemblyFileVersionAttribute", GeneratePropertyInProjectFileName: "GenerateAssemblyFileVersionAttribute" ) }, - { "NeutralLanguage", ( AttributeName: "System.Resources.NeutralResourcesLanguageAttribute", GeneratePropertyInProjectFileName: "GenerateNeutralResourcesLanguageAttribute" ) }, - }.ToImmutableDictionary(); - - public AssemblyInfoProperties( - IProjectProperties delegatedProjectProperties, - Func getActiveProjectId, - Workspace workspace, - IProjectThreadingService threadingService) - : base(delegatedProjectProperties) + private static ImmutableDictionary CreateAttributeValueProviderMap( + Func getActiveProjectId, + Workspace workspace, + IProjectThreadingService threadingService) + { + var builder = PooledDictionary.GetInstance(); + foreach ((string key, (string attributeName, _)) in AssemblyPropertyInfoMap) { - _attributeValueProviderMap = CreateAttributeValueProviderMap(getActiveProjectId, workspace, threadingService); + var provider = new SourceAssemblyAttributePropertyValueProvider(attributeName, getActiveProjectId, workspace, threadingService); + builder.Add(key, provider); } - private static ImmutableDictionary CreateAttributeValueProviderMap( - Func getActiveProjectId, - Workspace workspace, - IProjectThreadingService threadingService) + return builder.ToImmutableDictionaryAndFree(); + } + + /// + /// Get the unevaluated property value. + /// + public override async Task GetUnevaluatedPropertyValueAsync(string propertyName) + { + if (_attributeValueProviderMap.ContainsKey(propertyName) && + !await IsAssemblyInfoPropertyGeneratedByBuildAsync(propertyName)) { - var builder = PooledDictionary.GetInstance(); - foreach ((string key, (string attributeName, _)) in AssemblyPropertyInfoMap) - { - var provider = new SourceAssemblyAttributePropertyValueProvider(attributeName, getActiveProjectId, workspace, threadingService); - builder.Add(key, provider); - } - - return builder.ToImmutableDictionaryAndFree(); + return await GetPropertyValueFromSourceAttributeAsync(propertyName); } - /// - /// Get the unevaluated property value. - /// - public override async Task GetUnevaluatedPropertyValueAsync(string propertyName) - { - if (_attributeValueProviderMap.ContainsKey(propertyName) && - !await IsAssemblyInfoPropertyGeneratedByBuildAsync(propertyName)) - { - return await GetPropertyValueFromSourceAttributeAsync(propertyName); - } + return await base.GetUnevaluatedPropertyValueAsync(propertyName); + } - return await base.GetUnevaluatedPropertyValueAsync(propertyName); + /// + /// Get the value of a property. + /// + public override async Task GetEvaluatedPropertyValueAsync(string propertyName) + { + if (_attributeValueProviderMap.ContainsKey(propertyName) && + !await IsAssemblyInfoPropertyGeneratedByBuildAsync(propertyName)) + { + return (await GetPropertyValueFromSourceAttributeAsync(propertyName)) ?? ""; } - /// - /// Get the value of a property. - /// - public override async Task GetEvaluatedPropertyValueAsync(string propertyName) - { - if (_attributeValueProviderMap.ContainsKey(propertyName) && - !await IsAssemblyInfoPropertyGeneratedByBuildAsync(propertyName)) - { - return (await GetPropertyValueFromSourceAttributeAsync(propertyName)) ?? ""; - } + return await base.GetEvaluatedPropertyValueAsync(propertyName); + } - return await base.GetEvaluatedPropertyValueAsync(propertyName); + /// + /// Get the value of a property from source assembly attribute. + /// + private async Task GetPropertyValueFromSourceAttributeAsync(string propertyName) + { + if (_attributeValueProviderMap.TryGetValue(propertyName, out SourceAssemblyAttributePropertyValueProvider? provider)) + { + return await provider.GetPropertyValueAsync(); } - /// - /// Get the value of a property from source assembly attribute. - /// - private async Task GetPropertyValueFromSourceAttributeAsync(string propertyName) - { - if (_attributeValueProviderMap.TryGetValue(propertyName, out SourceAssemblyAttributePropertyValueProvider? provider)) - { - return await provider.GetPropertyValueAsync(); - } + return null; + } - return null; + /// + /// Set the value of a property. + /// + public override async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + { + if (_attributeValueProviderMap.TryGetValue(propertyName, out SourceAssemblyAttributePropertyValueProvider? provider) && + !await IsAssemblyInfoPropertyGeneratedByBuildAsync(propertyName)) + { + await provider.SetPropertyValueAsync(unevaluatedPropertyValue); } - - /// - /// Set the value of a property. - /// - public override async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + else { - if (_attributeValueProviderMap.TryGetValue(propertyName, out SourceAssemblyAttributePropertyValueProvider? provider) && - !await IsAssemblyInfoPropertyGeneratedByBuildAsync(propertyName)) - { - await provider.SetPropertyValueAsync(unevaluatedPropertyValue); - } - else - { - await base.SetPropertyValueAsync(propertyName, unevaluatedPropertyValue, dimensionalConditions); - } + await base.SetPropertyValueAsync(propertyName, unevaluatedPropertyValue, dimensionalConditions); } + } + + private async Task IsAssemblyInfoPropertyGeneratedByBuildAsync(string propertyName) + { + (_, string generatePropertyInProjectFileName) = AssemblyPropertyInfoMap[propertyName]; - private async Task IsAssemblyInfoPropertyGeneratedByBuildAsync(string propertyName) + // Generate property in project file only if: + // 1. "GenerateAssemblyInfo" is true AND + // 2. "GenerateXXX" for this specific property is true. + string? propertyValue = await base.GetEvaluatedPropertyValueAsync("GenerateAssemblyInfo"); + if (!bool.TryParse(propertyValue, out bool value) || !value) { - (_, string generatePropertyInProjectFileName) = AssemblyPropertyInfoMap[propertyName]; - - // Generate property in project file only if: - // 1. "GenerateAssemblyInfo" is true AND - // 2. "GenerateXXX" for this specific property is true. - string? propertyValue = await base.GetEvaluatedPropertyValueAsync("GenerateAssemblyInfo"); - if (!bool.TryParse(propertyValue, out bool value) || !value) - { - return false; - } - - propertyValue = await base.GetEvaluatedPropertyValueAsync(generatePropertyInProjectFileName); - return bool.TryParse(propertyValue, out value) && value; + return false; } + + propertyValue = await base.GetEvaluatedPropertyValueAsync(generatePropertyInProjectFileName); + return bool.TryParse(propertyValue, out value) && value; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/SourceAssemblyAttributePropertyValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/SourceAssemblyAttributePropertyValueProvider.cs index 2a3a3d3836..d6585dc8c2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/SourceAssemblyAttributePropertyValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/AssemblyAttributeProperties/SourceAssemblyAttributePropertyValueProvider.cs @@ -3,126 +3,125 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editing; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// This class represents properties corresponding to assembly attributes that are stored in the source code of the project. +/// +internal class SourceAssemblyAttributePropertyValueProvider { + private readonly string _assemblyAttributeFullName; + private readonly Func _getActiveProjectId; + private readonly Workspace _workspace; + private readonly IProjectThreadingService _threadingService; + + public SourceAssemblyAttributePropertyValueProvider( + string assemblyAttributeFullName, + Func getActiveProjectId, + Workspace workspace, + IProjectThreadingService threadingService) + { + _assemblyAttributeFullName = assemblyAttributeFullName; + _getActiveProjectId = getActiveProjectId; + _workspace = workspace; + _threadingService = threadingService; + } + + private Project? GetActiveProject() + { + ProjectId? activeProjectId = _getActiveProjectId(); + if (activeProjectId is null) + { + return null; + } + + return _workspace.CurrentSolution.Projects.SingleOrDefault((p, id) => p.Id == id, activeProjectId); + } + /// - /// This class represents properties corresponding to assembly attributes that are stored in the source code of the project. + /// Gets the value of the property from the source assembly attribute. /// - internal class SourceAssemblyAttributePropertyValueProvider + public async Task GetPropertyValueAsync() { - private readonly string _assemblyAttributeFullName; - private readonly Func _getActiveProjectId; - private readonly Workspace _workspace; - private readonly IProjectThreadingService _threadingService; - - public SourceAssemblyAttributePropertyValueProvider( - string assemblyAttributeFullName, - Func getActiveProjectId, - Workspace workspace, - IProjectThreadingService threadingService) + Project? project = GetActiveProject(); + if (project is null) { - _assemblyAttributeFullName = assemblyAttributeFullName; - _getActiveProjectId = getActiveProjectId; - _workspace = workspace; - _threadingService = threadingService; + return null; } - private Project? GetActiveProject() - { - ProjectId? activeProjectId = _getActiveProjectId(); - if (activeProjectId is null) - { - return null; - } + AttributeData? attribute = await GetAttributeAsync(_assemblyAttributeFullName, project); - return _workspace.CurrentSolution.Projects.SingleOrDefault((p, id) => p.Id == id, activeProjectId); - } + return attribute?.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } - /// - /// Gets the value of the property from the source assembly attribute. - /// - public async Task GetPropertyValueAsync() + /// + /// Sets the value of the property in the source assembly attribute. + /// + /// + public async Task SetPropertyValueAsync(string value) + { + Project? project = GetActiveProject(); + if (project is null) { - Project? project = GetActiveProject(); - if (project is null) - { - return null; - } + return; + } - AttributeData? attribute = await GetAttributeAsync(_assemblyAttributeFullName, project); + AttributeData? attribute = await GetAttributeAsync(_assemblyAttributeFullName, project); + if (attribute is null) + { + return; + } - return attribute?.ConstructorArguments.FirstOrDefault().Value?.ToString(); + if (attribute.ApplicationSyntaxReference is null) + { + return; } + SyntaxNode attributeNode = await attribute.ApplicationSyntaxReference.GetSyntaxAsync(); + var syntaxGenerator = SyntaxGenerator.GetGenerator(project); + IReadOnlyList arguments = syntaxGenerator.GetAttributeArguments(attributeNode); - /// - /// Sets the value of the property in the source assembly attribute. - /// - /// - public async Task SetPropertyValueAsync(string value) + // The attributes of interest to us have one argument. If there are more then we have broken code - don't change that. + if (arguments.Count == 1) { - Project? project = GetActiveProject(); - if (project is null) + SyntaxNode argumentNode = arguments[0]; + SyntaxNode newNode; + if (attribute.AttributeConstructor?.Parameters.FirstOrDefault()?.Type.SpecialType == SpecialType.System_Boolean) { - return; + newNode = syntaxGenerator.AttributeArgument(bool.Parse(value) ? syntaxGenerator.TrueLiteralExpression() : syntaxGenerator.FalseLiteralExpression()); } - - AttributeData? attribute = await GetAttributeAsync(_assemblyAttributeFullName, project); - if (attribute is null) + else { - return; + newNode = syntaxGenerator.AttributeArgument(syntaxGenerator.LiteralExpression(value)); } - if (attribute.ApplicationSyntaxReference is null) - { - return; - } - SyntaxNode attributeNode = await attribute.ApplicationSyntaxReference.GetSyntaxAsync(); - var syntaxGenerator = SyntaxGenerator.GetGenerator(project); - IReadOnlyList arguments = syntaxGenerator.GetAttributeArguments(attributeNode); + newNode = newNode.WithTriviaFrom(argumentNode); + DocumentEditor editor = await DocumentEditor.CreateAsync(project.GetDocument(attributeNode.SyntaxTree)); + editor.ReplaceNode(argumentNode, newNode); - // The attributes of interest to us have one argument. If there are more then we have broken code - don't change that. - if (arguments.Count == 1) - { - SyntaxNode argumentNode = arguments[0]; - SyntaxNode newNode; - if (attribute.AttributeConstructor?.Parameters.FirstOrDefault()?.Type.SpecialType == SpecialType.System_Boolean) - { - newNode = syntaxGenerator.AttributeArgument(bool.Parse(value) ? syntaxGenerator.TrueLiteralExpression() : syntaxGenerator.FalseLiteralExpression()); - } - else - { - newNode = syntaxGenerator.AttributeArgument(syntaxGenerator.LiteralExpression(value)); - } - - newNode = newNode.WithTriviaFrom(argumentNode); - DocumentEditor editor = await DocumentEditor.CreateAsync(project.GetDocument(attributeNode.SyntaxTree)); - editor.ReplaceNode(argumentNode, newNode); - - // Apply changes needs to happen on the UI Thread. - await _threadingService.SwitchToUIThread(); - _workspace.TryApplyChanges(editor.GetChangedDocument().Project.Solution); - } + // Apply changes needs to happen on the UI Thread. + await _threadingService.SwitchToUIThread(); + _workspace.TryApplyChanges(editor.GetChangedDocument().Project.Solution); } + } - /// - /// Get the attribute corresponding to the given property from the given project. - /// - private static async Task GetAttributeAsync(string assemblyAttributeFullName, Project project) + /// + /// Get the attribute corresponding to the given property from the given project. + /// + private static async Task GetAttributeAsync(string assemblyAttributeFullName, Project project) + { + if (project is null) { - if (project is null) - { - return null; - } - Compilation? compilation = await project.GetCompilationAsync(); - ImmutableArray? assemblyAttributes = compilation?.Assembly.GetAttributes(); - - INamedTypeSymbol? attributeTypeSymbol = compilation?.GetTypeByMetadataName(assemblyAttributeFullName); - if (attributeTypeSymbol is null) - { - return null; - } + return null; + } + Compilation? compilation = await project.GetCompilationAsync(); + ImmutableArray? assemblyAttributes = compilation?.Assembly.GetAttributes(); - return assemblyAttributes?.FirstOrDefault((data, symbol) => SymbolEqualityComparer.Default.Equals(data.AttributeClass, symbol), attributeTypeSymbol); + INamedTypeSymbol? attributeTypeSymbol = compilation?.GetTypeByMetadataName(assemblyAttributeFullName); + if (attributeTypeSymbol is null) + { + return null; } + + return assemblyAttributes?.FirstOrDefault((data, symbol) => SymbolEqualityComparer.Default.Equals(data.AttributeClass, symbol), attributeTypeSymbol); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/BasePropertyExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/BasePropertyExtensions.cs index 411416dafc..a5fa14a2b6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/BasePropertyExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/BasePropertyExtensions.cs @@ -2,50 +2,49 @@ using Microsoft.Build.Framework.XamlTypes; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Provides extension methods for instances. +/// +internal static class BasePropertyExtensions { /// - /// Provides extension methods for instances. + /// Returns the value of the metadata item identified by . /// - internal static class BasePropertyExtensions + /// The property to examine. + /// The name of the metadata item to return. + /// + /// The value of the corresponding metadata item, or if it is not + /// found. + /// + /// + /// is . + /// + /// + /// is or empty. + /// + public static string? GetMetadataValueOrNull(this BaseProperty property, string metadataName) { - /// - /// Returns the value of the metadata item identified by . - /// - /// The property to examine. - /// The name of the metadata item to return. - /// - /// The value of the corresponding metadata item, or if it is not - /// found. - /// - /// - /// is . - /// - /// - /// is or empty. - /// - public static string? GetMetadataValueOrNull(this BaseProperty property, string metadataName) - { - Requires.NotNull(property); - Requires.NotNullOrEmpty(metadataName); + Requires.NotNull(property); + Requires.NotNullOrEmpty(metadataName); - return property.Metadata.FirstOrDefault(nvp => nvp.Name == metadataName)?.Value; - } + return property.Metadata.FirstOrDefault(nvp => nvp.Name == metadataName)?.Value; + } - /// - /// Whether or not the the is configuration-dependent or not. - /// - /// The property to examine. - /// - /// if the property's (or the 's - /// ) indicates that it is configuration-dependent; - /// otherwise. - /// - public static bool IsConfigurationDependent(this BaseProperty property) - { - return property.DataSource?.HasConfigurationCondition - ?? property.ContainingRule.DataSource?.HasConfigurationCondition - ?? false; - } + /// + /// Whether or not the the is configuration-dependent or not. + /// + /// The property to examine. + /// + /// if the property's (or the 's + /// ) indicates that it is configuration-dependent; + /// otherwise. + /// + public static bool IsConfigurationDependent(this BaseProperty property) + { + return property.DataSource?.HasConfigurationCondition + ?? property.ContainingRule.DataSource?.HasConfigurationCondition + ?? false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesBase.cs index fd5217a42a..571cf72820 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesBase.cs @@ -1,50 +1,49 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// An implementation of IProjectProperties that delegates its operations +/// to another IProjectProperties object +/// +internal class DelegatedProjectPropertiesBase : IProjectProperties { - /// - /// An implementation of IProjectProperties that delegates its operations - /// to another IProjectProperties object - /// - internal class DelegatedProjectPropertiesBase : IProjectProperties - { - protected readonly IProjectProperties DelegatedProperties; + protected readonly IProjectProperties DelegatedProperties; - protected DelegatedProjectPropertiesBase(IProjectProperties properties) - { - Requires.NotNull(properties); + protected DelegatedProjectPropertiesBase(IProjectProperties properties) + { + Requires.NotNull(properties); - DelegatedProperties = properties; - } + DelegatedProperties = properties; + } - public virtual IProjectPropertiesContext Context => DelegatedProperties.Context; + public virtual IProjectPropertiesContext Context => DelegatedProperties.Context; - public virtual string FileFullPath => DelegatedProperties.FileFullPath; + public virtual string FileFullPath => DelegatedProperties.FileFullPath; - public virtual PropertyKind PropertyKind => DelegatedProperties.PropertyKind; + public virtual PropertyKind PropertyKind => DelegatedProperties.PropertyKind; - public virtual Task DeleteDirectPropertiesAsync() - => DelegatedProperties.DeleteDirectPropertiesAsync(); + public virtual Task DeleteDirectPropertiesAsync() + => DelegatedProperties.DeleteDirectPropertiesAsync(); - public virtual Task DeletePropertyAsync(string propertyName, IReadOnlyDictionary? dimensionalConditions = null) - => DelegatedProperties.DeletePropertyAsync(propertyName, dimensionalConditions); + public virtual Task DeletePropertyAsync(string propertyName, IReadOnlyDictionary? dimensionalConditions = null) + => DelegatedProperties.DeletePropertyAsync(propertyName, dimensionalConditions); - public virtual Task> GetDirectPropertyNamesAsync() - => DelegatedProperties.GetDirectPropertyNamesAsync(); + public virtual Task> GetDirectPropertyNamesAsync() + => DelegatedProperties.GetDirectPropertyNamesAsync(); - public virtual Task GetEvaluatedPropertyValueAsync(string propertyName) - => DelegatedProperties.GetEvaluatedPropertyValueAsync(propertyName); + public virtual Task GetEvaluatedPropertyValueAsync(string propertyName) + => DelegatedProperties.GetEvaluatedPropertyValueAsync(propertyName); - public virtual Task> GetPropertyNamesAsync() - => DelegatedProperties.GetPropertyNamesAsync(); + public virtual Task> GetPropertyNamesAsync() + => DelegatedProperties.GetPropertyNamesAsync(); - public virtual Task GetUnevaluatedPropertyValueAsync(string propertyName) - => DelegatedProperties.GetUnevaluatedPropertyValueAsync(propertyName); + public virtual Task GetUnevaluatedPropertyValueAsync(string propertyName) + => DelegatedProperties.GetUnevaluatedPropertyValueAsync(propertyName); - public virtual Task IsValueInheritedAsync(string propertyName) - => DelegatedProperties.IsValueInheritedAsync(propertyName); + public virtual Task IsValueInheritedAsync(string propertyName) + => DelegatedProperties.IsValueInheritedAsync(propertyName); - public virtual Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) - => DelegatedProperties.SetPropertyValueAsync(propertyName, unevaluatedPropertyValue, dimensionalConditions); - } + public virtual Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + => DelegatedProperties.SetPropertyValueAsync(propertyName, unevaluatedPropertyValue, dimensionalConditions); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesProviderBase.cs index be786ca3a9..4244b5f72a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/DelegatedProjectPropertiesProviderBase.cs @@ -4,93 +4,92 @@ using Microsoft.Build.Framework; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Base class for Project Property Providers that delegate most of their +/// responsibilities to a different project property provider, only overriding +/// the things they want to change. +/// +internal class DelegatedProjectPropertiesProviderBase : IProjectPropertiesProvider, IProjectInstancePropertiesProvider { /// - /// Base class for Project Property Providers that delegate most of their - /// responsibilities to a different project property provider, only overriding - /// the things they want to change. + /// Gets the unconfigured project /// - internal class DelegatedProjectPropertiesProviderBase : IProjectPropertiesProvider, IProjectInstancePropertiesProvider + protected readonly UnconfiguredProject Project; + + /// + /// The Project Properties Provider that is delegated to for most operations + /// + protected readonly IProjectPropertiesProvider DelegatedProvider; + + /// + /// The Project Instance Properties provider that is being delegated to for most operations + /// + protected readonly IProjectInstancePropertiesProvider DelegatedInstanceProvider; + + /// + /// Construct using the provider that should be delegated to for most operations + /// + public DelegatedProjectPropertiesProviderBase(IProjectPropertiesProvider provider, IProjectInstancePropertiesProvider instanceProvider, UnconfiguredProject project) { - /// - /// Gets the unconfigured project - /// - protected readonly UnconfiguredProject Project; - - /// - /// The Project Properties Provider that is delegated to for most operations - /// - protected readonly IProjectPropertiesProvider DelegatedProvider; - - /// - /// The Project Instance Properties provider that is being delegated to for most operations - /// - protected readonly IProjectInstancePropertiesProvider DelegatedInstanceProvider; - - /// - /// Construct using the provider that should be delegated to for most operations - /// - public DelegatedProjectPropertiesProviderBase(IProjectPropertiesProvider provider, IProjectInstancePropertiesProvider instanceProvider, UnconfiguredProject project) - { - Requires.NotNull(provider); - Requires.NotNull(instanceProvider); - Requires.NotNull(project); + Requires.NotNull(provider); + Requires.NotNull(instanceProvider); + Requires.NotNull(project); - DelegatedProvider = provider; - DelegatedInstanceProvider = instanceProvider; - Project = project; - } + DelegatedProvider = provider; + DelegatedInstanceProvider = instanceProvider; + Project = project; + } - public virtual string DefaultProjectPath + public virtual string DefaultProjectPath + { + get { - get - { - Assumes.NotNull(Project.FullPath); - return Project.FullPath; - } + Assumes.NotNull(Project.FullPath); + return Project.FullPath; } + } - public event AsyncEventHandler? ProjectPropertyChanged - { - add { DelegatedProvider.ProjectPropertyChanged += value; } - remove { DelegatedProvider.ProjectPropertyChanged -= value; } - } + public event AsyncEventHandler? ProjectPropertyChanged + { + add { DelegatedProvider.ProjectPropertyChanged += value; } + remove { DelegatedProvider.ProjectPropertyChanged -= value; } + } - public event AsyncEventHandler? ProjectPropertyChangedOnWriter - { - add { DelegatedProvider.ProjectPropertyChangedOnWriter += value; } - remove { DelegatedProvider.ProjectPropertyChangedOnWriter -= value; } - } + public event AsyncEventHandler? ProjectPropertyChangedOnWriter + { + add { DelegatedProvider.ProjectPropertyChangedOnWriter += value; } + remove { DelegatedProvider.ProjectPropertyChangedOnWriter -= value; } + } - public event AsyncEventHandler? ProjectPropertyChanging - { - add { DelegatedProvider.ProjectPropertyChanging += value; } - remove { DelegatedProvider.ProjectPropertyChanging -= value; } - } + public event AsyncEventHandler? ProjectPropertyChanging + { + add { DelegatedProvider.ProjectPropertyChanging += value; } + remove { DelegatedProvider.ProjectPropertyChanging -= value; } + } - public virtual IProjectProperties GetCommonProperties() - => DelegatedProvider.GetCommonProperties(); + public virtual IProjectProperties GetCommonProperties() + => DelegatedProvider.GetCommonProperties(); - public virtual IProjectProperties GetItemProperties(string? itemType, string? item) - => DelegatedProvider.GetItemProperties(itemType, item); + public virtual IProjectProperties GetItemProperties(string? itemType, string? item) + => DelegatedProvider.GetItemProperties(itemType, item); - public virtual IProjectProperties GetItemTypeProperties(string? itemType) - => DelegatedProvider.GetItemTypeProperties(itemType); + public virtual IProjectProperties GetItemTypeProperties(string? itemType) + => DelegatedProvider.GetItemTypeProperties(itemType); - public virtual IProjectProperties GetProperties(string file, string? itemType, string? item) - => DelegatedProvider.GetProperties(file, itemType, item); + public virtual IProjectProperties GetProperties(string file, string? itemType, string? item) + => DelegatedProvider.GetProperties(file, itemType, item); - public virtual IProjectProperties GetCommonProperties(ProjectInstance projectInstance) - => DelegatedInstanceProvider.GetCommonProperties(projectInstance); + public virtual IProjectProperties GetCommonProperties(ProjectInstance projectInstance) + => DelegatedInstanceProvider.GetCommonProperties(projectInstance); - public virtual IProjectProperties GetItemTypeProperties(ProjectInstance projectInstance, string? itemType) - => DelegatedInstanceProvider.GetItemTypeProperties(projectInstance, itemType); + public virtual IProjectProperties GetItemTypeProperties(ProjectInstance projectInstance, string? itemType) + => DelegatedInstanceProvider.GetItemTypeProperties(projectInstance, itemType); - public virtual IProjectProperties GetItemProperties(ProjectInstance projectInstance, string? itemType, string? itemName) - => DelegatedInstanceProvider.GetItemProperties(projectInstance, itemType, itemName); + public virtual IProjectProperties GetItemProperties(ProjectInstance projectInstance, string? itemType, string? itemName) + => DelegatedInstanceProvider.GetItemProperties(projectInstance, itemType, itemName); - public virtual IProjectProperties GetItemProperties(ITaskItem taskItem) - => DelegatedInstanceProvider.GetItemProperties(taskItem); - } + public virtual IProjectProperties GetItemProperties(ITaskItem taskItem) + => DelegatedInstanceProvider.GetItemProperties(taskItem); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/IProjectDesignerService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/IProjectDesignerService.cs index 9a4cea0bb7..e8d9d1ef1a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/IProjectDesignerService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/IProjectDesignerService.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Provides members for opening the Project Designer and querying whether it is supported. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IProjectDesignerService { /// - /// Provides members for opening the Project Designer and querying whether it is supported. + /// Gets a value indicating whether the current project supports the Project Designer. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IProjectDesignerService - { - /// - /// Gets a value indicating whether the current project supports the Project Designer. - /// - /// - /// if the current project supports the Project Designer; otherwise, . - /// - bool SupportsProjectDesigner { get; } + /// + /// if the current project supports the Project Designer; otherwise, . + /// + bool SupportsProjectDesigner { get; } - /// - /// Shows the current project's Project Designer window. - /// - /// - /// is . - /// - Task ShowProjectDesignerAsync(); - } + /// + /// Shows the current project's Project Designer window. + /// + /// + /// is . + /// + Task ShowProjectDesignerAsync(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ITemporaryPropertyStorage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ITemporaryPropertyStorage.cs index 5c3c1d7f2a..f1117a99c8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ITemporaryPropertyStorage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ITemporaryPropertyStorage.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Provides for the temporary storage of property values. +/// Consider the project property pages. In some cases, changing the value of property A +/// will cause us to clear the value of property B. If A is changed back to its original +/// value, we would like to be able to restore the previous value of B as well. This +/// type provides the means to store that previous value until (and if) we need it again. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface ITemporaryPropertyStorage { - /// - /// Provides for the temporary storage of property values. - /// Consider the project property pages. In some cases, changing the value of property A - /// will cause us to clear the value of property B. If A is changed back to its original - /// value, we would like to be able to restore the previous value of B as well. This - /// type provides the means to store that previous value until (and if) we need it again. - /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface ITemporaryPropertyStorage - { - void AddOrUpdatePropertyValue(string propertyName, string propertyValue); - string? GetPropertyValue(string propertyName); - } + void AddOrUpdatePropertyValue(string propertyName, string propertyValue); + string? GetPropertyValue(string propertyName); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationManifestValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationManifestValueProvider.cs index b48fa5b16c..b13cfc1560 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationManifestValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationManifestValueProvider.cs @@ -1,93 +1,92 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// See also: , . +/// +[ExportInterceptingPropertyValueProvider("ApplicationManifest", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ApplicationManifestValueProvider : InterceptingPropertyValueProviderBase { + private readonly UnconfiguredProject _unconfiguredProject; + + private const string NoManifestMSBuildProperty = "NoWin32Manifest"; + private const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; + private const string NoManifestValue = "NoManifest"; + private const string DefaultManifestValue = "DefaultManifest"; + + [ImportingConstructor] + public ApplicationManifestValueProvider(UnconfiguredProject project) + { + _unconfiguredProject = project; + } + + /// + /// Gets the application manifest property + /// /// - /// See also: , . + /// The Application Manifest's value is one of three possibilities: + /// - It's either a path to file that is the manifest + /// - It's the value "NoManifest" which means the application doesn't have a manifest. + /// - It's the value "DefaultManifest" which means that the application will have a default manifest. + /// + /// These three values map to two MSBuild properties - ApplicationManifest (specified if it's a path) or NoWin32Manifest + /// which is true for the second case and false or non-existent for the third. /// - [ExportInterceptingPropertyValueProvider("ApplicationManifest", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ApplicationManifestValueProvider : InterceptingPropertyValueProviderBase + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) { - private readonly UnconfiguredProject _unconfiguredProject; - - private const string NoManifestMSBuildProperty = "NoWin32Manifest"; - private const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; - private const string NoManifestValue = "NoManifest"; - private const string DefaultManifestValue = "DefaultManifest"; - - [ImportingConstructor] - public ApplicationManifestValueProvider(UnconfiguredProject project) + if (!string.IsNullOrEmpty(evaluatedPropertyValue)) { - _unconfiguredProject = project; + return evaluatedPropertyValue; } - /// - /// Gets the application manifest property - /// - /// - /// The Application Manifest's value is one of three possibilities: - /// - It's either a path to file that is the manifest - /// - It's the value "NoManifest" which means the application doesn't have a manifest. - /// - It's the value "DefaultManifest" which means that the application will have a default manifest. - /// - /// These three values map to two MSBuild properties - ApplicationManifest (specified if it's a path) or NoWin32Manifest - /// which is true for the second case and false or non-existent for the third. - /// - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + string noManifestPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(NoManifestMSBuildProperty); + if (noManifestPropertyValue?.Equals("true", StringComparison.InvariantCultureIgnoreCase) == true) { - if (!string.IsNullOrEmpty(evaluatedPropertyValue)) - { - return evaluatedPropertyValue; - } + return NoManifestValue; + } - string noManifestPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(NoManifestMSBuildProperty); - if (noManifestPropertyValue?.Equals("true", StringComparison.InvariantCultureIgnoreCase) == true) - { - return NoManifestValue; - } + // It doesn't matter if it is set to false or the value is not present. We default to "DefaultManifest" scenario. + return DefaultManifestValue; + } - // It doesn't matter if it is set to false or the value is not present. We default to "DefaultManifest" scenario. - return DefaultManifestValue; - } + /// + /// Sets the application manifest property + /// + public override async Task OnSetPropertyValueAsync( + string propertyName, + string? unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + string? returnValue = null; - /// - /// Sets the application manifest property - /// - public override async Task OnSetPropertyValueAsync( - string propertyName, - string? unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) + // We treat NULL/empty value as reset to default and remove the two properties from the project. + if (Strings.IsNullOrEmpty(unevaluatedPropertyValue) || string.Equals(unevaluatedPropertyValue, DefaultManifestValue, StringComparison.InvariantCultureIgnoreCase)) { - string? returnValue = null; - - // We treat NULL/empty value as reset to default and remove the two properties from the project. - if (Strings.IsNullOrEmpty(unevaluatedPropertyValue) || string.Equals(unevaluatedPropertyValue, DefaultManifestValue, StringComparison.InvariantCultureIgnoreCase)) - { - await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); - await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); - } - else if (string.Equals(unevaluatedPropertyValue, NoManifestValue, StringComparison.InvariantCultureIgnoreCase)) + await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); + await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); + } + else if (string.Equals(unevaluatedPropertyValue, NoManifestValue, StringComparison.InvariantCultureIgnoreCase)) + { + await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); + await defaultProperties.SetPropertyValueAsync(NoManifestMSBuildProperty, "true"); + } + else + { + await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); + // If we can make the path relative to the project folder do so. Otherwise just use the given path. + if (Path.IsPathRooted(unevaluatedPropertyValue) && + PathHelper.TryMakeRelativeToProjectDirectory(_unconfiguredProject, unevaluatedPropertyValue, out string? relativePath)) { - await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); - await defaultProperties.SetPropertyValueAsync(NoManifestMSBuildProperty, "true"); + returnValue = relativePath; } else { - await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); - // If we can make the path relative to the project folder do so. Otherwise just use the given path. - if (Path.IsPathRooted(unevaluatedPropertyValue) && - PathHelper.TryMakeRelativeToProjectDirectory(_unconfiguredProject, unevaluatedPropertyValue, out string? relativePath)) - { - returnValue = relativePath; - } - else - { - returnValue = unevaluatedPropertyValue; - } + returnValue = unevaluatedPropertyValue; } - - return returnValue; } + + return returnValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationIconValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationIconValueProvider.cs index 76d539d6f1..e06f36a330 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationIconValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationIconValueProvider.cs @@ -2,92 +2,91 @@ using Microsoft.VisualStudio.IO; -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +// Sets the ApplicationIcon property to the path to the .ico file. This path is relative +// to the project file directory. If the selected file is not within the project directory +// tree, the file will be copied into the project file directory. Previously copied files +// are not deleted when a new file is selected. ApplicationIcon allows for both full paths +// and relative paths anywhere on disk, but we are not utilizing that in this implementation. +// The icon file does not need to be included in the project (such as a None/Content item). +[ExportInterceptingPropertyValueProvider(ConfigurationGeneral.ApplicationIconProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ApplicationIconValueProvider : InterceptingPropertyValueProviderBase { - // Sets the ApplicationIcon property to the path to the .ico file. This path is relative - // to the project file directory. If the selected file is not within the project directory - // tree, the file will be copied into the project file directory. Previously copied files - // are not deleted when a new file is selected. ApplicationIcon allows for both full paths - // and relative paths anywhere on disk, but we are not utilizing that in this implementation. - // The icon file does not need to be included in the project (such as a None/Content item). - [ExportInterceptingPropertyValueProvider(ConfigurationGeneral.ApplicationIconProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ApplicationIconValueProvider : InterceptingPropertyValueProviderBase + private readonly IProjectItemProvider _sourceItemsProvider; + private readonly UnconfiguredProject _unconfiguredProject; + private readonly IFileSystem _fileSystem; + + [ImportingConstructor] + public ApplicationIconValueProvider( + [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, + UnconfiguredProject unconfiguredProject, + IFileSystem fileSystem) { - private readonly IProjectItemProvider _sourceItemsProvider; - private readonly UnconfiguredProject _unconfiguredProject; - private readonly IFileSystem _fileSystem; + _sourceItemsProvider = sourceItemsProvider; + _unconfiguredProject = unconfiguredProject; + _fileSystem = fileSystem; + } - [ImportingConstructor] - public ApplicationIconValueProvider( - [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, - UnconfiguredProject unconfiguredProject, - IFileSystem fileSystem) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + // Allows the user to clear the application icon. + if (string.IsNullOrEmpty(unevaluatedPropertyValue)) { - _sourceItemsProvider = sourceItemsProvider; - _unconfiguredProject = unconfiguredProject; - _fileSystem = fileSystem; + return null; } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + // If selected path is invalid, ignore and return previous value. + if (!File.Exists(unevaluatedPropertyValue)) { - // Allows the user to clear the application icon. - if (string.IsNullOrEmpty(unevaluatedPropertyValue)) - { - return null; - } - - // If selected path is invalid, ignore and return previous value. - if (!File.Exists(unevaluatedPropertyValue)) - { - return await defaultProperties.GetUnevaluatedPropertyValueAsync(propertyName); - } + return await defaultProperties.GetUnevaluatedPropertyValueAsync(propertyName); + } - string propertyValue; - // Copy the file into the project directory. - // This isn't a limitation of ApplicationIcon; it is to simply mimic the legacy property page interaction. - if (_unconfiguredProject.IsOutsideProjectDirectory(unevaluatedPropertyValue)) + string propertyValue; + // Copy the file into the project directory. + // This isn't a limitation of ApplicationIcon; it is to simply mimic the legacy property page interaction. + if (_unconfiguredProject.IsOutsideProjectDirectory(unevaluatedPropertyValue)) + { + try { - try + propertyValue = Path.GetFileName(unevaluatedPropertyValue); + var destinationInfo = new FileInfo(Path.Combine(_unconfiguredProject.GetProjectDirectory(), propertyValue)); + if (destinationInfo.Exists && destinationInfo.IsReadOnly) { - propertyValue = Path.GetFileName(unevaluatedPropertyValue); - var destinationInfo = new FileInfo(Path.Combine(_unconfiguredProject.GetProjectDirectory(), propertyValue)); - if (destinationInfo.Exists && destinationInfo.IsReadOnly) - { - // The file cannot be copied over; return the previous value. - return await defaultProperties.GetUnevaluatedPropertyValueAsync(propertyName); - } - _fileSystem.CopyFile(unevaluatedPropertyValue, destinationInfo.FullName, overwrite: true); - } - catch - { - // If anything goes wrong with trying to copy the file, simply return the previous value. + // The file cannot be copied over; return the previous value. return await defaultProperties.GetUnevaluatedPropertyValueAsync(propertyName); } + _fileSystem.CopyFile(unevaluatedPropertyValue, destinationInfo.FullName, overwrite: true); } - else + catch { - propertyValue = PathHelper.MakeRelative(_unconfiguredProject, unevaluatedPropertyValue); - } - - string existingPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(ConfigurationGeneral.ApplicationIconProperty); - IProjectItem? existingItem = await GetExistingContentItemAsync(existingPropertyValue); - if (existingItem is not null) - { - await existingItem.SetUnevaluatedIncludeAsync(propertyValue); - } - else - { - await _sourceItemsProvider.AddAsync(Content.SchemaName, propertyValue); + // If anything goes wrong with trying to copy the file, simply return the previous value. + return await defaultProperties.GetUnevaluatedPropertyValueAsync(propertyName); } - - return propertyValue; + } + else + { + propertyValue = PathHelper.MakeRelative(_unconfiguredProject, unevaluatedPropertyValue); } - private async Task GetExistingContentItemAsync(string existingPropertyValue) + string existingPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(ConfigurationGeneral.ApplicationIconProperty); + IProjectItem? existingItem = await GetExistingContentItemAsync(existingPropertyValue); + if (existingItem is not null) + { + await existingItem.SetUnevaluatedIncludeAsync(propertyValue); + } + else { - return await _sourceItemsProvider.GetItemAsync(Content.SchemaName, ci => - // If the filename of this item and the filename of the property's value match, consider those to be related to one another. - ci.PropertiesContext is { IsProjectFile: true } && Path.GetFileName(ci.EvaluatedInclude).Equals(Path.GetFileName(existingPropertyValue), StringComparisons.PropertyLiteralValues)); + await _sourceItemsProvider.AddAsync(Content.SchemaName, propertyValue); } + + return propertyValue; + } + + private async Task GetExistingContentItemAsync(string existingPropertyValue) + { + return await _sourceItemsProvider.GetItemAsync(Content.SchemaName, ci => + // If the filename of this item and the filename of the property's value match, consider those to be related to one another. + ci.PropertiesContext is { IsProjectFile: true } && Path.GetFileName(ci.EvaluatedInclude).Equals(Path.GetFileName(existingPropertyValue), StringComparisons.PropertyLiteralValues)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProvider.cs index 803cb6eebe..f3ce0f8bb4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProvider.cs @@ -1,114 +1,113 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Supports choosing between no application manifest, a default manifest, and a custom manifest. +/// Adjusts the MSBuild properties NoWin32Manifest and ApplicationManifest as +/// appropriate. In the case of a custom manifest, the path to the manifest file is handled by the +/// . +/// +/// +/// This type, along with , provide the same +/// functionality as but in a different context. That +/// provider is currently used by the legacy property pages and the VS property APIs; these are +/// designed to be used by the new property pages. +/// +[ExportInterceptingPropertyValueProvider(ApplicationManifestKindProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ApplicationManifestKindValueProvider : InterceptingPropertyValueProviderBase { + private const string ApplicationManifestKindProperty = "ApplicationManifestKind"; + private const string NoManifestMSBuildProperty = "NoWin32Manifest"; + private const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; + private const string NoManifestValue = "NoManifest"; + private const string DefaultManifestValue = "DefaultManifest"; + private const string CustomManifestValue = "CustomManifest"; + private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; + + private static readonly string[] s_msBuildPropertyNames = { NoManifestMSBuildProperty, ApplicationManifestMSBuildProperty }; + + [ImportingConstructor] + public ApplicationManifestKindValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) + { + _temporaryPropertyStorage = temporaryPropertyStorage; + } + + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } + /// - /// Supports choosing between no application manifest, a default manifest, and a custom manifest. - /// Adjusts the MSBuild properties NoWin32Manifest and ApplicationManifest as - /// appropriate. In the case of a custom manifest, the path to the manifest file is handled by the - /// . + /// Gets the application manifest kind property /// /// - /// This type, along with , provide the same - /// functionality as but in a different context. That - /// provider is currently used by the legacy property pages and the VS property APIs; these are - /// designed to be used by the new property pages. + /// The Application Manifest kind's value is one of three possibilities: + /// - It's the value "CustomManifest" which means the user will supply the path to a custom manifest file. + /// - It's the value "NoManifest" which means the application doesn't have a manifest. + /// - It's the value "DefaultManifest" which means that the application will have a default manifest. + /// + /// These three values map to two MSBuild properties - ApplicationManifest (for the first case) or NoWin32Manifest + /// which is true for the second case and false or non-existent for the third. If those two properties aren't set + /// then we'll use the stored value (if any) or default to DefaultManifest. /// - [ExportInterceptingPropertyValueProvider(ApplicationManifestKindProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ApplicationManifestKindValueProvider : InterceptingPropertyValueProviderBase + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) { - private const string ApplicationManifestKindProperty = "ApplicationManifestKind"; - private const string NoManifestMSBuildProperty = "NoWin32Manifest"; - private const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; - private const string NoManifestValue = "NoManifest"; - private const string DefaultManifestValue = "DefaultManifest"; - private const string CustomManifestValue = "CustomManifest"; - private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; - - private static readonly string[] s_msBuildPropertyNames = { NoManifestMSBuildProperty, ApplicationManifestMSBuildProperty }; - - [ImportingConstructor] - public ApplicationManifestKindValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) + if (!string.IsNullOrEmpty(await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty))) { - _temporaryPropertyStorage = temporaryPropertyStorage; + return CustomManifestValue; } - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + string noManifestPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(NoManifestMSBuildProperty); + if (StringComparers.PropertyLiteralValues.Equals(noManifestPropertyValue, "true")) { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + return NoManifestValue; } - /// - /// Gets the application manifest kind property - /// - /// - /// The Application Manifest kind's value is one of three possibilities: - /// - It's the value "CustomManifest" which means the user will supply the path to a custom manifest file. - /// - It's the value "NoManifest" which means the application doesn't have a manifest. - /// - It's the value "DefaultManifest" which means that the application will have a default manifest. - /// - /// These three values map to two MSBuild properties - ApplicationManifest (for the first case) or NoWin32Manifest - /// which is true for the second case and false or non-existent for the third. If those two properties aren't set - /// then we'll use the stored value (if any) or default to DefaultManifest. - /// - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + string? storedValue = _temporaryPropertyStorage.GetPropertyValue(ApplicationManifestKindProperty); + if (!Strings.IsNullOrEmpty(storedValue)) { - if (!string.IsNullOrEmpty(await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty))) - { - return CustomManifestValue; - } + return storedValue; + } - string noManifestPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(NoManifestMSBuildProperty); - if (StringComparers.PropertyLiteralValues.Equals(noManifestPropertyValue, "true")) - { - return NoManifestValue; - } + // It doesn't matter if it is set to false or the value is not present. We default to "DefaultManifest" scenario. + return DefaultManifestValue; + } - string? storedValue = _temporaryPropertyStorage.GetPropertyValue(ApplicationManifestKindProperty); - if (!Strings.IsNullOrEmpty(storedValue)) - { - return storedValue; - } + /// + /// Sets the application manifest kind property + /// + public override async Task OnSetPropertyValueAsync( + string propertyName, + string? unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, DefaultManifestValue)) + { + _temporaryPropertyStorage.AddOrUpdatePropertyValue(ApplicationManifestKindProperty, DefaultManifestValue); - // It doesn't matter if it is set to false or the value is not present. We default to "DefaultManifest" scenario. - return DefaultManifestValue; + await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); + await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); } - - /// - /// Sets the application manifest kind property - /// - public override async Task OnSetPropertyValueAsync( - string propertyName, - string? unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) + else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, NoManifestValue)) { - if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, DefaultManifestValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(ApplicationManifestKindProperty, DefaultManifestValue); + _temporaryPropertyStorage.AddOrUpdatePropertyValue(ApplicationManifestKindProperty, NoManifestValue); - await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); - await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); - } - else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, NoManifestValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(ApplicationManifestKindProperty, NoManifestValue); - - await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); - await defaultProperties.SetPropertyValueAsync(NoManifestMSBuildProperty, "true"); - } - else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, CustomManifestValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(ApplicationManifestKindProperty, CustomManifestValue); - - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); - } + await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); + await defaultProperties.SetPropertyValueAsync(NoManifestMSBuildProperty, "true"); + } + else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, CustomManifestValue)) + { + _temporaryPropertyStorage.AddOrUpdatePropertyValue(ApplicationManifestKindProperty, CustomManifestValue); - // We don't want to store a value for this so return null. - return null; + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); + await defaultProperties.DeletePropertyAsync(NoManifestMSBuildProperty); } + + // We don't want to store a value for this so return null. + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestPathValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestPathValueProvider.cs index ff391eb668..59d516fe3c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestPathValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ApplicationManifestPathValueProvider.cs @@ -1,60 +1,59 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Supports setting the path to a custom manifest file. Ultimately this reads and writes the +/// ApplicationManifest MSBuild property. It also handles saving the manifest path as +/// a relative path, if possible. +/// +/// +/// This type, along with , provide the same +/// functionality as but in a different context. That +/// provider is currently used by the legacy property pages and the VS property APIs; these are +/// designed to be used by the new property pages. +/// +[ExportInterceptingPropertyValueProvider("ApplicationManifestPath", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ApplicationManifestPathValueProvider : InterceptingPropertyValueProviderBase { - /// - /// Supports setting the path to a custom manifest file. Ultimately this reads and writes the - /// ApplicationManifest MSBuild property. It also handles saving the manifest path as - /// a relative path, if possible. - /// - /// - /// This type, along with , provide the same - /// functionality as but in a different context. That - /// provider is currently used by the legacy property pages and the VS property APIs; these are - /// designed to be used by the new property pages. - /// - [ExportInterceptingPropertyValueProvider("ApplicationManifestPath", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ApplicationManifestPathValueProvider : InterceptingPropertyValueProviderBase + private readonly UnconfiguredProject _unconfiguredProject; + + private const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; + private static readonly string[] s_msBuildPropertyNames = { ApplicationManifestMSBuildProperty }; + + [ImportingConstructor] + public ApplicationManifestPathValueProvider(UnconfiguredProject project) { - private readonly UnconfiguredProject _unconfiguredProject; + _unconfiguredProject = project; + } - private const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; - private static readonly string[] s_msBuildPropertyNames = { ApplicationManifestMSBuildProperty }; - - [ImportingConstructor] - public ApplicationManifestPathValueProvider(UnconfiguredProject project) - { - _unconfiguredProject = project; - } + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); - } + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty); + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - return defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty); - } + public override async Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return await defaultProperties.GetUnevaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty) + ?? string.Empty; + } - public override async Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + // If we can make the path relative to the project folder do so. Otherwise just use the given path. + if (Path.IsPathRooted(unevaluatedPropertyValue) && + PathHelper.TryMakeRelativeToProjectDirectory(_unconfiguredProject, unevaluatedPropertyValue, out string? relativePath)) { - return await defaultProperties.GetUnevaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty) - ?? string.Empty; + unevaluatedPropertyValue = relativePath; } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) - { - // If we can make the path relative to the project folder do so. Otherwise just use the given path. - if (Path.IsPathRooted(unevaluatedPropertyValue) && - PathHelper.TryMakeRelativeToProjectDirectory(_unconfiguredProject, unevaluatedPropertyValue, out string? relativePath)) - { - unevaluatedPropertyValue = relativePath; - } - - await defaultProperties.SetPropertyValueAsync(ApplicationManifestMSBuildProperty, unevaluatedPropertyValue); + await defaultProperties.SetPropertyValueAsync(ApplicationManifestMSBuildProperty, unevaluatedPropertyValue); - return null; - } + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/CompoundTargetFrameworkValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/CompoundTargetFrameworkValueProvider.cs index 6367386612..889a242c75 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/CompoundTargetFrameworkValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/CompoundTargetFrameworkValueProvider.cs @@ -2,335 +2,334 @@ using System.Text.RegularExpressions; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// The complex Target Framework property consists of three UI-exposed properties: target framework moniker, target platform and target platform version. +/// Those combine and produce a single value like this: [target framework alias]-[target platform][target platform version]. +/// This class intercepts those properties from the UI to saved their values into the TargetFramework property. +/// In the case of retrieving the values of those properties, this class looks for the associated properties found in the project's configuration. +/// +[ExportInterceptingPropertyValueProvider( + new[] + { + InterceptedTargetFrameworkProperty, + TargetPlatformProperty, + TargetPlatformVersionProperty + }, + ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal class CompoundTargetFrameworkValueProvider : InterceptingPropertyValueProviderBase { - /// - /// The complex Target Framework property consists of three UI-exposed properties: target framework moniker, target platform and target platform version. - /// Those combine and produce a single value like this: [target framework alias]-[target platform][target platform version]. - /// This class intercepts those properties from the UI to saved their values into the TargetFramework property. - /// In the case of retrieving the values of those properties, this class looks for the associated properties found in the project's configuration. - /// - [ExportInterceptingPropertyValueProvider( - new[] - { - InterceptedTargetFrameworkProperty, - TargetPlatformProperty, - TargetPlatformVersionProperty - }, - ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal class CompoundTargetFrameworkValueProvider : InterceptingPropertyValueProviderBase + private const string InterceptedTargetFrameworkProperty = "InterceptedTargetFramework"; + private const string TargetPlatformProperty = ConfigurationGeneral.TargetPlatformIdentifierProperty; + private const string TargetPlatformVersionProperty = ConfigurationGeneral.TargetPlatformVersionProperty; + private const string TargetFrameworkProperty = ConfigurationGeneral.TargetFrameworkProperty; + private const string SupportedOSPlatformVersionProperty = "SupportedOSPlatformVersion"; + + private readonly ProjectProperties _properties; + private readonly ConfiguredProject _configuredProject; + private bool? _useWPFProperty; + private bool? _useWindowsFormsProperty; + private bool? _useWinUIProperty; + private static readonly string[] s_msBuildPropertyNames = { TargetFrameworkProperty, TargetPlatformProperty, TargetPlatformVersionProperty, SupportedOSPlatformVersionProperty }; + private static readonly Regex s_versionRegex = new(@"^net(?[0-9.]+)$", RegexOptions.ExplicitCapture); + + private struct ComplexTargetFramework { - private const string InterceptedTargetFrameworkProperty = "InterceptedTargetFramework"; - private const string TargetPlatformProperty = ConfigurationGeneral.TargetPlatformIdentifierProperty; - private const string TargetPlatformVersionProperty = ConfigurationGeneral.TargetPlatformVersionProperty; - private const string TargetFrameworkProperty = ConfigurationGeneral.TargetFrameworkProperty; - private const string SupportedOSPlatformVersionProperty = "SupportedOSPlatformVersion"; - - private readonly ProjectProperties _properties; - private readonly ConfiguredProject _configuredProject; - private bool? _useWPFProperty; - private bool? _useWindowsFormsProperty; - private bool? _useWinUIProperty; - private static readonly string[] s_msBuildPropertyNames = { TargetFrameworkProperty, TargetPlatformProperty, TargetPlatformVersionProperty, SupportedOSPlatformVersionProperty }; - private static readonly Regex s_versionRegex = new(@"^net(?[0-9.]+)$", RegexOptions.ExplicitCapture); - - private struct ComplexTargetFramework - { - public string? TargetFrameworkMoniker; - public string? TargetPlatformIdentifier; - public string? TargetPlatformVersion; - public string? TargetFrameworkIdentifier; - public string? TargetFramework; - } + public string? TargetFrameworkMoniker; + public string? TargetPlatformIdentifier; + public string? TargetPlatformVersion; + public string? TargetFrameworkIdentifier; + public string? TargetFramework; + } - [ImportingConstructor] - public CompoundTargetFrameworkValueProvider(ProjectProperties properties) - { - _properties = properties; - _configuredProject = properties.ConfiguredProject; - } + [ImportingConstructor] + public CompoundTargetFrameworkValueProvider(ProjectProperties properties) + { + _properties = properties; + _configuredProject = properties.ConfiguredProject; + } - private async Task GetStoredPropertiesAsync() - { - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - return await GetStoredComplexTargetFrameworkAsync(configuration); - } + private async Task GetStoredPropertiesAsync() + { + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + return await GetStoredComplexTargetFrameworkAsync(configuration); + } - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); - } + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) - { - ComplexTargetFramework storedProperties = await GetStoredPropertiesAsync(); + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + ComplexTargetFramework storedProperties = await GetStoredPropertiesAsync(); - if (storedProperties.TargetFrameworkMoniker is not null) + if (storedProperties.TargetFrameworkMoniker is not null) + { + // Changing the Target Framework Moniker + if (StringComparers.PropertyLiteralValues.Equals(propertyName, InterceptedTargetFrameworkProperty)) { - // Changing the Target Framework Moniker - if (StringComparers.PropertyLiteralValues.Equals(propertyName, InterceptedTargetFrameworkProperty)) + if (Strings.IsNullOrEmpty(unevaluatedPropertyValue)) { - if (Strings.IsNullOrEmpty(unevaluatedPropertyValue)) - { - return null; - } - - storedProperties.TargetFrameworkMoniker = unevaluatedPropertyValue; - - // Only projects targeting .NET 5 or higher use platform properties. - string targetFrameworkAlias = await GetTargetFrameworkAliasAsync(unevaluatedPropertyValue); - if (!IsNetCore5OrHigher(targetFrameworkAlias)) - { - // Delete platform properties - storedProperties.TargetPlatformIdentifier = null; - storedProperties.TargetPlatformVersion = null; - await ResetPlatformPropertiesAsync(defaultProperties); - } + return null; } - - // Changing the Target Platform Identifier - else if (StringComparers.PropertyLiteralValues.Equals(propertyName, TargetPlatformProperty)) + + storedProperties.TargetFrameworkMoniker = unevaluatedPropertyValue; + + // Only projects targeting .NET 5 or higher use platform properties. + string targetFrameworkAlias = await GetTargetFrameworkAliasAsync(unevaluatedPropertyValue); + if (!IsNetCore5OrHigher(targetFrameworkAlias)) { - if (unevaluatedPropertyValue != storedProperties.TargetPlatformIdentifier) - { - // Delete platform properties. - storedProperties.TargetPlatformVersion = null; - await ResetPlatformPropertiesAsync(defaultProperties); - - storedProperties.TargetPlatformIdentifier = unevaluatedPropertyValue; - } + // Delete platform properties + storedProperties.TargetPlatformIdentifier = null; + storedProperties.TargetPlatformVersion = null; + await ResetPlatformPropertiesAsync(defaultProperties); } + } - // Changing the Target Platform Version - else if (StringComparers.PropertyLiteralValues.Equals(propertyName, TargetPlatformVersionProperty)) + // Changing the Target Platform Identifier + else if (StringComparers.PropertyLiteralValues.Equals(propertyName, TargetPlatformProperty)) + { + if (unevaluatedPropertyValue != storedProperties.TargetPlatformIdentifier) { - storedProperties.TargetPlatformVersion = unevaluatedPropertyValue; + // Delete platform properties. + storedProperties.TargetPlatformVersion = null; + await ResetPlatformPropertiesAsync(defaultProperties); + + storedProperties.TargetPlatformIdentifier = unevaluatedPropertyValue; } + } - await defaultProperties.SetPropertyValueAsync(TargetFrameworkProperty, await ComputeValueAsync(storedProperties)); + // Changing the Target Platform Version + else if (StringComparers.PropertyLiteralValues.Equals(propertyName, TargetPlatformVersionProperty)) + { + storedProperties.TargetPlatformVersion = unevaluatedPropertyValue; } - return null; + await defaultProperties.SetPropertyValueAsync(TargetFrameworkProperty, await ComputeValueAsync(storedProperties)); } - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - string? storedValue = await GetStoredValueAsync(configuration, propertyName); + return null; + } - if (storedValue is null) - { - return string.Empty; - } + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + string? storedValue = await GetStoredValueAsync(configuration, propertyName); - return storedValue; + if (storedValue is null) + { + return string.Empty; } - public override async Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - string? storedValue = await GetStoredValueAsync(configuration, propertyName); + return storedValue; + } - if (storedValue is null) - { - return string.Empty; - } + public override async Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + string? storedValue = await GetStoredValueAsync(configuration, propertyName); - return storedValue; + if (storedValue is null) + { + return string.Empty; } - private static async Task GetStoredComplexTargetFrameworkAsync(ConfigurationGeneral configuration) + return storedValue; + } + + private static async Task GetStoredComplexTargetFrameworkAsync(ConfigurationGeneral configuration) + { + var storedValues = new ComplexTargetFramework { - var storedValues = new ComplexTargetFramework - { - TargetFrameworkMoniker = (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(), - TargetPlatformIdentifier = (string?)await configuration.TargetPlatformIdentifier.GetValueAsync(), - TargetPlatformVersion = (string?)await configuration.TargetPlatformVersion.GetValueAsync(), - TargetFrameworkIdentifier = (string?)await configuration.TargetFrameworkIdentifier.GetValueAsync(), - TargetFramework = (string?)await configuration.TargetFramework.GetValueAsync() - }; - - return storedValues; - } + TargetFrameworkMoniker = (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(), + TargetPlatformIdentifier = (string?)await configuration.TargetPlatformIdentifier.GetValueAsync(), + TargetPlatformVersion = (string?)await configuration.TargetPlatformVersion.GetValueAsync(), + TargetFrameworkIdentifier = (string?)await configuration.TargetFrameworkIdentifier.GetValueAsync(), + TargetFramework = (string?)await configuration.TargetFramework.GetValueAsync() + }; + + return storedValues; + } - private static async Task GetStoredValueAsync(ConfigurationGeneral configuration, string propertyName) + private static async Task GetStoredValueAsync(ConfigurationGeneral configuration, string propertyName) + { + return propertyName switch { - return propertyName switch - { - InterceptedTargetFrameworkProperty => (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(), - TargetPlatformProperty => (string?)await configuration.TargetPlatformIdentifier.GetValueAsync(), - TargetPlatformVersionProperty => (string?)await configuration.TargetPlatformVersion.GetValueAsync(), - TargetFrameworkProperty => (string?)await configuration.TargetFramework.GetValueAsync(), - _ => string.Empty, - }; + InterceptedTargetFrameworkProperty => (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(), + TargetPlatformProperty => (string?)await configuration.TargetPlatformIdentifier.GetValueAsync(), + TargetPlatformVersionProperty => (string?)await configuration.TargetPlatformVersion.GetValueAsync(), + TargetFrameworkProperty => (string?)await configuration.TargetFramework.GetValueAsync(), + _ => string.Empty, + }; + } + + /// + /// Combines the three values that make up the TargetFramework property. + /// In order to save the correct value, we make calls to retrieve the alias for the target framework and platform + /// (.NET 5.0 => net5.0, .NET Core 3.1 => netcoreapp3.1, Windows => windows). + /// Examples: + /// { .NET 5.0, Windows, 10.0 } => net5.0-windows10.0 + /// { .NET 5.0, null, null } => net5.0 + /// { null, null, null } => String.Empty + /// + /// A struct with each property. + /// A string with the combined values. + private async Task ComputeValueAsync(ComplexTargetFramework complexTargetFramework) + { + if (Strings.IsNullOrEmpty(complexTargetFramework.TargetFrameworkMoniker)) + { + return string.Empty; } - /// - /// Combines the three values that make up the TargetFramework property. - /// In order to save the correct value, we make calls to retrieve the alias for the target framework and platform - /// (.NET 5.0 => net5.0, .NET Core 3.1 => netcoreapp3.1, Windows => windows). - /// Examples: - /// { .NET 5.0, Windows, 10.0 } => net5.0-windows10.0 - /// { .NET 5.0, null, null } => net5.0 - /// { null, null, null } => String.Empty - /// - /// A struct with each property. - /// A string with the combined values. - private async Task ComputeValueAsync(ComplexTargetFramework complexTargetFramework) + string targetFrameworkAlias = await GetTargetFrameworkAliasAsync(complexTargetFramework.TargetFrameworkMoniker); + + if (string.IsNullOrEmpty(targetFrameworkAlias)) { - if (Strings.IsNullOrEmpty(complexTargetFramework.TargetFrameworkMoniker)) + // The value on the TargetFrameworkMoniker is not on the supported list and we shouldn't try to parse it. + // Therefore, we return the user value as it is. I.e. foo + if (!Strings.IsNullOrEmpty(complexTargetFramework.TargetFramework)) { - return string.Empty; + return complexTargetFramework.TargetFramework; } + } - string targetFrameworkAlias = await GetTargetFrameworkAliasAsync(complexTargetFramework.TargetFrameworkMoniker); + // Check if the project requires an explicit platform. + if (IsNetCore5OrHigher(targetFrameworkAlias) && await IsWindowsPlatformNeededAsync()) + { + // Ideally we would set the complexTargetFramework.TargetPlatformIdentifier = "Windows", + // but we're in a difficult position right now to retrieve the correct TargetPlatformAlias from GetTargetPlatformAliasAsync below, + // reason being it calls for the previous TargetFramework, not the new one that is being set. + // Therefore, in the case where we are going from a TargetFramework with no need of platform, like netcoreapp3.1, + // to one that does, like net5.0, we would be querying the list of TargetPlatformAlias for netcoreapp3.1 and get an empty list. + // I have to revisit this approach, but for now we can pass the TargetPlatformAlias that should be. + return targetFrameworkAlias + "-windows" + complexTargetFramework.TargetPlatformVersion; + } - if (string.IsNullOrEmpty(targetFrameworkAlias)) - { - // The value on the TargetFrameworkMoniker is not on the supported list and we shouldn't try to parse it. - // Therefore, we return the user value as it is. I.e. foo - if (!Strings.IsNullOrEmpty(complexTargetFramework.TargetFramework)) - { - return complexTargetFramework.TargetFramework; - } - } + // We only keep the platform properties for projects targeting .NET 5 or higher. + if (!Strings.IsNullOrEmpty(complexTargetFramework.TargetPlatformIdentifier) && IsNetCore5OrHigher(targetFrameworkAlias)) + { + string targetPlatformAlias = await GetTargetPlatformAliasAsync(complexTargetFramework.TargetPlatformIdentifier); - // Check if the project requires an explicit platform. - if (IsNetCore5OrHigher(targetFrameworkAlias) && await IsWindowsPlatformNeededAsync()) + if (string.IsNullOrEmpty(targetPlatformAlias)) { - // Ideally we would set the complexTargetFramework.TargetPlatformIdentifier = "Windows", - // but we're in a difficult position right now to retrieve the correct TargetPlatformAlias from GetTargetPlatformAliasAsync below, - // reason being it calls for the previous TargetFramework, not the new one that is being set. - // Therefore, in the case where we are going from a TargetFramework with no need of platform, like netcoreapp3.1, - // to one that does, like net5.0, we would be querying the list of TargetPlatformAlias for netcoreapp3.1 and get an empty list. - // I have to revisit this approach, but for now we can pass the TargetPlatformAlias that should be. - return targetFrameworkAlias + "-windows" + complexTargetFramework.TargetPlatformVersion; + return targetFrameworkAlias; } - // We only keep the platform properties for projects targeting .NET 5 or higher. - if (!Strings.IsNullOrEmpty(complexTargetFramework.TargetPlatformIdentifier) && IsNetCore5OrHigher(targetFrameworkAlias)) - { - string targetPlatformAlias = await GetTargetPlatformAliasAsync(complexTargetFramework.TargetPlatformIdentifier); + return targetFrameworkAlias + "-" + targetPlatformAlias + complexTargetFramework.TargetPlatformVersion; + } - if (string.IsNullOrEmpty(targetPlatformAlias)) - { - return targetFrameworkAlias; - } + return targetFrameworkAlias; + } - return targetFrameworkAlias + "-" + targetPlatformAlias + complexTargetFramework.TargetPlatformVersion; - } + private static bool IsNetCore5OrHigher(string targetFrameworkAlias) + { + // Ideally, we want to use the TargetFrameworkIdentifier and TargetFrameworkVersion; + // however, in this case the target framework properties we have are for the currently set value, + // not the value we want to set it to (for example, if we go from netcoreapp3.1 to net5.0). + // The only property we have that describes the value to be set is the TargetFrameworkAlias which + // is passed to this method. - return targetFrameworkAlias; - } + Match match = s_versionRegex.Match(targetFrameworkAlias); - private static bool IsNetCore5OrHigher(string targetFrameworkAlias) + if (match.Success) { - // Ideally, we want to use the TargetFrameworkIdentifier and TargetFrameworkVersion; - // however, in this case the target framework properties we have are for the currently set value, - // not the value we want to set it to (for example, if we go from netcoreapp3.1 to net5.0). - // The only property we have that describes the value to be set is the TargetFrameworkAlias which - // is passed to this method. + string versionString = match.Groups["version"].Value; - Match match = s_versionRegex.Match(targetFrameworkAlias); - - if (match.Success) + if (Version.TryParse(versionString, out Version? version)) { - string versionString = match.Groups["version"].Value; - - if (Version.TryParse(versionString, out Version? version)) + if (version.Major >= 5) { - if (version.Major >= 5) - { - // This is a .NET Core app with version greater than five. - return true; - } + // This is a .NET Core app with version greater than five. + return true; } } - return false; } + return false; + } - private async Task IsWindowsPlatformNeededAsync() - { - // Checks if the project has either UseWPF or UseWindowsForms properties. - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - _useWPFProperty = (bool?)await configuration.UseWPF.GetValueAsync(); - - if (_useWPFProperty is not null) - { - return (bool)_useWPFProperty; - } - - _useWindowsFormsProperty = (bool?)await configuration.UseWindowsForms.GetValueAsync(); - if (_useWindowsFormsProperty is not null) - { - return (bool)_useWindowsFormsProperty; - } + private async Task IsWindowsPlatformNeededAsync() + { + // Checks if the project has either UseWPF or UseWindowsForms properties. + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + _useWPFProperty = (bool?)await configuration.UseWPF.GetValueAsync(); - _useWinUIProperty = (bool?)await configuration.UseWinUI.GetValueAsync(); - if (_useWinUIProperty is not null) - { - return (bool)_useWinUIProperty; - } + if (_useWPFProperty is not null) + { + return (bool)_useWPFProperty; + } - return false; + _useWindowsFormsProperty = (bool?)await configuration.UseWindowsForms.GetValueAsync(); + if (_useWindowsFormsProperty is not null) + { + return (bool)_useWindowsFormsProperty; } - /// - /// Retrieves the target framework alias (i.e. net5.0) from the project's subscription service. - /// - /// - internal virtual async Task GetTargetFrameworkAliasAsync(string targetFrameworkMoniker) + _useWinUIProperty = (bool?)await configuration.UseWinUI.GetValueAsync(); + if (_useWinUIProperty is not null) { - IProjectSubscriptionService? subscriptionService = _configuredProject.Services.ProjectSubscription; - Assumes.Present(subscriptionService); + return (bool)_useWinUIProperty; + } - IImmutableDictionary supportedTargetFrameworks = await subscriptionService.ProjectRuleSource.GetLatestVersionAsync(_configuredProject, new string[] { SupportedTargetFramework.SchemaName }); - IProjectRuleSnapshot targetFrameworkRuleSnapshot = supportedTargetFrameworks[SupportedTargetFramework.SchemaName]; + return false; + } - IImmutableDictionary? targetFrameworkProperties = targetFrameworkRuleSnapshot.GetProjectItemProperties(targetFrameworkMoniker); + /// + /// Retrieves the target framework alias (i.e. net5.0) from the project's subscription service. + /// + /// + internal virtual async Task GetTargetFrameworkAliasAsync(string targetFrameworkMoniker) + { + IProjectSubscriptionService? subscriptionService = _configuredProject.Services.ProjectSubscription; + Assumes.Present(subscriptionService); - if (targetFrameworkProperties is not null && - targetFrameworkProperties.TryGetValue(SupportedTargetFramework.AliasProperty, out string? targetFrameworkAlias)) - { - return targetFrameworkAlias; - } + IImmutableDictionary supportedTargetFrameworks = await subscriptionService.ProjectRuleSource.GetLatestVersionAsync(_configuredProject, new string[] { SupportedTargetFramework.SchemaName }); + IProjectRuleSnapshot targetFrameworkRuleSnapshot = supportedTargetFrameworks[SupportedTargetFramework.SchemaName]; - return string.Empty; - } + IImmutableDictionary? targetFrameworkProperties = targetFrameworkRuleSnapshot.GetProjectItemProperties(targetFrameworkMoniker); - /// - /// Retrieves the associated platform alias (i.e. windows) from the project's subscription service. - /// - /// - /// - private async Task GetTargetPlatformAliasAsync(string targetPlatformIdentifier) + if (targetFrameworkProperties is not null && + targetFrameworkProperties.TryGetValue(SupportedTargetFramework.AliasProperty, out string? targetFrameworkAlias)) { - IProjectSubscriptionService? subscriptionService = _configuredProject.Services.ProjectSubscription; - Assumes.Present(subscriptionService); + return targetFrameworkAlias; + } - IImmutableDictionary sdkSupportedTargetPlatformIdentifiers = await subscriptionService.ProjectRuleSource.GetLatestVersionAsync(_configuredProject, new string[] { SdkSupportedTargetPlatformIdentifier.SchemaName }); + return string.Empty; + } - IProjectRuleSnapshot sdkSupportedTargetPlatformIdentifierRuleSnapshot = sdkSupportedTargetPlatformIdentifiers[SdkSupportedTargetPlatformIdentifier.SchemaName]; + /// + /// Retrieves the associated platform alias (i.e. windows) from the project's subscription service. + /// + /// + /// + private async Task GetTargetPlatformAliasAsync(string targetPlatformIdentifier) + { + IProjectSubscriptionService? subscriptionService = _configuredProject.Services.ProjectSubscription; + Assumes.Present(subscriptionService); - // The SdkSupportedTargetPlatformIdentifier rule stores the alias in the key value. - if (sdkSupportedTargetPlatformIdentifierRuleSnapshot.Items.TryGetKey(targetPlatformIdentifier, out string targetPlatformAlias)) - { - return targetPlatformAlias; - } + IImmutableDictionary sdkSupportedTargetPlatformIdentifiers = await subscriptionService.ProjectRuleSource.GetLatestVersionAsync(_configuredProject, new string[] { SdkSupportedTargetPlatformIdentifier.SchemaName }); - return string.Empty; - } + IProjectRuleSnapshot sdkSupportedTargetPlatformIdentifierRuleSnapshot = sdkSupportedTargetPlatformIdentifiers[SdkSupportedTargetPlatformIdentifier.SchemaName]; - /// - /// Resets the values on the TargetPlatformProperty and SupportedOSPlatformVersionProperty. - /// - /// - /// - private static async Task ResetPlatformPropertiesAsync(IProjectProperties projectProperties) + // The SdkSupportedTargetPlatformIdentifier rule stores the alias in the key value. + if (sdkSupportedTargetPlatformIdentifierRuleSnapshot.Items.TryGetKey(targetPlatformIdentifier, out string targetPlatformAlias)) { - await projectProperties.DeletePropertyAsync(TargetPlatformProperty); - await projectProperties.DeletePropertyAsync(TargetPlatformVersionProperty); - await projectProperties.DeletePropertyAsync(SupportedOSPlatformVersionProperty); + return targetPlatformAlias; } + + return string.Empty; + } + + /// + /// Resets the values on the TargetPlatformProperty and SupportedOSPlatformVersionProperty. + /// + /// + /// + private static async Task ResetPlatformPropertiesAsync(IProjectProperties projectProperties) + { + await projectProperties.DeletePropertyAsync(TargetPlatformProperty); + await projectProperties.DeletePropertyAsync(TargetPlatformVersionProperty); + await projectProperties.DeletePropertyAsync(SupportedOSPlatformVersionProperty); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/InstallOtherFrameworksValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/InstallOtherFrameworksValueProvider.cs index 93d77fcf25..e8d91c5274 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/InstallOtherFrameworksValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/InstallOtherFrameworksValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("InstallOtherFrameworks", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class InstallOtherFrameworksValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("InstallOtherFrameworks", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class InstallOtherFrameworksValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProvider.cs index 8eeeb53be2..215af8c16f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProvider.cs @@ -1,94 +1,93 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider(ResourceSpecificationKindProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ResourceSpecificationKindValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider(ResourceSpecificationKindProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ResourceSpecificationKindValueProvider : InterceptingPropertyValueProviderBase - { - private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; + private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; - internal const string ResourceSpecificationKindProperty = "ResourceSpecificationKind"; - internal const string Win32ResourceMSBuildProperty = "Win32Resource"; - internal const string ApplicationIconMSBuildProperty = "ApplicationIcon"; - internal const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; - internal const string IconAndManifestValue = "IconAndManifest"; - internal const string ResourceFileValue = "ResourceFile"; + internal const string ResourceSpecificationKindProperty = "ResourceSpecificationKind"; + internal const string Win32ResourceMSBuildProperty = "Win32Resource"; + internal const string ApplicationIconMSBuildProperty = "ApplicationIcon"; + internal const string ApplicationManifestMSBuildProperty = "ApplicationManifest"; + internal const string IconAndManifestValue = "IconAndManifest"; + internal const string ResourceFileValue = "ResourceFile"; - private static readonly string[] s_msBuildPropertyNames = { Win32ResourceMSBuildProperty, ApplicationIconMSBuildProperty, ApplicationManifestMSBuildProperty }; + private static readonly string[] s_msBuildPropertyNames = { Win32ResourceMSBuildProperty, ApplicationIconMSBuildProperty, ApplicationManifestMSBuildProperty }; - [ImportingConstructor] - public ResourceSpecificationKindValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) - { - _temporaryPropertyStorage = temporaryPropertyStorage; - } + [ImportingConstructor] + public ResourceSpecificationKindValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) + { + _temporaryPropertyStorage = temporaryPropertyStorage; + } - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); - } + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, IconAndManifestValue)) { - if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, IconAndManifestValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(ResourceSpecificationKindProperty, IconAndManifestValue); - - await defaultProperties.SaveValueIfCurrentlySetAsync(Win32ResourceMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(Win32ResourceMSBuildProperty); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(ApplicationIconMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - } - else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, ResourceFileValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(ResourceSpecificationKindProperty, ResourceFileValue); - - await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationIconMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(ApplicationIconMSBuildProperty); - await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(Win32ResourceMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - } - - return null; - } + _temporaryPropertyStorage.AddOrUpdatePropertyValue(ResourceSpecificationKindProperty, IconAndManifestValue); - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + await defaultProperties.SaveValueIfCurrentlySetAsync(Win32ResourceMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(Win32ResourceMSBuildProperty); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(ApplicationIconMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); + } + else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, ResourceFileValue)) { - string win32Resource = await defaultProperties.GetEvaluatedPropertyValueAsync(Win32ResourceMSBuildProperty); - string applicationIconResource = await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationIconMSBuildProperty); - string applicationManifestResource = await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty); + _temporaryPropertyStorage.AddOrUpdatePropertyValue(ResourceSpecificationKindProperty, ResourceFileValue); - return ComputeValue(win32Resource, applicationIconResource, applicationManifestResource); + await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationIconMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.SaveValueIfCurrentlySetAsync(ApplicationManifestMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(ApplicationIconMSBuildProperty); + await defaultProperties.DeletePropertyAsync(ApplicationManifestMSBuildProperty); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(Win32ResourceMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); } - public override async Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - string? win32Resource = await defaultProperties.GetUnevaluatedPropertyValueAsync(Win32ResourceMSBuildProperty); - string? applicationIconResource = await defaultProperties.GetUnevaluatedPropertyValueAsync(ApplicationIconMSBuildProperty); - string? applicationManifestResource = await defaultProperties.GetUnevaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty); + return null; + } - return ComputeValue(win32Resource, applicationIconResource, applicationManifestResource); + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + string win32Resource = await defaultProperties.GetEvaluatedPropertyValueAsync(Win32ResourceMSBuildProperty); + string applicationIconResource = await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationIconMSBuildProperty); + string applicationManifestResource = await defaultProperties.GetEvaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty); + + return ComputeValue(win32Resource, applicationIconResource, applicationManifestResource); + } + + public override async Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + string? win32Resource = await defaultProperties.GetUnevaluatedPropertyValueAsync(Win32ResourceMSBuildProperty); + string? applicationIconResource = await defaultProperties.GetUnevaluatedPropertyValueAsync(ApplicationIconMSBuildProperty); + string? applicationManifestResource = await defaultProperties.GetUnevaluatedPropertyValueAsync(ApplicationManifestMSBuildProperty); + + return ComputeValue(win32Resource, applicationIconResource, applicationManifestResource); + } + + private string ComputeValue(string? win32Resource, string? applicationIconResource, string? applicationManifestResource) + { + if (!string.IsNullOrEmpty(applicationIconResource) + || !string.IsNullOrEmpty(applicationManifestResource)) + { + return IconAndManifestValue; } - private string ComputeValue(string? win32Resource, string? applicationIconResource, string? applicationManifestResource) + if (!string.IsNullOrEmpty(win32Resource)) { - if (!string.IsNullOrEmpty(applicationIconResource) - || !string.IsNullOrEmpty(applicationManifestResource)) - { - return IconAndManifestValue; - } - - if (!string.IsNullOrEmpty(win32Resource)) - { - return ResourceFileValue; - } - - if (_temporaryPropertyStorage.GetPropertyValue(ResourceSpecificationKindProperty) is string savedValue) - { - return savedValue; - } + return ResourceFileValue; + } - return IconAndManifestValue; + if (_temporaryPropertyStorage.GetPropertyValue(ResourceSpecificationKindProperty) is string savedValue) + { + return savedValue; } + + return IconAndManifestValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/TargetMultipleFrameworksValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/TargetMultipleFrameworksValueProvider.cs index 736c7eea6d..e9fbea604e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/TargetMultipleFrameworksValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ApplicationPropertyPage/TargetMultipleFrameworksValueProvider.cs @@ -3,71 +3,70 @@ using Microsoft.VisualStudio.Text; using static Microsoft.VisualStudio.ProjectSystem.ConfigurationGeneral; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("TargetMultipleFrameworks", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class TargetMultipleFrameworksValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("TargetMultipleFrameworks", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class TargetMultipleFrameworksValueProvider : InterceptingPropertyValueProviderBase + private static readonly string[] s_msBuildPropertyNames = { TargetFrameworksProperty, TargetFrameworkProperty}; + + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) { - private static readonly string[] s_msBuildPropertyNames = { TargetFrameworksProperty, TargetFrameworkProperty}; - - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); - } + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (bool.TryParse(unevaluatedPropertyValue, out bool value)) { - if (bool.TryParse(unevaluatedPropertyValue, out bool value)) + if (value) { - if (value) - { - // Move TargetFramework -> TargetFrameworks - string? targetFramework = await defaultProperties.GetUnevaluatedPropertyValueAsync(TargetFrameworkProperty); + // Move TargetFramework -> TargetFrameworks + string? targetFramework = await defaultProperties.GetUnevaluatedPropertyValueAsync(TargetFrameworkProperty); - if (!Strings.IsNullOrEmpty(targetFramework)) - { - await defaultProperties.SetPropertyValueAsync(TargetFrameworksProperty, targetFramework); - await defaultProperties.DeletePropertyAsync(TargetFrameworkProperty); - } + if (!Strings.IsNullOrEmpty(targetFramework)) + { + await defaultProperties.SetPropertyValueAsync(TargetFrameworksProperty, targetFramework); + await defaultProperties.DeletePropertyAsync(TargetFrameworkProperty); } - else + } + else + { + // Move TargetFrameworks -> TargetFramework + // + // Requires TargetFrameworks to contain a valid string + string? targetFrameworks = await defaultProperties.GetUnevaluatedPropertyValueAsync(TargetFrameworksProperty); + + if (!Strings.IsNullOrEmpty(targetFrameworks)) { - // Move TargetFrameworks -> TargetFramework - // - // Requires TargetFrameworks to contain a valid string - string? targetFrameworks = await defaultProperties.GetUnevaluatedPropertyValueAsync(TargetFrameworksProperty); + string? firstTargetFramework = new LazyStringSplit(targetFrameworks, ';').FirstOrDefault(); - if (!Strings.IsNullOrEmpty(targetFrameworks)) + if (!Strings.IsNullOrEmpty(firstTargetFramework)) { - string? firstTargetFramework = new LazyStringSplit(targetFrameworks, ';').FirstOrDefault(); - - if (!Strings.IsNullOrEmpty(firstTargetFramework)) - { - await defaultProperties.SetPropertyValueAsync(TargetFrameworkProperty, firstTargetFramework); - await defaultProperties.DeletePropertyAsync(TargetFrameworksProperty); - } + await defaultProperties.SetPropertyValueAsync(TargetFrameworkProperty, firstTargetFramework); + await defaultProperties.DeletePropertyAsync(TargetFrameworksProperty); } } } - - return null; } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - return ComputeValueAsync(defaultProperties.GetEvaluatedPropertyValueAsync!); - } + return null; + } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - return ComputeValueAsync(defaultProperties.GetUnevaluatedPropertyValueAsync); - } + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return ComputeValueAsync(defaultProperties.GetEvaluatedPropertyValueAsync!); + } - private static async Task ComputeValueAsync(Func> getValue) - { - string? targetFrameworks = await getValue(TargetFrameworksProperty); + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return ComputeValueAsync(defaultProperties.GetUnevaluatedPropertyValueAsync); + } - return string.IsNullOrEmpty(targetFrameworks) ? bool.FalseString : bool.TrueString; - } + private static async Task ComputeValueAsync(Func> getValue) + { + string? targetFrameworks = await getValue(TargetFrameworksProperty); + + return string.IsNullOrEmpty(targetFrameworks) ? bool.FalseString : bool.TrueString; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DebugTypeValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DebugTypeValueProvider.cs index ccd80f3068..276c5fa074 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DebugTypeValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DebugTypeValueProvider.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("DebugType", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class DebugTypeValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("DebugType", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class DebugTypeValueProvider : InterceptingPropertyValueProviderBase + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) { - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - string value = evaluatedPropertyValue == "pdbonly" - ? "full" - : evaluatedPropertyValue; + string value = evaluatedPropertyValue == "pdbonly" + ? "full" + : evaluatedPropertyValue; - return Task.FromResult(value); - } + return Task.FromResult(value); + } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - string value = unevaluatedPropertyValue == "pdbonly" - ? "full" - : unevaluatedPropertyValue; + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + string value = unevaluatedPropertyValue == "pdbonly" + ? "full" + : unevaluatedPropertyValue; - return Task.FromResult(value); - } + return Task.FromResult(value); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DefaultPlatformPropertyValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DefaultPlatformPropertyValueProvider.cs index b79cf48bbc..e55c766c24 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DefaultPlatformPropertyValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/DefaultPlatformPropertyValueProvider.cs @@ -1,44 +1,43 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider(ConfiguredBrowseObject.PlatformTargetProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class DefaultPlatformPropertyValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider(ConfiguredBrowseObject.PlatformTargetProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class DefaultPlatformPropertyValueProvider : InterceptingPropertyValueProviderBase - { - private const string AnyCpuPlatformName = "AnyCPU"; - private const string AnyCpuDisplayName = "Any CPU"; + private const string AnyCpuPlatformName = "AnyCPU"; + private const string AnyCpuDisplayName = "Any CPU"; - private readonly ConfiguredProject _configuredProject; + private readonly ConfiguredProject _configuredProject; - [ImportingConstructor] - public DefaultPlatformPropertyValueProvider(ConfiguredProject configuredProject) - { - _configuredProject = configuredProject; - } + [ImportingConstructor] + public DefaultPlatformPropertyValueProvider(ConfiguredProject configuredProject) + { + _configuredProject = configuredProject; + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - return Task.FromResult(GetPlatformValue(evaluatedPropertyValue)); - } + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return Task.FromResult(GetPlatformValue(evaluatedPropertyValue)); + } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - return Task.FromResult(GetPlatformValue(unevaluatedPropertyValue)); - } + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return Task.FromResult(GetPlatformValue(unevaluatedPropertyValue)); + } - private string GetPlatformValue(string value) + private string GetPlatformValue(string value) + { + if (string.IsNullOrEmpty(value) && _configuredProject.ProjectConfiguration.Dimensions.TryGetValue("Platform", out string? platform)) { - if (string.IsNullOrEmpty(value) && _configuredProject.ProjectConfiguration.Dimensions.TryGetValue("Platform", out string? platform)) - { - value = platform; - } - - return value.Equals(AnyCpuPlatformName, StringComparisons.ConfigurationDimensionValues) ? AnyCpuDisplayName : value; + value = platform; } - public override Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) - { - return Task.FromResult(unevaluatedPropertyValue.Equals(AnyCpuDisplayName, StringComparisons.ConfigurationDimensionValues) ? AnyCpuPlatformName : unevaluatedPropertyValue); - } + return value.Equals(AnyCpuPlatformName, StringComparisons.ConfigurationDimensionValues) ? AnyCpuDisplayName : value; + } + + public override Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + return Task.FromResult(unevaluatedPropertyValue.Equals(AnyCpuDisplayName, StringComparisons.ConfigurationDimensionValues) ? AnyCpuPlatformName : unevaluatedPropertyValue); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/GenerateDocumentationFileValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/GenerateDocumentationFileValueProvider.cs index b2f7562f33..eabfb9b81e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/GenerateDocumentationFileValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/GenerateDocumentationFileValueProvider.cs @@ -1,44 +1,43 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("GenerateDocumentationFile", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class GenerateDocumentationFileValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("GenerateDocumentationFile", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class GenerateDocumentationFileValueProvider : InterceptingPropertyValueProviderBase - { - private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; + private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; - private const string DocumentationFileMSBuildProperty = "DocumentationFile"; - private const string PublishDocumentationFileMSBuildProperty = "PublishDocumentationFile"; - private static readonly string[] s_msBuildPropertyNames = { DocumentationFileMSBuildProperty }; - - [ImportingConstructor] - public GenerateDocumentationFileValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) - { - _temporaryPropertyStorage = temporaryPropertyStorage; - } + private const string DocumentationFileMSBuildProperty = "DocumentationFile"; + private const string PublishDocumentationFileMSBuildProperty = "PublishDocumentationFile"; + private static readonly string[] s_msBuildPropertyNames = { DocumentationFileMSBuildProperty }; + + [ImportingConstructor] + public GenerateDocumentationFileValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) + { + _temporaryPropertyStorage = temporaryPropertyStorage; + } - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); - } + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (bool.TryParse(unevaluatedPropertyValue, out bool value)) { - if (bool.TryParse(unevaluatedPropertyValue, out bool value)) + if (!value) { - if (!value) - { - await defaultProperties.SaveValueIfCurrentlySetAsync(DocumentationFileMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(DocumentationFileMSBuildProperty); - await defaultProperties.DeletePropertyAsync(PublishDocumentationFileMSBuildProperty); - } - else - { - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(DocumentationFileMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - } + await defaultProperties.SaveValueIfCurrentlySetAsync(DocumentationFileMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(DocumentationFileMSBuildProperty); + await defaultProperties.DeletePropertyAsync(PublishDocumentationFileMSBuildProperty); + } + else + { + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(DocumentationFileMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); } - - return unevaluatedPropertyValue; } + + return unevaluatedPropertyValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/RunPostBuildEventValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/RunPostBuildEventValueProvider.cs index a43bf3023e..2356c38450 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/RunPostBuildEventValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/RunPostBuildEventValueProvider.cs @@ -1,35 +1,34 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Set the default value for the RunPostBuildEvent property via interception. +/// +/// +/// If the property system is updated to support this class +/// can be removed, and the property's Persistence changed to remove interception. +/// +[ExportInterceptingPropertyValueProvider(ConfigurationGeneralBrowseObject.RunPostBuildEventProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class RunPostBuildEventValueProvider : InterceptingPropertyValueProviderBase { - /// - /// Set the default value for the RunPostBuildEvent property via interception. - /// - /// - /// If the property system is updated to support this class - /// can be removed, and the property's Persistence changed to remove interception. - /// - [ExportInterceptingPropertyValueProvider(ConfigurationGeneralBrowseObject.RunPostBuildEventProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class RunPostBuildEventValueProvider : InterceptingPropertyValueProviderBase + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) { - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + if (string.IsNullOrEmpty(evaluatedPropertyValue)) { - if (string.IsNullOrEmpty(evaluatedPropertyValue)) - { - return Task.FromResult(ConfigurationGeneralBrowseObject.RunPostBuildEventValues.OnBuildSuccess); - } - - return Task.FromResult(evaluatedPropertyValue); + return Task.FromResult(ConfigurationGeneralBrowseObject.RunPostBuildEventValues.OnBuildSuccess); } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - if (string.IsNullOrEmpty(unevaluatedPropertyValue)) - { - return Task.FromResult(ConfigurationGeneralBrowseObject.RunPostBuildEventValues.OnBuildSuccess); - } + return Task.FromResult(evaluatedPropertyValue); + } - return Task.FromResult(unevaluatedPropertyValue); + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + if (string.IsNullOrEmpty(unevaluatedPropertyValue)) + { + return Task.FromResult(ConfigurationGeneralBrowseObject.RunPostBuildEventValues.OnBuildSuccess); } + + return Task.FromResult(unevaluatedPropertyValue); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/TreatWarningsAsErrorsValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/TreatWarningsAsErrorsValueProvider.cs index d6e8f9192a..05ffe17caa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/TreatWarningsAsErrorsValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/BuildPropertyPage/TreatWarningsAsErrorsValueProvider.cs @@ -1,43 +1,42 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("TreatWarningsAsErrors", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class TreatWarningsAsErrorsValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("TreatWarningsAsErrors", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class TreatWarningsAsErrorsValueProvider : InterceptingPropertyValueProviderBase + private const string WarningsAsErrorsProperty = "WarningsAsErrors"; + private const string WarningsNotAsErrorsProperty = "WarningsNotAsErrors"; + private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; + + private static readonly string[] s_msBuildPropertyNames = { WarningsAsErrorsProperty, WarningsNotAsErrorsProperty }; + + [ImportingConstructor] + public TreatWarningsAsErrorsValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) { - private const string WarningsAsErrorsProperty = "WarningsAsErrors"; - private const string WarningsNotAsErrorsProperty = "WarningsNotAsErrors"; - private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; - - private static readonly string[] s_msBuildPropertyNames = { WarningsAsErrorsProperty, WarningsNotAsErrorsProperty }; - - [ImportingConstructor] - public TreatWarningsAsErrorsValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) - { - _temporaryPropertyStorage = temporaryPropertyStorage; - } + _temporaryPropertyStorage = temporaryPropertyStorage; + } - public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); - } + public override Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, s_msBuildPropertyNames); + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (!bool.TryParse(unevaluatedPropertyValue, out bool value)) { - if (!bool.TryParse(unevaluatedPropertyValue, out bool value)) - { - return null; - } + return null; + } - // When true, remove WarningsAsErrors. Otherwise, remove WarningsNotAsErrors. - string removePropertyName = value ? WarningsAsErrorsProperty : WarningsNotAsErrorsProperty; - string restorePropertyName = value ? WarningsNotAsErrorsProperty : WarningsAsErrorsProperty; + // When true, remove WarningsAsErrors. Otherwise, remove WarningsNotAsErrors. + string removePropertyName = value ? WarningsAsErrorsProperty : WarningsNotAsErrorsProperty; + string restorePropertyName = value ? WarningsNotAsErrorsProperty : WarningsAsErrorsProperty; - await defaultProperties.SaveValueIfCurrentlySetAsync(removePropertyName, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(removePropertyName, dimensionalConditions); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(restorePropertyName, _temporaryPropertyStorage, dimensionalConditions); + await defaultProperties.SaveValueIfCurrentlySetAsync(removePropertyName, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(removePropertyName, dimensionalConditions); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(restorePropertyName, _temporaryPropertyStorage, dimensionalConditions); - return unevaluatedPropertyValue; - } + return unevaluatedPropertyValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/DebugPagePlaceholderDescriptionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/DebugPagePlaceholderDescriptionValueProvider.cs index 30aea69687..2b010e5f5f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/DebugPagePlaceholderDescriptionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/DebugPagePlaceholderDescriptionValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("DebugPagePlaceholderDescription", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class DebugPagePlaceholderDescriptionValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("DebugPagePlaceholderDescription", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class DebugPagePlaceholderDescriptionValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/LaunchSettingsValueProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/LaunchSettingsValueProviderBase.cs index 43331f4b7a..71c8adbf8d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/LaunchSettingsValueProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/LaunchSettingsValueProviderBase.cs @@ -2,90 +2,89 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// +/// An implementation of that +/// simplifies reading and writing property values in the launch settings. +/// +/// +/// Derived types are only responsible for reading values from a provided +/// and/or writing values to a ; this type +/// handles acquiring the settings in the first place and storing them after updates +/// have been made. +/// +/// +/// Concrete implementations of this type, as with all implementations of , +/// should be tagged with the . +/// +/// +public abstract class LaunchSettingsValueProviderBase : InterceptingPropertyValueProviderBase { - /// - /// - /// An implementation of that - /// simplifies reading and writing property values in the launch settings. - /// - /// - /// Derived types are only responsible for reading values from a provided - /// and/or writing values to a ; this type - /// handles acquiring the settings in the first place and storing them after updates - /// have been made. - /// - /// - /// Concrete implementations of this type, as with all implementations of , - /// should be tagged with the . - /// - /// - public abstract class LaunchSettingsValueProviderBase : InterceptingPropertyValueProviderBase + private readonly ILaunchSettingsProvider _launchSettingsProvider; + + public LaunchSettingsValueProviderBase(ILaunchSettingsProvider launchSettingsProvider) { - private readonly ILaunchSettingsProvider _launchSettingsProvider; + _launchSettingsProvider = launchSettingsProvider; + } - public LaunchSettingsValueProviderBase(ILaunchSettingsProvider launchSettingsProvider) - { - _launchSettingsProvider = launchSettingsProvider; - } + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return GetPropertyValueAsync(propertyName); + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - return GetPropertyValueAsync(propertyName); - } + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return GetPropertyValueAsync(propertyName); + } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - return GetPropertyValueAsync(propertyName); - } + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + IWritableLaunchSettings writableLaunchSettings = launchSettings.ToWritableLaunchSettings(); + if (SetPropertyValue(propertyName, unevaluatedPropertyValue, writableLaunchSettings)) { - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - - IWritableLaunchSettings writableLaunchSettings = launchSettings.ToWritableLaunchSettings(); - if (SetPropertyValue(propertyName, unevaluatedPropertyValue, writableLaunchSettings)) - { - await _launchSettingsProvider.UpdateAndSaveSettingsAsync(writableLaunchSettings.ToLaunchSettings()); - } - - // We've intercepted the "set" operation and redirected it to the launch settings. - // Return "null" to indicate that the value should _not_ be set in the project file - // as well. - return null; + await _launchSettingsProvider.UpdateAndSaveSettingsAsync(writableLaunchSettings.ToLaunchSettings()); } - private async Task GetPropertyValueAsync(string propertyName) - { - ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - - return GetPropertyValue(propertyName, launchSettings) ?? string.Empty; - } + // We've intercepted the "set" operation and redirected it to the launch settings. + // Return "null" to indicate that the value should _not_ be set in the project file + // as well. + return null; + } - /// - /// Retrieves the property specified by from the - /// given . - /// - /// - /// The value of the property if it is found in the ; - /// otherwise a default value or if there is no applicable default. - /// - /// - /// Thrown if the given is not known (that is, it is - /// not declared in the implementor's ). - /// - public abstract string? GetPropertyValue(string propertyName, ILaunchSettings launchSettings); + private async Task GetPropertyValueAsync(string propertyName) + { + ILaunchSettings launchSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - /// - /// Sets the property specified by to - /// in the given . - /// - /// true if the were updated; - /// otherwise false. - /// - /// Thrown if the given is not known (that is, it is - /// not declared in the implementor's ). - /// - public abstract bool SetPropertyValue(string propertyName, string value, IWritableLaunchSettings launchSettings); + return GetPropertyValue(propertyName, launchSettings) ?? string.Empty; } + + /// + /// Retrieves the property specified by from the + /// given . + /// + /// + /// The value of the property if it is found in the ; + /// otherwise a default value or if there is no applicable default. + /// + /// + /// Thrown if the given is not known (that is, it is + /// not declared in the implementor's ). + /// + public abstract string? GetPropertyValue(string propertyName, ILaunchSettings launchSettings); + + /// + /// Sets the property specified by to + /// in the given . + /// + /// true if the were updated; + /// otherwise false. + /// + /// Thrown if the given is not known (that is, it is + /// not declared in the implementor's ). + /// + public abstract bool SetPropertyValue(string propertyName, string value, IWritableLaunchSettings launchSettings); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/OpenLaunchProfilesEditorValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/OpenLaunchProfilesEditorValueProvider.cs index b1df759719..ef9e460826 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/OpenLaunchProfilesEditorValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/DebugPropertyPage/OpenLaunchProfilesEditorValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("OpenLaunchProfilesEditor", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class OpenLaunchProfilesEditorValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("OpenLaunchProfilesEditor", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class OpenLaunchProfilesEditorValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ExportInterceptingPropertyValueProviderAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ExportInterceptingPropertyValueProviderAttribute.cs index 7e611ac404..5ea992eaf2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ExportInterceptingPropertyValueProviderAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ExportInterceptingPropertyValueProviderAttribute.cs @@ -1,69 +1,68 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Exports a extension to CPS. +/// +[MetadataAttribute] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] +public sealed class ExportInterceptingPropertyValueProviderAttribute : ExportAttribute { + public string[] PropertyNames { get; } + /// - /// Exports a extension to CPS. + /// Initializes a new instance of the + /// class for a single intercepted property. /// - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - public sealed class ExportInterceptingPropertyValueProviderAttribute : ExportAttribute + public ExportInterceptingPropertyValueProviderAttribute(string propertyName, ExportInterceptingPropertyValueProviderFile file) + : this(new[] { propertyName }, file) { - public string[] PropertyNames { get; } - - /// - /// Initializes a new instance of the - /// class for a single intercepted property. - /// - public ExportInterceptingPropertyValueProviderAttribute(string propertyName, ExportInterceptingPropertyValueProviderFile file) - : this(new[] { propertyName }, file) - { - } + } - /// - /// Initializes a new instance of the - /// class for multiple intercepted properties. - /// - public ExportInterceptingPropertyValueProviderAttribute(string[] propertyNames, ExportInterceptingPropertyValueProviderFile file) - : base(GetFile(file), typeof(IInterceptingPropertyValueProvider)) - { - PropertyNames = propertyNames; - } + /// + /// Initializes a new instance of the + /// class for multiple intercepted properties. + /// + public ExportInterceptingPropertyValueProviderAttribute(string[] propertyNames, ExportInterceptingPropertyValueProviderFile file) + : base(GetFile(file), typeof(IInterceptingPropertyValueProvider)) + { + PropertyNames = propertyNames; + } - private static string GetFile(ExportInterceptingPropertyValueProviderFile file) + private static string GetFile(ExportInterceptingPropertyValueProviderFile file) + { + return file switch { - return file switch - { - ExportInterceptingPropertyValueProviderFile.ProjectFile => ContractNames.ProjectPropertyProviders.ProjectFile, - ExportInterceptingPropertyValueProviderFile.UserFile => ContractNames.ProjectPropertyProviders.UserFile, - ExportInterceptingPropertyValueProviderFile.UserFileWithXamlDefaults => ContractNames.ProjectPropertyProviders.UserFileWithXamlDefaults, + ExportInterceptingPropertyValueProviderFile.ProjectFile => ContractNames.ProjectPropertyProviders.ProjectFile, + ExportInterceptingPropertyValueProviderFile.UserFile => ContractNames.ProjectPropertyProviders.UserFile, + ExportInterceptingPropertyValueProviderFile.UserFileWithXamlDefaults => ContractNames.ProjectPropertyProviders.UserFileWithXamlDefaults, - _ => string.Empty, - }; - } + _ => string.Empty, + }; } +} +/// +/// Specifies the "backing store" for an . +/// This determines where the original (non-intercepted) value is retrieved from/stored to. +/// +public enum ExportInterceptingPropertyValueProviderFile +{ /// - /// Specifies the "backing store" for an . - /// This determines where the original (non-intercepted) value is retrieved from/stored to. + /// Intercepted properties are backed by the property provider that reads/writes + /// from the project file. /// - public enum ExportInterceptingPropertyValueProviderFile - { - /// - /// Intercepted properties are backed by the property provider that reads/writes - /// from the project file. - /// - ProjectFile, - /// - /// Intercepted properties are backed by the property provider that reads/writes - /// from the user file. - /// - UserFile, - /// - /// Intercepted properties are backed by the property provider that reads/writes - /// from the user file, except that default values come from the underlying XAML - /// file instead of elsewhere in the project (e.g., an imported .props file). - /// - UserFileWithXamlDefaults - } + ProjectFile, + /// + /// Intercepted properties are backed by the property provider that reads/writes + /// from the user file. + /// + UserFile, + /// + /// Intercepted properties are backed by the property provider that reads/writes + /// from the user file, except that default values come from the underlying XAML + /// file instead of elsewhere in the project (e.g., an imported .props file). + /// + UserFileWithXamlDefaults } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider.cs index f7dffaf16d..6e31d386cb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// A project property provider that intercepts all the callbacks for a specific property name +/// on the default for validation and/or transformation of the property value. +/// +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IInterceptingPropertyValueProvider { /// - /// A project property provider that intercepts all the callbacks for a specific property name - /// on the default for validation and/or transformation of the property value. + /// Validate and/or transform the given evaluated property value. /// - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IInterceptingPropertyValueProvider - { - /// - /// Validate and/or transform the given evaluated property value. - /// - Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties); + Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties); - /// - /// Validate and/or transform the given unevaluated property value, i.e. "raw" value read from the project file. - /// - Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties); + /// + /// Validate and/or transform the given unevaluated property value, i.e. "raw" value read from the project file. + /// + Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties); - /// - /// Validate and/or transform the given unevaluated property value to be written back to the project file. - /// - Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null); - } + /// + /// Validate and/or transform the given unevaluated property value to be written back to the project file. + /// + Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider2.cs index 5721318798..9015a68ac5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProvider2.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// An extension to that allows value providers to share which project file +/// properties they are writing to. +/// +public interface IInterceptingPropertyValueProvider2 : IInterceptingPropertyValueProvider { /// - /// An extension to that allows value providers to share which project file - /// properties they are writing to. + /// Obtain the MSBuild properties to which this is writing to, given the parameters. /// - public interface IInterceptingPropertyValueProvider2 : IInterceptingPropertyValueProvider - { - /// - /// Obtain the MSBuild properties to which this is writing to, given the parameters. - /// - Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties); - } + Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata.cs index ad38e58aba..de52db8edc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Metadata mapping interface for the . +/// +public interface IInterceptingPropertyValueProviderMetadata { - /// - /// Metadata mapping interface for the . - /// - public interface IInterceptingPropertyValueProviderMetadata - { #pragma warning disable CA1819 // Properties should not return arrays - /// - /// Property names handled by the provider. - /// This must match . - /// - string[] PropertyNames { get; } + /// + /// Property names handled by the provider. + /// This must match . + /// + string[] PropertyNames { get; } #pragma warning restore CA1819 // Properties should not return arrays - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata2.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata2.cs index 349bbb6995..a5809e98c5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata2.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/IInterceptingPropertyValueProviderMetadata2.cs @@ -2,17 +2,16 @@ using System.ComponentModel; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Metadata mapping interface for the . +/// +internal interface IInterceptingPropertyValueProviderMetadata2 : IInterceptingPropertyValueProviderMetadata { /// - /// Metadata mapping interface for the . + /// Gets the expression that indicates where this export should be applied. /// - internal interface IInterceptingPropertyValueProviderMetadata2 : IInterceptingPropertyValueProviderMetadata - { - /// - /// Gets the expression that indicates where this export should be applied. - /// - [DefaultValue(null)] - string? AppliesTo { get; } - } + [DefaultValue(null)] + string? AppliesTo { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectProperties.cs index eed37acf65..cb8e52306b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectProperties.cs @@ -2,89 +2,88 @@ using Microsoft.Build.Framework.XamlTypes; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// An implementation of IProjectProperties that intercepts all the get/set callbacks for each property on +/// the given default and passes it to corresponding +/// to validate and/or transform the property value to get/set. +/// +internal sealed class InterceptedProjectProperties : DelegatedProjectPropertiesBase, IRuleAwareProjectProperties { - /// - /// An implementation of IProjectProperties that intercepts all the get/set callbacks for each property on - /// the given default and passes it to corresponding - /// to validate and/or transform the property value to get/set. - /// - internal sealed class InterceptedProjectProperties : DelegatedProjectPropertiesBase, IRuleAwareProjectProperties + private readonly UnconfiguredProject _project; + private readonly InterceptedPropertiesProviderBase _valueProvider; + + public InterceptedProjectProperties(InterceptedPropertiesProviderBase valueProvider, IProjectProperties defaultProperties, UnconfiguredProject project) + : base(defaultProperties) + { + _project = project; + _valueProvider = valueProvider; + } + + public override async Task IsValueInheritedAsync(string propertyName) { - private readonly UnconfiguredProject _project; - private readonly InterceptedPropertiesProviderBase _valueProvider; - - public InterceptedProjectProperties(InterceptedPropertiesProviderBase valueProvider, IProjectProperties defaultProperties, UnconfiguredProject project) - : base(defaultProperties) + if (!_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) || + propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is not { } valueProvider) { - _project = project; - _valueProvider = valueProvider; + return await base.IsValueInheritedAsync(propertyName); } - public override async Task IsValueInheritedAsync(string propertyName) + if (valueProvider is IInterceptingPropertyValueProvider2 valueProviderValueWithMsBuildProperties) { - if (!_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) || - propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is not { } valueProvider) - { - return await base.IsValueInheritedAsync(propertyName); - } + return await valueProviderValueWithMsBuildProperties.IsValueDefinedInContextAsync(propertyName, DelegatedProperties); + } - if (valueProvider is IInterceptingPropertyValueProvider2 valueProviderValueWithMsBuildProperties) - { - return await valueProviderValueWithMsBuildProperties.IsValueDefinedInContextAsync(propertyName, DelegatedProperties); - } + return await base.IsValueInheritedAsync(propertyName); + } + - return await base.IsValueInheritedAsync(propertyName); + public override async Task GetEvaluatedPropertyValueAsync(string propertyName) + { + string evaluatedProperty = await base.GetEvaluatedPropertyValueAsync(propertyName); + if (_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) && + propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is { } valueProvider) + { + evaluatedProperty = await valueProvider.OnGetEvaluatedPropertyValueAsync(propertyName, evaluatedProperty, DelegatedProperties); } - - public override async Task GetEvaluatedPropertyValueAsync(string propertyName) - { - string evaluatedProperty = await base.GetEvaluatedPropertyValueAsync(propertyName); - if (_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) && - propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is { } valueProvider) - { - evaluatedProperty = await valueProvider.OnGetEvaluatedPropertyValueAsync(propertyName, evaluatedProperty, DelegatedProperties); - } + return evaluatedProperty; + } - return evaluatedProperty; + public override async Task GetUnevaluatedPropertyValueAsync(string propertyName) + { + string? unevaluatedProperty = await base.GetUnevaluatedPropertyValueAsync(propertyName); + if (_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) && + propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is { } valueProvider) + { + unevaluatedProperty = await valueProvider.OnGetUnevaluatedPropertyValueAsync(propertyName, unevaluatedProperty ?? "", DelegatedProperties); } - public override async Task GetUnevaluatedPropertyValueAsync(string propertyName) - { - string? unevaluatedProperty = await base.GetUnevaluatedPropertyValueAsync(propertyName); - if (_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) && - propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is { } valueProvider) - { - unevaluatedProperty = await valueProvider.OnGetUnevaluatedPropertyValueAsync(propertyName, unevaluatedProperty ?? "", DelegatedProperties); - } + return unevaluatedProperty; + } - return unevaluatedProperty; + public override async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + { + string? valueToSet; + if (_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) && + propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is { } valueProvider) + { + valueToSet = await valueProvider.OnSetPropertyValueAsync(propertyName, unevaluatedPropertyValue, DelegatedProperties, dimensionalConditions); } - - public override async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + else { - string? valueToSet; - if (_valueProvider.TryGetInterceptingValueProvider(propertyName, out Providers? propertyValueProviders) && - propertyValueProviders.GetFilteredProvider(propertyName, _project.Capabilities.AppliesTo) is { } valueProvider) - { - valueToSet = await valueProvider.OnSetPropertyValueAsync(propertyName, unevaluatedPropertyValue, DelegatedProperties, dimensionalConditions); - } - else - { - valueToSet = unevaluatedPropertyValue; - } - - if (valueToSet is not null) - { - await base.SetPropertyValueAsync(propertyName, valueToSet, dimensionalConditions); - } + valueToSet = unevaluatedPropertyValue; } - public void SetRuleContext(Rule rule) + if (valueToSet is not null) { - var ruleAwareProperties = DelegatedProperties as IRuleAwareProjectProperties; - ruleAwareProperties?.SetRuleContext(rule); + await base.SetPropertyValueAsync(propertyName, valueToSet, dimensionalConditions); } } + + public void SetRuleContext(Rule rule) + { + var ruleAwareProperties = DelegatedProperties as IRuleAwareProjectProperties; + ruleAwareProperties?.SetRuleContext(rule); + } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectPropertiesProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectPropertiesProviderBase.cs index 24151feaf5..f89bd2439a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectPropertiesProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedProjectPropertiesProviderBase.cs @@ -1,34 +1,33 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// An intercepting project properties provider that validates and/or transforms the default +/// using the exported s. +/// +internal abstract class InterceptedProjectPropertiesProviderBase : InterceptedPropertiesProviderBase { - /// - /// An intercepting project properties provider that validates and/or transforms the default - /// using the exported s. - /// - internal abstract class InterceptedProjectPropertiesProviderBase : InterceptedPropertiesProviderBase - { - private readonly UnconfiguredProject _project; + private readonly UnconfiguredProject _project; - protected InterceptedProjectPropertiesProviderBase( - IProjectPropertiesProvider provider, - IProjectInstancePropertiesProvider instanceProvider, - UnconfiguredProject project, - IEnumerable> interceptingValueProviders) - : base(provider, instanceProvider, project, interceptingValueProviders) - { - _project = project; - } + protected InterceptedProjectPropertiesProviderBase( + IProjectPropertiesProvider provider, + IProjectInstancePropertiesProvider instanceProvider, + UnconfiguredProject project, + IEnumerable> interceptingValueProviders) + : base(provider, instanceProvider, project, interceptingValueProviders) + { + _project = project; + } - public override IProjectProperties GetProperties(string file, string? itemType, string? item) - { - IProjectProperties defaultProperties = base.GetProperties(file, itemType, item); - return InterceptProperties(defaultProperties); - } + public override IProjectProperties GetProperties(string file, string? itemType, string? item) + { + IProjectProperties defaultProperties = base.GetProperties(file, itemType, item); + return InterceptProperties(defaultProperties); + } - protected IProjectProperties InterceptProperties(IProjectProperties defaultProperties) - { - return HasInterceptingValueProvider ? new InterceptedProjectProperties(this, defaultProperties, _project) : defaultProperties; - } + protected IProjectProperties InterceptProperties(IProjectProperties defaultProperties) + { + return HasInterceptingValueProvider ? new InterceptedProjectProperties(this, defaultProperties, _project) : defaultProperties; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedPropertiesProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedPropertiesProviderBase.cs index 476a263765..d3c3cb2f1a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedPropertiesProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptedPropertiesProviderBase.cs @@ -2,86 +2,85 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// An intercepting project properties provider that validates and/or transforms the default +/// using the exported s. +/// +internal class InterceptedPropertiesProviderBase : DelegatedProjectPropertiesProviderBase { - /// - /// An intercepting project properties provider that validates and/or transforms the default - /// using the exported s. - /// - internal class InterceptedPropertiesProviderBase : DelegatedProjectPropertiesProviderBase + private readonly Dictionary _interceptingValueProviders = new(StringComparers.PropertyNames); + + protected InterceptedPropertiesProviderBase( + IProjectPropertiesProvider provider, + IProjectInstancePropertiesProvider instanceProvider, + UnconfiguredProject project, + IEnumerable> interceptingValueProviders) + : base(provider, instanceProvider, project) { - private readonly Dictionary _interceptingValueProviders = new(StringComparers.PropertyNames); + Requires.NotNullOrEmpty(interceptingValueProviders); - protected InterceptedPropertiesProviderBase( - IProjectPropertiesProvider provider, - IProjectInstancePropertiesProvider instanceProvider, - UnconfiguredProject project, - IEnumerable> interceptingValueProviders) - : base(provider, instanceProvider, project) + foreach (Lazy valueProvider in interceptingValueProviders) { - Requires.NotNullOrEmpty(interceptingValueProviders); + string[] propertyNames = valueProvider.Metadata.PropertyNames; - foreach (Lazy valueProvider in interceptingValueProviders) + foreach (string propertyName in propertyNames) { - string[] propertyNames = valueProvider.Metadata.PropertyNames; + Requires.Argument(!string.IsNullOrEmpty(propertyName), nameof(valueProvider), "A null or empty property name was found"); - foreach (string propertyName in propertyNames) + if (!_interceptingValueProviders.TryGetValue(propertyName, out Providers? entry)) { - Requires.Argument(!string.IsNullOrEmpty(propertyName), nameof(valueProvider), "A null or empty property name was found"); - - if (!_interceptingValueProviders.TryGetValue(propertyName, out Providers? entry)) - { - entry = new Providers(new List>(1) { valueProvider }); - _interceptingValueProviders.Add(propertyName, entry); - } - else - { - entry.Exports.Add(valueProvider); - } + entry = new Providers(new List>(1) { valueProvider }); + _interceptingValueProviders.Add(propertyName, entry); + } + else + { + entry.Exports.Add(valueProvider); } } } + } - protected bool HasInterceptingValueProvider => _interceptingValueProviders.Count > 0; + protected bool HasInterceptingValueProvider => _interceptingValueProviders.Count > 0; - internal bool TryGetInterceptingValueProvider(string propertyName, [NotNullWhen(returnValue: true)] out Providers? propertyValueProviders) - { - return _interceptingValueProviders.TryGetValue(propertyName, out propertyValueProviders); - } + internal bool TryGetInterceptingValueProvider(string propertyName, [NotNullWhen(returnValue: true)] out Providers? propertyValueProviders) + { + return _interceptingValueProviders.TryGetValue(propertyName, out propertyValueProviders); } +} - internal class Providers +internal class Providers +{ + public Providers(List> exports) { - public Providers(List> exports) - { - Exports = exports; - } + Exports = exports; + } - public List> Exports { get; } + public List> Exports { get; } - public IInterceptingPropertyValueProvider? GetFilteredProvider( - string propertyName, - Func appliesToEvaluator) + public IInterceptingPropertyValueProvider? GetFilteredProvider( + string propertyName, + Func appliesToEvaluator) + { + // todo consider caching this based on capability + IInterceptingPropertyValueProvider? firstProvider = null; + foreach (var lazyProvider in Exports) { - // todo consider caching this based on capability - IInterceptingPropertyValueProvider? firstProvider = null; - foreach (var lazyProvider in Exports) + string? appliesToExpression = lazyProvider.Metadata.AppliesTo; + if (appliesToExpression is null || appliesToEvaluator(appliesToExpression)) { - string? appliesToExpression = lazyProvider.Metadata.AppliesTo; - if (appliesToExpression is null || appliesToEvaluator(appliesToExpression)) + if (firstProvider is null) { - if (firstProvider is null) - { - firstProvider = lazyProvider.Value; - } - else if (lazyProvider.Value.GetType() != firstProvider.GetType()) - { - throw new ArgumentException($"Duplicate property value providers for same property name: {propertyName}"); - } + firstProvider = lazyProvider.Value; + } + else if (lazyProvider.Value.GetType() != firstProvider.GetType()) + { + throw new ArgumentException($"Duplicate property value providers for same property name: {propertyName}"); } } - - return firstProvider; } + + return firstProvider; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptingPropertyValueProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptingPropertyValueProviderBase.cs index 6b2af4f356..3f6a5eb76b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptingPropertyValueProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/InterceptingPropertyValueProviderBase.cs @@ -1,37 +1,36 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Base intercepting project property provider that intercepts all the callbacks for a specific property name +/// on the default for validation and/or transformation of the property value. +/// +public abstract class InterceptingPropertyValueProviderBase : IInterceptingPropertyValueProvider2 { - /// - /// Base intercepting project property provider that intercepts all the callbacks for a specific property name - /// on the default for validation and/or transformation of the property value. - /// - public abstract class InterceptingPropertyValueProviderBase : IInterceptingPropertyValueProvider2 + public virtual Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) { - public virtual Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - return Task.FromResult(evaluatedPropertyValue); - } + return Task.FromResult(evaluatedPropertyValue); + } - public virtual Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - return Task.FromResult(unevaluatedPropertyValue); - } + public virtual Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return Task.FromResult(unevaluatedPropertyValue); + } - public virtual Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) - { - return Task.FromResult(unevaluatedPropertyValue); - } + public virtual Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + return Task.FromResult(unevaluatedPropertyValue); + } - public virtual Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) - { - return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, new[]{ propertyName }); - } + public virtual Task IsValueDefinedInContextAsync(string propertyName, IProjectProperties defaultProperties) + { + return IsValueDefinedInContextMSBuildPropertiesAsync(defaultProperties, new[]{ propertyName }); + } - internal static async Task IsValueDefinedInContextMSBuildPropertiesAsync(IProjectProperties defaultProperties, string[] msBuildPropertyNames) - { - string[] propertiesDefinedInProjectFile = (await defaultProperties.GetDirectPropertyNamesAsync()).ToArray(); - return !msBuildPropertyNames.Any(static (name, properties) => properties.Contains(name, StringComparers.PropertyNames), propertiesDefinedInProjectFile); - } + internal static async Task IsValueDefinedInContextMSBuildPropertiesAsync(IProjectProperties defaultProperties, string[] msBuildPropertyNames) + { + string[] propertiesDefinedInProjectFile = (await defaultProperties.GetDirectPropertyNamesAsync()).ToArray(); + return !msBuildPropertyNames.Any(static (name, properties) => properties.Contains(name, StringComparers.PropertyNames), propertiesDefinedInProjectFile); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/NoOpInterceptingPropertyValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/NoOpInterceptingPropertyValueProvider.cs index a8886c587c..da3f7fe975 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/NoOpInterceptingPropertyValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/NoOpInterceptingPropertyValueProvider.cs @@ -2,23 +2,22 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal abstract class NoOpInterceptingPropertyValueProvider : InterceptingPropertyValueProviderBase { - internal abstract class NoOpInterceptingPropertyValueProvider : InterceptingPropertyValueProviderBase + public override Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) { - public override Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) - { - return TaskResult.Null(); - } + return TaskResult.Null(); + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - return TaskResult.EmptyString; - } + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return TaskResult.EmptyString; + } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - { - return TaskResult.EmptyString; - } + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return TaskResult.EmptyString; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/AssemblyVersionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/AssemblyVersionValueProvider.cs index 3a10cb3b8f..332fa1a065 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/AssemblyVersionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/AssemblyVersionValueProvider.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package -{ - [ExportInterceptingPropertyValueProvider(AssemblyVersionPropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class AssemblyVersionValueProvider : BaseVersionValueProvider - { - private static readonly Version s_defaultAssemblyVersion = new(1, 0, 0, 0); +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; - private const string AssemblyVersionPropertyName = "AssemblyVersion"; +[ExportInterceptingPropertyValueProvider(AssemblyVersionPropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class AssemblyVersionValueProvider : BaseVersionValueProvider +{ + private static readonly Version s_defaultAssemblyVersion = new(1, 0, 0, 0); - protected override string PropertyName => AssemblyVersionPropertyName; + private const string AssemblyVersionPropertyName = "AssemblyVersion"; - protected override async Task GetDefaultVersionAsync(IProjectProperties defaultProperties) - { - // Default semantic/package version just has 3 fields, we need to append an additional Revision field with value "0". - Version defaultVersion = await base.GetDefaultVersionAsync(defaultProperties); + protected override string PropertyName => AssemblyVersionPropertyName; - if (ReferenceEquals(defaultVersion, DefaultVersion)) - { - return s_defaultAssemblyVersion; - } + protected override async Task GetDefaultVersionAsync(IProjectProperties defaultProperties) + { + // Default semantic/package version just has 3 fields, we need to append an additional Revision field with value "0". + Version defaultVersion = await base.GetDefaultVersionAsync(defaultProperties); - return new Version(defaultVersion.Major, defaultVersion.Minor, Math.Max(defaultVersion.Build, 0), revision: Math.Max(defaultVersion.Revision, 0)); + if (ReferenceEquals(defaultVersion, DefaultVersion)) + { + return s_defaultAssemblyVersion; } + + return new Version(defaultVersion.Major, defaultVersion.Minor, Math.Max(defaultVersion.Build, 0), revision: Math.Max(defaultVersion.Revision, 0)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/BaseVersionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/BaseVersionValueProvider.cs index 4ac5aa6187..02f3dfb70d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/BaseVersionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/BaseVersionValueProvider.cs @@ -2,65 +2,64 @@ using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +internal abstract class BaseVersionValueProvider : InterceptingPropertyValueProviderBase { - internal abstract class BaseVersionValueProvider : InterceptingPropertyValueProviderBase - { - private const string PackageVersionMSBuildProperty = "Version"; - protected static readonly Version DefaultVersion = new(1, 0, 0); + private const string PackageVersionMSBuildProperty = "Version"; + protected static readonly Version DefaultVersion = new(1, 0, 0); - protected abstract string PropertyName { get; } + protected abstract string PropertyName { get; } - protected virtual async Task GetDefaultVersionAsync(IProjectProperties defaultProperties) + protected virtual async Task GetDefaultVersionAsync(IProjectProperties defaultProperties) + { + string versionStr = await defaultProperties.GetEvaluatedPropertyValueAsync(PackageVersionMSBuildProperty); + if (string.IsNullOrEmpty(versionStr)) { - string versionStr = await defaultProperties.GetEvaluatedPropertyValueAsync(PackageVersionMSBuildProperty); - if (string.IsNullOrEmpty(versionStr)) - { - return DefaultVersion; - } + return DefaultVersion; + } - // Ignore the semantic version suffix (e.g. "1.0.0-beta1" => "1.0.0") - versionStr = new LazyStringSplit(versionStr, '-').First(); + // Ignore the semantic version suffix (e.g. "1.0.0-beta1" => "1.0.0") + versionStr = new LazyStringSplit(versionStr, '-').First(); - return Version.TryParse(versionStr, out Version? version) ? version : DefaultVersion; - } + return Version.TryParse(versionStr, out Version? version) ? version : DefaultVersion; + } - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + if (!string.IsNullOrEmpty(evaluatedPropertyValue)) { - if (!string.IsNullOrEmpty(evaluatedPropertyValue)) - { - return evaluatedPropertyValue; - } - - // Default value is Version (major.minor.build components only) - Version version = await GetDefaultVersionAsync(defaultProperties); - return version.ToString(); + return evaluatedPropertyValue; } - public override async Task OnSetPropertyValueAsync( - string propertyName, - string unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) - { - // Don't set the new value if both of the following is true: - // 1. There is no existing property entry AND - // 2. The new value is identical to the default value. + // Default value is Version (major.minor.build components only) + Version version = await GetDefaultVersionAsync(defaultProperties); + return version.ToString(); + } - IEnumerable propertyNames = await defaultProperties.GetPropertyNamesAsync(); - if (!propertyNames.Contains(PropertyName)) + public override async Task OnSetPropertyValueAsync( + string propertyName, + string unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + // Don't set the new value if both of the following is true: + // 1. There is no existing property entry AND + // 2. The new value is identical to the default value. + + IEnumerable propertyNames = await defaultProperties.GetPropertyNamesAsync(); + if (!propertyNames.Contains(PropertyName)) + { + if (Version.TryParse(unevaluatedPropertyValue, out Version? version)) { - if (Version.TryParse(unevaluatedPropertyValue, out Version? version)) + Version defaultVersion = await GetDefaultVersionAsync(defaultProperties); + if (version.Equals(defaultVersion)) { - Version defaultVersion = await GetDefaultVersionAsync(defaultProperties); - if (version.Equals(defaultVersion)) - { - return null; - } + return null; } } - - return unevaluatedPropertyValue; } + + return unevaluatedPropertyValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/FileVersionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/FileVersionValueProvider.cs index e5a05a10b6..e24a49f13c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/FileVersionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/FileVersionValueProvider.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package -{ - [ExportInterceptingPropertyValueProvider(FileVersionPropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class FileVersionValueProvider : BaseVersionValueProvider - { - private const string FileVersionPropertyName = "FileVersion"; +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; - private static readonly Version s_defaultFileVersion = new(1, 0, 0, 0); +[ExportInterceptingPropertyValueProvider(FileVersionPropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class FileVersionValueProvider : BaseVersionValueProvider +{ + private const string FileVersionPropertyName = "FileVersion"; - protected override string PropertyName => FileVersionPropertyName; + private static readonly Version s_defaultFileVersion = new(1, 0, 0, 0); - protected override async Task GetDefaultVersionAsync(IProjectProperties defaultProperties) - { - // Default semantic/package version just has 3 fields, we need to append an additional Revision field with value "0". - Version defaultVersion = await base.GetDefaultVersionAsync(defaultProperties); + protected override string PropertyName => FileVersionPropertyName; - if (ReferenceEquals(defaultVersion, DefaultVersion)) - { - return s_defaultFileVersion; - } + protected override async Task GetDefaultVersionAsync(IProjectProperties defaultProperties) + { + // Default semantic/package version just has 3 fields, we need to append an additional Revision field with value "0". + Version defaultVersion = await base.GetDefaultVersionAsync(defaultProperties); - return new Version(defaultVersion.Major, defaultVersion.Minor, Math.Max(defaultVersion.Build, 0), revision: Math.Max(defaultVersion.Revision, 0)); + if (ReferenceEquals(defaultVersion, DefaultVersion)) + { + return s_defaultFileVersion; } + + return new Version(defaultVersion.Major, defaultVersion.Minor, Math.Max(defaultVersion.Build, 0), revision: Math.Max(defaultVersion.Revision, 0)); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/NeutralLanguageValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/NeutralLanguageValueProvider.cs index ab4e82ef18..0cb4e5146e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/NeutralLanguageValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/NeutralLanguageValueProvider.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +[ExportInterceptingPropertyValueProvider(NeutralLanguagePropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class NeutralLanguageValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider(NeutralLanguagePropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class NeutralLanguageValueProvider : InterceptingPropertyValueProviderBase - { - internal const string NeutralLanguagePropertyName = "NeutralLanguage"; - internal const string NoneValue = "(none)"; + internal const string NeutralLanguagePropertyName = "NeutralLanguage"; + internal const string NoneValue = "(none)"; - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (string.Equals(unevaluatedPropertyValue, NoneValue, StringComparison.Ordinal)) { - if (string.Equals(unevaluatedPropertyValue, NoneValue, StringComparison.Ordinal)) - { - await defaultProperties.DeletePropertyAsync(NeutralLanguagePropertyName); - return null; - } - - return unevaluatedPropertyValue; + await defaultProperties.DeletePropertyAsync(NeutralLanguagePropertyName); + return null; } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - { - if (string.IsNullOrEmpty(evaluatedPropertyValue)) - { - return Task.FromResult(NoneValue); - } + return unevaluatedPropertyValue; + } - return Task.FromResult(evaluatedPropertyValue); + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + if (string.IsNullOrEmpty(evaluatedPropertyValue)) + { + return Task.FromResult(NoneValue); } + + return Task.FromResult(evaluatedPropertyValue); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBase.cs index 756cab592a..bebe198fcf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBase.cs @@ -1,143 +1,142 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +// Sets the provided property to the path from the root of the package, and creates the +// None item associated with the file on disk. The Include metadata of the None item is a relative +// path to the file on disk from the project's directory. The None item includes 2 metadata elements +// as Pack (set to True to be included in the package) and PackagePath (directory structure in the package +// for the file to be placed). The provided property will use the directory indicated in PackagePath, +// and the filename of the file in the Include filepath. +// +// Example: +// +// content\shell32_192.png +// +// +// +// +// True +// content +// +// +internal abstract class PackageFilePropertyValueProviderBase : InterceptingPropertyValueProviderBase { - // Sets the provided property to the path from the root of the package, and creates the - // None item associated with the file on disk. The Include metadata of the None item is a relative - // path to the file on disk from the project's directory. The None item includes 2 metadata elements - // as Pack (set to True to be included in the package) and PackagePath (directory structure in the package - // for the file to be placed). The provided property will use the directory indicated in PackagePath, - // and the filename of the file in the Include filepath. - // - // Example: - // - // content\shell32_192.png - // - // - // - // - // True - // content - // - // - internal abstract class PackageFilePropertyValueProviderBase : InterceptingPropertyValueProviderBase - { - private const string PackMetadataName = "Pack"; - private const string PackagePathMetadataName = "PackagePath"; + private const string PackMetadataName = "Pack"; + private const string PackagePathMetadataName = "PackagePath"; - private readonly IProjectItemProvider _sourceItemsProvider; - private readonly UnconfiguredProject _unconfiguredProject; - private readonly string _propertyName; + private readonly IProjectItemProvider _sourceItemsProvider; + private readonly UnconfiguredProject _unconfiguredProject; + private readonly string _propertyName; - protected PackageFilePropertyValueProviderBase(string propertyName, IProjectItemProvider sourceItemsProvider, UnconfiguredProject unconfiguredProject) - { - _propertyName = propertyName; - _sourceItemsProvider = sourceItemsProvider; - _unconfiguredProject = unconfiguredProject; - } + protected PackageFilePropertyValueProviderBase(string propertyName, IProjectItemProvider sourceItemsProvider, UnconfiguredProject unconfiguredProject) + { + _propertyName = propertyName; + _sourceItemsProvider = sourceItemsProvider; + _unconfiguredProject = unconfiguredProject; + } - // https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-scenarios - private static string CreatePropertyValue(string filePath, string packagePath) + // https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-scenarios + private static string CreatePropertyValue(string filePath, string packagePath) + { + string filename = Path.GetFileName(filePath); + // Make a slash-only value into empty string, so it won't get prepended onto the path. + if (@"\".Equals(packagePath, StringComparisons.Paths) || "/".Equals(packagePath, StringComparisons.Paths)) { - string filename = Path.GetFileName(filePath); - // Make a slash-only value into empty string, so it won't get prepended onto the path. - if (@"\".Equals(packagePath, StringComparisons.Paths) || "/".Equals(packagePath, StringComparisons.Paths)) - { - packagePath = string.Empty; - } - // The assumption is that packagePath does not contain a path to a file; only a directory path. - return Path.Combine(packagePath, filename); + packagePath = string.Empty; } + // The assumption is that packagePath does not contain a path to a file; only a directory path. + return Path.Combine(packagePath, filename); + } - private async Task GetExistingNoneItemAsync(string existingPropertyValue) + private async Task GetExistingNoneItemAsync(string existingPropertyValue) + { + return await _sourceItemsProvider.GetItemAsync(None.SchemaName, async ni => { - return await _sourceItemsProvider.GetItemAsync(None.SchemaName, async ni => - { - string pack = await ni.Metadata.GetEvaluatedPropertyValueAsync(PackMetadataName); - // Instead of doing pure equality between a calculated item's property value and the existing property value, - // a user may update the PackagePath of the item and forget to update the property's value to reflect those changes, or vice versa. - // If the filename of this packed None item and the filename of the property's value match, consider those to be related to one another. - return bool.TryParse(pack, out bool packValue) && packValue && - Path.GetFileName(ni.EvaluatedInclude).Equals(Path.GetFileName(existingPropertyValue), StringComparisons.PropertyLiteralValues); - }); - } + string pack = await ni.Metadata.GetEvaluatedPropertyValueAsync(PackMetadataName); + // Instead of doing pure equality between a calculated item's property value and the existing property value, + // a user may update the PackagePath of the item and forget to update the property's value to reflect those changes, or vice versa. + // If the filename of this packed None item and the filename of the property's value match, consider those to be related to one another. + return bool.TryParse(pack, out bool packValue) && packValue && + Path.GetFileName(ni.EvaluatedInclude).Equals(Path.GetFileName(existingPropertyValue), StringComparisons.PropertyLiteralValues); + }); + } - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + bool isEmptyValue = string.IsNullOrEmpty(unevaluatedPropertyValue); + string relativePath = !isEmptyValue ? PathHelper.MakeRelative(_unconfiguredProject, unevaluatedPropertyValue) : string.Empty; + string existingPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(_propertyName); + IProjectItem? existingItem = await GetExistingNoneItemAsync(existingPropertyValue); + // This default was string.Empty but caused issues for files already part of the project via globbing in the AddAsync call. It would not add the necessary + // node, which causes the value during pack to default to "content;contentFiles" which is not the intended directory. + // See discussion here for more details: https://github.com/dotnet/project-system/issues/7642 + string packagePath = @"\"; + if (existingItem is not null) { - bool isEmptyValue = string.IsNullOrEmpty(unevaluatedPropertyValue); - string relativePath = !isEmptyValue ? PathHelper.MakeRelative(_unconfiguredProject, unevaluatedPropertyValue) : string.Empty; - string existingPropertyValue = await defaultProperties.GetEvaluatedPropertyValueAsync(_propertyName); - IProjectItem? existingItem = await GetExistingNoneItemAsync(existingPropertyValue); - // This default was string.Empty but caused issues for files already part of the project via globbing in the AddAsync call. It would not add the necessary - // node, which causes the value during pack to default to "content;contentFiles" which is not the intended directory. - // See discussion here for more details: https://github.com/dotnet/project-system/issues/7642 - string packagePath = @"\"; - if (existingItem is not null) + packagePath = await existingItem.Metadata.GetEvaluatedPropertyValueAsync(PackagePathMetadataName); + // The new filepath is the same as the current. No item changes are required. + if (relativePath.Equals(existingItem.EvaluatedInclude, StringComparisons.Paths)) { - packagePath = await existingItem.Metadata.GetEvaluatedPropertyValueAsync(PackagePathMetadataName); - // The new filepath is the same as the current. No item changes are required. - if (relativePath.Equals(existingItem.EvaluatedInclude, StringComparisons.Paths)) - { - return CreatePropertyValue(existingItem.EvaluatedInclude, packagePath); - } + return CreatePropertyValue(existingItem.EvaluatedInclude, packagePath); } + } - // None items outside of the project file cannot be updated. - if (existingItem?.PropertiesContext?.IsProjectFile ?? false) - { - if (!isEmptyValue) - { - try - { - await existingItem.SetUnevaluatedIncludeAsync(relativePath); - } - catch (ArgumentException) - { - // The user likely provided an unevaluated MSBuild property that doesn't exist which results in an empty string. - // We cannot set the Include metadata of the None item to an empty string. - // We'll return the current value since the value the user provided is not valid to update the None item. - // See: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1748809 - return existingPropertyValue; - } - } - else - { - await existingItem.RemoveAsync(); - } - } - else + // None items outside of the project file cannot be updated. + if (existingItem?.PropertiesContext?.IsProjectFile ?? false) + { + if (!isEmptyValue) { try { - await _sourceItemsProvider.AddAsync(None.SchemaName, relativePath, new Dictionary - { - { PackMetadataName, bool.TrueString }, - { PackagePathMetadataName, packagePath } - }); + await existingItem.SetUnevaluatedIncludeAsync(relativePath); } catch (ArgumentException) { // The user likely provided an unevaluated MSBuild property that doesn't exist which results in an empty string. // We cannot set the Include metadata of the None item to an empty string. - // We'll return the current value since the value the user provided is not valid to create the None item. + // We'll return the current value since the value the user provided is not valid to update the None item. // See: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1748809 return existingPropertyValue; } } - - return !isEmptyValue ? CreatePropertyValue(relativePath, packagePath) : string.Empty; + else + { + await existingItem.RemoveAsync(); + } } - - private async Task GetItemIncludeValueAsync(string propertyValue) + else { - IProjectItem? existingItem = await GetExistingNoneItemAsync(propertyValue); - return existingItem?.EvaluatedInclude ?? propertyValue; + try + { + await _sourceItemsProvider.AddAsync(None.SchemaName, relativePath, new Dictionary + { + { PackMetadataName, bool.TrueString }, + { PackagePathMetadataName, packagePath } + }); + } + catch (ArgumentException) + { + // The user likely provided an unevaluated MSBuild property that doesn't exist which results in an empty string. + // We cannot set the Include metadata of the None item to an empty string. + // We'll return the current value since the value the user provided is not valid to create the None item. + // See: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1748809 + return existingPropertyValue; + } } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) - => GetItemIncludeValueAsync(unevaluatedPropertyValue); + return !isEmptyValue ? CreatePropertyValue(relativePath, packagePath) : string.Empty; + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) - => GetItemIncludeValueAsync(evaluatedPropertyValue); + private async Task GetItemIncludeValueAsync(string propertyValue) + { + IProjectItem? existingItem = await GetExistingNoneItemAsync(propertyValue); + return existingItem?.EvaluatedInclude ?? propertyValue; } + + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + => GetItemIncludeValueAsync(unevaluatedPropertyValue); + + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + => GetItemIncludeValueAsync(evaluatedPropertyValue); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageIconValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageIconValueProvider.cs index b6b0a63237..41fe3aefb6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageIconValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageIconValueProvider.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +// Sets the PackageIcon property to the path from the root of the package, and creates the +// None item associated with the file on disk. The Include metadata of the None item is a relative +// path to the file on disk from the project's directory. The None item includes 2 metadata elements +// as Pack (set to True to be included in the package) and PackagePath (directory structure in the package +// for the file to be placed). The PackageIcon property will use the directory indicated in PackagePath, +// and the filename of the file in the Include filepath. +// +// Example: +// +// content\shell32_192.png +// +// +// +// +// True +// content +// +// +[ExportInterceptingPropertyValueProvider(PackageIconPropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class PackageIconValueProvider : PackageFilePropertyValueProviderBase { - // Sets the PackageIcon property to the path from the root of the package, and creates the - // None item associated with the file on disk. The Include metadata of the None item is a relative - // path to the file on disk from the project's directory. The None item includes 2 metadata elements - // as Pack (set to True to be included in the package) and PackagePath (directory structure in the package - // for the file to be placed). The PackageIcon property will use the directory indicated in PackagePath, - // and the filename of the file in the Include filepath. - // - // Example: - // - // content\shell32_192.png - // - // - // - // - // True - // content - // - // - [ExportInterceptingPropertyValueProvider(PackageIconPropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class PackageIconValueProvider : PackageFilePropertyValueProviderBase - { - private const string PackageIconPropertyName = "PackageIcon"; + private const string PackageIconPropertyName = "PackageIcon"; - [ImportingConstructor] - public PackageIconValueProvider( - [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, - UnconfiguredProject unconfiguredProject) - : base(PackageIconPropertyName, sourceItemsProvider, unconfiguredProject) - { - } + [ImportingConstructor] + public PackageIconValueProvider( + [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, + UnconfiguredProject unconfiguredProject) + : base(PackageIconPropertyName, sourceItemsProvider, unconfiguredProject) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseFileValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseFileValueProvider.cs index 14c0793202..cc7ed6a48c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseFileValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseFileValueProvider.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +// Sets the PackageLicenseFile property to the path from the root of the package, and creates the +// None item associated with the file on disk. The Include metadata of the None item is a relative +// path to the file on disk from the project's directory. The None item includes 2 metadata elements +// as Pack (set to True to be included in the package) and PackagePath (directory structure in the package +// for the file to be placed). The PackageLicenseFile property will use the directory indicated in PackagePath, +// and the filename of the file in the Include filepath. +// +// Example: +// +// docs\LICENSE.txt +// +// +// +// +// True +// docs +// +// +[ExportInterceptingPropertyValueProvider(PackageLicenseFilePropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class PackageLicenseFileValueProvider : PackageFilePropertyValueProviderBase { - // Sets the PackageLicenseFile property to the path from the root of the package, and creates the - // None item associated with the file on disk. The Include metadata of the None item is a relative - // path to the file on disk from the project's directory. The None item includes 2 metadata elements - // as Pack (set to True to be included in the package) and PackagePath (directory structure in the package - // for the file to be placed). The PackageLicenseFile property will use the directory indicated in PackagePath, - // and the filename of the file in the Include filepath. - // - // Example: - // - // docs\LICENSE.txt - // - // - // - // - // True - // docs - // - // - [ExportInterceptingPropertyValueProvider(PackageLicenseFilePropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class PackageLicenseFileValueProvider : PackageFilePropertyValueProviderBase - { - private const string PackageLicenseFilePropertyName = "PackageLicenseFile"; + private const string PackageLicenseFilePropertyName = "PackageLicenseFile"; - [ImportingConstructor] - public PackageLicenseFileValueProvider( - [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, - UnconfiguredProject unconfiguredProject) - : base(PackageLicenseFilePropertyName, sourceItemsProvider, unconfiguredProject) - { - } + [ImportingConstructor] + public PackageLicenseFileValueProvider( + [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, + UnconfiguredProject unconfiguredProject) + : base(PackageLicenseFilePropertyName, sourceItemsProvider, unconfiguredProject) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseKindValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseKindValueProvider.cs index 2d72b62e64..6641bb66e6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseKindValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageLicenseKindValueProvider.cs @@ -1,90 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider(PackageLicenseKindProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class PackageLicenseKindValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider(PackageLicenseKindProperty, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class PackageLicenseKindValueProvider : InterceptingPropertyValueProviderBase - { - private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; + private readonly ITemporaryPropertyStorage _temporaryPropertyStorage; + + private const string PackageLicenseKindProperty = "PackageLicenseKind"; + private const string PackageLicenseFileMSBuildProperty = "PackageLicenseFile"; + private const string PackageLicenseExpressionMSBuildProperty = "PackageLicenseExpression"; + private const string PackageRequireLicenseAcceptanceMSBuildProperty = "PackageRequireLicenseAcceptance"; + private const string ExpressionValue = "Expression"; + private const string FileValue = "File"; + private const string NoneValue = "None"; - private const string PackageLicenseKindProperty = "PackageLicenseKind"; - private const string PackageLicenseFileMSBuildProperty = "PackageLicenseFile"; - private const string PackageLicenseExpressionMSBuildProperty = "PackageLicenseExpression"; - private const string PackageRequireLicenseAcceptanceMSBuildProperty = "PackageRequireLicenseAcceptance"; - private const string ExpressionValue = "Expression"; - private const string FileValue = "File"; - private const string NoneValue = "None"; + [ImportingConstructor] + public PackageLicenseKindValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) + { + _temporaryPropertyStorage = temporaryPropertyStorage; + } - [ImportingConstructor] - public PackageLicenseKindValueProvider(ITemporaryPropertyStorage temporaryPropertyStorage) + public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + { + if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, ExpressionValue)) { - _temporaryPropertyStorage = temporaryPropertyStorage; + _temporaryPropertyStorage.AddOrUpdatePropertyValue(PackageLicenseKindProperty, ExpressionValue); + + await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseFileMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(PackageLicenseFileMSBuildProperty); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageLicenseExpressionMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageRequireLicenseAcceptanceMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); } + else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, FileValue)) + { + _temporaryPropertyStorage.AddOrUpdatePropertyValue(PackageLicenseKindProperty, FileValue); - public override async Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseExpressionMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(PackageLicenseExpressionMSBuildProperty); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageLicenseFileMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); + await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageRequireLicenseAcceptanceMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); + } + else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, NoneValue)) { - if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, ExpressionValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(PackageLicenseKindProperty, ExpressionValue); + _temporaryPropertyStorage.AddOrUpdatePropertyValue(PackageLicenseKindProperty, NoneValue); - await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseFileMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(PackageLicenseFileMSBuildProperty); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageLicenseExpressionMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageRequireLicenseAcceptanceMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - } - else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, FileValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(PackageLicenseKindProperty, FileValue); + await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseFileMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseExpressionMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.SaveValueIfCurrentlySetAsync(PackageRequireLicenseAcceptanceMSBuildProperty, _temporaryPropertyStorage); + await defaultProperties.DeletePropertyAsync(PackageLicenseFileMSBuildProperty); + await defaultProperties.DeletePropertyAsync(PackageLicenseExpressionMSBuildProperty); + await defaultProperties.DeletePropertyAsync(PackageRequireLicenseAcceptanceMSBuildProperty); + } - await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseExpressionMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(PackageLicenseExpressionMSBuildProperty); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageLicenseFileMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - await defaultProperties.RestoreValueIfNotCurrentlySetAsync(PackageRequireLicenseAcceptanceMSBuildProperty, _temporaryPropertyStorage, dimensionalConditions); - } - else if (StringComparers.PropertyLiteralValues.Equals(unevaluatedPropertyValue, NoneValue)) - { - _temporaryPropertyStorage.AddOrUpdatePropertyValue(PackageLicenseKindProperty, NoneValue); + return null; + } - await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseFileMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.SaveValueIfCurrentlySetAsync(PackageLicenseExpressionMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.SaveValueIfCurrentlySetAsync(PackageRequireLicenseAcceptanceMSBuildProperty, _temporaryPropertyStorage); - await defaultProperties.DeletePropertyAsync(PackageLicenseFileMSBuildProperty); - await defaultProperties.DeletePropertyAsync(PackageLicenseExpressionMSBuildProperty); - await defaultProperties.DeletePropertyAsync(PackageRequireLicenseAcceptanceMSBuildProperty); - } + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return ComputeValueAsync(defaultProperties.GetEvaluatedPropertyValueAsync!); + } - return null; - } + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return ComputeValueAsync(defaultProperties.GetUnevaluatedPropertyValueAsync); + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + private async Task ComputeValueAsync(Func> getValue) + { + if (!string.IsNullOrEmpty(await getValue(PackageLicenseExpressionMSBuildProperty))) { - return ComputeValueAsync(defaultProperties.GetEvaluatedPropertyValueAsync!); + return ExpressionValue; } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + if (!string.IsNullOrEmpty(await getValue(PackageLicenseFileMSBuildProperty))) { - return ComputeValueAsync(defaultProperties.GetUnevaluatedPropertyValueAsync); + return FileValue; } - private async Task ComputeValueAsync(Func> getValue) + string? storedValue = _temporaryPropertyStorage.GetPropertyValue(PackageLicenseKindProperty); + if (!Strings.IsNullOrEmpty(storedValue)) { - if (!string.IsNullOrEmpty(await getValue(PackageLicenseExpressionMSBuildProperty))) - { - return ExpressionValue; - } - - if (!string.IsNullOrEmpty(await getValue(PackageLicenseFileMSBuildProperty))) - { - return FileValue; - } - - string? storedValue = _temporaryPropertyStorage.GetPropertyValue(PackageLicenseKindProperty); - if (!Strings.IsNullOrEmpty(storedValue)) - { - return storedValue; - } - - return NoneValue; + return storedValue; } + + return NoneValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageReadmeFileValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageReadmeFileValueProvider.cs index 0bf5025f43..b0ce88bea5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageReadmeFileValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/PackageReadmeFileValueProvider.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package +namespace Microsoft.VisualStudio.ProjectSystem.Properties.Package; + +// Sets the PackageReadmeFile property to the path from the root of the package, and creates the +// None item associated with the file on disk. The Include metadata of the None item is a relative +// path to the file on disk from the project's directory. The None item includes 2 metadata elements +// as Pack (set to True to be included in the package) and PackagePath (directory structure in the package +// for the file to be placed). The PackageReadmeFile property will use the directory indicated in PackagePath, +// and the filename of the file in the Include filepath. +// +// Example: +// +// docs\readme.md +// +// +// +// +// True +// docs +// +// +[ExportInterceptingPropertyValueProvider(PackageReadmeFilePropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class PackageReadmeFileValueProvider : PackageFilePropertyValueProviderBase { - // Sets the PackageReadmeFile property to the path from the root of the package, and creates the - // None item associated with the file on disk. The Include metadata of the None item is a relative - // path to the file on disk from the project's directory. The None item includes 2 metadata elements - // as Pack (set to True to be included in the package) and PackagePath (directory structure in the package - // for the file to be placed). The PackageReadmeFile property will use the directory indicated in PackagePath, - // and the filename of the file in the Include filepath. - // - // Example: - // - // docs\readme.md - // - // - // - // - // True - // docs - // - // - [ExportInterceptingPropertyValueProvider(PackageReadmeFilePropertyName, ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class PackageReadmeFileValueProvider : PackageFilePropertyValueProviderBase - { - private const string PackageReadmeFilePropertyName = "PackageReadmeFile"; + private const string PackageReadmeFilePropertyName = "PackageReadmeFile"; - [ImportingConstructor] - public PackageReadmeFileValueProvider( - [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, - UnconfiguredProject unconfiguredProject) - : base(PackageReadmeFilePropertyName, sourceItemsProvider, unconfiguredProject) - { - } + [ImportingConstructor] + public PackageReadmeFileValueProvider( + [Import(ExportContractNames.ProjectItemProviders.SourceFiles)] IProjectItemProvider sourceItemsProvider, + UnconfiguredProject unconfiguredProject) + : base(PackageReadmeFilePropertyName, sourceItemsProvider, unconfiguredProject) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/ReadAboutSpdxExpressionsValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/ReadAboutSpdxExpressionsValueProvider.cs index 346d43a8c9..b7d6b617aa 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/ReadAboutSpdxExpressionsValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/PackagePropertyPage/ReadAboutSpdxExpressionsValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("ReadAboutSpdxExpressions", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ReadAboutSpdxExpressionsValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("ReadAboutSpdxExpressions", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ReadAboutSpdxExpressionsValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedProjectPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedProjectPropertiesProvider.cs index 6ee8e488bc..edc2e9644d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedProjectPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedProjectPropertiesProvider.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[Export("ProjectFileWithInterception", typeof(IProjectPropertiesProvider))] +[Export(typeof(IProjectPropertiesProvider))] +[Export("ProjectFileWithInterception", typeof(IProjectInstancePropertiesProvider))] +[Export(typeof(IProjectInstancePropertiesProvider))] +[ExportMetadata("Name", "ProjectFileWithInterception")] +[AppliesTo(ProjectCapability.ProjectPropertyInterception)] +internal sealed class ProjectFileInterceptedProjectPropertiesProvider : InterceptedProjectPropertiesProviderBase { - [Export("ProjectFileWithInterception", typeof(IProjectPropertiesProvider))] - [Export(typeof(IProjectPropertiesProvider))] - [Export("ProjectFileWithInterception", typeof(IProjectInstancePropertiesProvider))] - [Export(typeof(IProjectInstancePropertiesProvider))] - [ExportMetadata("Name", "ProjectFileWithInterception")] - [AppliesTo(ProjectCapability.ProjectPropertyInterception)] - internal sealed class ProjectFileInterceptedProjectPropertiesProvider : InterceptedProjectPropertiesProviderBase + [ImportingConstructor] + public ProjectFileInterceptedProjectPropertiesProvider( + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider provider, + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, + UnconfiguredProject project, + [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)]IEnumerable> interceptingValueProviders) + : base(provider, instanceProvider, project, interceptingValueProviders) { - [ImportingConstructor] - public ProjectFileInterceptedProjectPropertiesProvider( - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider provider, - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, - UnconfiguredProject project, - [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)]IEnumerable> interceptingValueProviders) - : base(provider, instanceProvider, project, interceptingValueProviders) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedViaSnapshotProjectPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedViaSnapshotProjectPropertiesProvider.cs index b8fd95ad06..30d57cfb0e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedViaSnapshotProjectPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ProjectFileInterceptedViaSnapshotProjectPropertiesProvider.cs @@ -2,37 +2,36 @@ using Microsoft.Build.Execution; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[Export("ProjectFileWithInterceptionViaSnapshot", typeof(IProjectPropertiesProvider))] +[Export(typeof(IProjectPropertiesProvider))] +[Export("ProjectFileWithInterceptionViaSnapshot", typeof(IProjectInstancePropertiesProvider))] +[Export(typeof(IProjectInstancePropertiesProvider))] +[ExportMetadata("Name", "ProjectFileWithInterceptionViaSnapshot")] +[ExportMetadata("HasEquivalentProjectInstancePropertiesProvider", true)] +[AppliesTo(ProjectCapability.ProjectPropertyInterception)] +internal sealed class ProjectFileInterceptedViaSnapshotProjectPropertiesProvider : InterceptedProjectPropertiesProviderBase { - [Export("ProjectFileWithInterceptionViaSnapshot", typeof(IProjectPropertiesProvider))] - [Export(typeof(IProjectPropertiesProvider))] - [Export("ProjectFileWithInterceptionViaSnapshot", typeof(IProjectInstancePropertiesProvider))] - [Export(typeof(IProjectInstancePropertiesProvider))] - [ExportMetadata("Name", "ProjectFileWithInterceptionViaSnapshot")] - [ExportMetadata("HasEquivalentProjectInstancePropertiesProvider", true)] - [AppliesTo(ProjectCapability.ProjectPropertyInterception)] - internal sealed class ProjectFileInterceptedViaSnapshotProjectPropertiesProvider : InterceptedProjectPropertiesProviderBase + [ImportingConstructor] + public ProjectFileInterceptedViaSnapshotProjectPropertiesProvider( + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider provider, + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, + UnconfiguredProject project, + [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)]IEnumerable> interceptingValueProviders) + : base(provider, instanceProvider, project, interceptingValueProviders) { - [ImportingConstructor] - public ProjectFileInterceptedViaSnapshotProjectPropertiesProvider( - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectPropertiesProvider provider, - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, - UnconfiguredProject project, - [ImportMany(ContractNames.ProjectPropertyProviders.ProjectFile)]IEnumerable> interceptingValueProviders) - : base(provider, instanceProvider, project, interceptingValueProviders) - { - } + } - public override IProjectProperties GetCommonProperties() - { - IProjectProperties defaultProperties = base.GetCommonProperties(); - return InterceptProperties(defaultProperties); - } + public override IProjectProperties GetCommonProperties() + { + IProjectProperties defaultProperties = base.GetCommonProperties(); + return InterceptProperties(defaultProperties); + } - public override IProjectProperties GetCommonProperties(ProjectInstance projectInstance) - { - IProjectProperties defaultProperties = base.GetCommonProperties(projectInstance); - return InterceptProperties(defaultProperties); - } + public override IProjectProperties GetCommonProperties(ProjectInstance projectInstance) + { + IProjectProperties defaultProperties = base.GetCommonProperties(projectInstance); + return InterceptProperties(defaultProperties); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ReferencesPage/ImplicitUsingsEnabledValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ReferencesPage/ImplicitUsingsEnabledValueProvider.cs index 5796c5df42..f55f139446 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ReferencesPage/ImplicitUsingsEnabledValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ReferencesPage/ImplicitUsingsEnabledValueProvider.cs @@ -2,62 +2,61 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("ImplicitUsings", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ImplicitUsingsEnabledValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("ImplicitUsings", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ImplicitUsingsEnabledValueProvider : InterceptingPropertyValueProviderBase + private static readonly Task s_enableStringTaskResult = Task.FromResult("enable"); + private static readonly Task s_disableStringTaskResult = Task.FromResult("disable"); + + public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + return ToBooleanStringAsync(evaluatedPropertyValue); + } + + public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + { + return ToBooleanStringAsync(unevaluatedPropertyValue); + } + + public override Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) { - private static readonly Task s_enableStringTaskResult = Task.FromResult("enable"); - private static readonly Task s_disableStringTaskResult = Task.FromResult("disable"); + return FromBooleanStringAsync(unevaluatedPropertyValue); + } - public override Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + private static Task ToBooleanStringAsync(string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "enable")) { - return ToBooleanStringAsync(evaluatedPropertyValue); + return TaskResult.TrueString; } - public override Task OnGetUnevaluatedPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties) + if (StringComparer.OrdinalIgnoreCase.Equals(value, "disable")) { - return ToBooleanStringAsync(unevaluatedPropertyValue); + return TaskResult.FalseString; } - public override Task OnSetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IProjectProperties defaultProperties, IReadOnlyDictionary? dimensionalConditions = null) + return Task.FromResult(value); + } + + private static Task FromBooleanStringAsync(string? value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, bool.TrueString)) { - return FromBooleanStringAsync(unevaluatedPropertyValue); + return s_enableStringTaskResult; } - private static Task ToBooleanStringAsync(string value) + if (StringComparer.OrdinalIgnoreCase.Equals(value, bool.FalseString)) { - if (StringComparer.OrdinalIgnoreCase.Equals(value, "enable")) - { - return TaskResult.TrueString; - } - - if (StringComparer.OrdinalIgnoreCase.Equals(value, "disable")) - { - return TaskResult.FalseString; - } - - return Task.FromResult(value); + return s_disableStringTaskResult; } - private static Task FromBooleanStringAsync(string? value) + if (value is null) { - if (StringComparer.OrdinalIgnoreCase.Equals(value, bool.TrueString)) - { - return s_enableStringTaskResult; - } - - if (StringComparer.OrdinalIgnoreCase.Equals(value, bool.FalseString)) - { - return s_disableStringTaskResult; - } - - if (value is null) - { - return TaskResult.Null(); - } - - return Task.FromResult(value); + return TaskResult.Null(); } + + return Task.FromResult(value); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/OpenLaunchProfilesEditorValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/OpenLaunchProfilesEditorValueProvider.cs index 0afc6bd368..068489418b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/OpenLaunchProfilesEditorValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/OpenLaunchProfilesEditorValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("CreateOrOpenAssemblyResources", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class CreateOrOpenAssemblyResourcesValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("CreateOrOpenAssemblyResources", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class CreateOrOpenAssemblyResourcesValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/ResourcesPagePlaceholderDescriptionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/ResourcesPagePlaceholderDescriptionValueProvider.cs index daabaebaff..fea42832b2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/ResourcesPagePlaceholderDescriptionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/ResourcesPropertyPage/ResourcesPagePlaceholderDescriptionValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("ResourcesPagePlaceholderDescription", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class ResourcesPagePlaceholderDescriptionValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("ResourcesPagePlaceholderDescription", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class ResourcesPagePlaceholderDescriptionValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/OpenLaunchProfilesEditorValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/OpenLaunchProfilesEditorValueProvider.cs index 9392a6a2fb..3077b43801 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/OpenLaunchProfilesEditorValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/OpenLaunchProfilesEditorValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("CreateOrOpenApplicationSettings", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class CreateOrOpenApplicationSettingsValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("CreateOrOpenApplicationSettings", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class CreateOrOpenApplicationSettingsValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/SettingsPagePlaceholderDescriptionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/SettingsPagePlaceholderDescriptionValueProvider.cs index e0dc0a587a..c333fac9b2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/SettingsPagePlaceholderDescriptionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SettingsPropertyPage/SettingsPagePlaceholderDescriptionValueProvider.cs @@ -1,9 +1,8 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("SettingsPagePlaceholderDescription", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class SettingsPagePlaceholderDescriptionValueProvider : NoOpInterceptingPropertyValueProvider { - [ExportInterceptingPropertyValueProvider("SettingsPagePlaceholderDescription", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class SettingsPagePlaceholderDescriptionValueProvider : NoOpInterceptingPropertyValueProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SigningPropertyPage/AssemblyOriginatorKeyFileValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SigningPropertyPage/AssemblyOriginatorKeyFileValueProvider.cs index 5d9af87db6..4bd121d5ef 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SigningPropertyPage/AssemblyOriginatorKeyFileValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/SigningPropertyPage/AssemblyOriginatorKeyFileValueProvider.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("AssemblyOriginatorKeyFile", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class AssemblyOriginatorKeyFileValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("AssemblyOriginatorKeyFile", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class AssemblyOriginatorKeyFileValueProvider : InterceptingPropertyValueProviderBase + private readonly UnconfiguredProject _unconfiguredProject; + + [ImportingConstructor] + public AssemblyOriginatorKeyFileValueProvider(UnconfiguredProject project) { - private readonly UnconfiguredProject _unconfiguredProject; + _unconfiguredProject = project; + } - [ImportingConstructor] - public AssemblyOriginatorKeyFileValueProvider(UnconfiguredProject project) + public override Task OnSetPropertyValueAsync( + string propertyName, + string unevaluatedPropertyValue, + IProjectProperties defaultProperties, + IReadOnlyDictionary? dimensionalConditions = null) + { + if (Path.IsPathRooted(unevaluatedPropertyValue) && + _unconfiguredProject.TryMakeRelativeToProjectDirectory(unevaluatedPropertyValue, out string? relativePath)) { - _unconfiguredProject = project; + unevaluatedPropertyValue = relativePath; } - public override Task OnSetPropertyValueAsync( - string propertyName, - string unevaluatedPropertyValue, - IProjectProperties defaultProperties, - IReadOnlyDictionary? dimensionalConditions = null) - { - if (Path.IsPathRooted(unevaluatedPropertyValue) && - _unconfiguredProject.TryMakeRelativeToProjectDirectory(unevaluatedPropertyValue, out string? relativePath)) - { - unevaluatedPropertyValue = relativePath; - } - - return Task.FromResult(unevaluatedPropertyValue); - } + return Task.FromResult(unevaluatedPropertyValue); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/TargetFrameworkValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/TargetFrameworkValueProvider.cs index 4bfb696aca..d90c240c87 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/TargetFrameworkValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/TargetFrameworkValueProvider.cs @@ -2,37 +2,36 @@ using System.Runtime.Versioning; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportInterceptingPropertyValueProvider("TargetFramework", ExportInterceptingPropertyValueProviderFile.ProjectFile)] +internal sealed class TargetFrameworkValueProvider : InterceptingPropertyValueProviderBase { - [ExportInterceptingPropertyValueProvider("TargetFramework", ExportInterceptingPropertyValueProviderFile.ProjectFile)] - internal sealed class TargetFrameworkValueProvider : InterceptingPropertyValueProviderBase + private readonly ProjectProperties _properties; + + [ImportingConstructor] + public TargetFrameworkValueProvider(ProjectProperties properties) { - private readonly ProjectProperties _properties; + _properties = properties; + } - [ImportingConstructor] - public TargetFrameworkValueProvider(ProjectProperties properties) - { - _properties = properties; - } + public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + { + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + string? targetFrameworkMoniker = (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(); - public override async Task OnGetEvaluatedPropertyValueAsync(string propertyName, string evaluatedPropertyValue, IProjectProperties defaultProperties) + if (targetFrameworkMoniker is not null) { - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - string? targetFrameworkMoniker = (string?)await configuration.TargetFrameworkMoniker.GetValueAsync(); - - if (targetFrameworkMoniker is not null) - { - var targetFramework = new FrameworkName(targetFrameworkMoniker); - - // define MAKETARGETFRAMEWORKVERSION(maj, min, rev) (TARGETFRAMEWORKVERSION)((maj) << 16 | (rev) << 8 | (min)) - int maj = targetFramework.Version.Major; - int min = targetFramework.Version.Minor; - int rev = targetFramework.Version.Revision >= 0 ? targetFramework.Version.Revision : 0; - uint propertyValue = unchecked((uint)((maj << 16) | (rev << 8) | min)); - return propertyValue.ToString(); - } - - return evaluatedPropertyValue; + var targetFramework = new FrameworkName(targetFrameworkMoniker); + + // define MAKETARGETFRAMEWORKVERSION(maj, min, rev) (TARGETFRAMEWORKVERSION)((maj) << 16 | (rev) << 8 | (min)) + int maj = targetFramework.Version.Major; + int min = targetFramework.Version.Minor; + int rev = targetFramework.Version.Revision >= 0 ? targetFramework.Version.Revision : 0; + uint propertyValue = unchecked((uint)((maj << 16) | (rev << 8) | min)); + return propertyValue.ToString(); } + + return evaluatedPropertyValue; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileInterceptedProjectPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileInterceptedProjectPropertiesProvider.cs index 4b70d5ade2..f7209624b4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileInterceptedProjectPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileInterceptedProjectPropertiesProvider.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[Export("UserFileWithInterception", typeof(IProjectPropertiesProvider))] +[Export(typeof(IProjectPropertiesProvider))] +[Export("UserFileWithInterception", typeof(IProjectInstancePropertiesProvider))] +[Export(typeof(IProjectInstancePropertiesProvider))] +[ExportMetadata("Name", "UserFileWithInterception")] +[AppliesTo(ProjectCapability.ProjectPropertyInterception)] +internal class UserFileInterceptedProjectPropertiesProvider : InterceptedProjectPropertiesProviderBase { - [Export("UserFileWithInterception", typeof(IProjectPropertiesProvider))] - [Export(typeof(IProjectPropertiesProvider))] - [Export("UserFileWithInterception", typeof(IProjectInstancePropertiesProvider))] - [Export(typeof(IProjectInstancePropertiesProvider))] - [ExportMetadata("Name", "UserFileWithInterception")] - [AppliesTo(ProjectCapability.ProjectPropertyInterception)] - internal class UserFileInterceptedProjectPropertiesProvider : InterceptedProjectPropertiesProviderBase - { - private const string UserSuffix = ".user"; + private const string UserSuffix = ".user"; - public override string DefaultProjectPath - { - get { return base.DefaultProjectPath + UserSuffix; } - } + public override string DefaultProjectPath + { + get { return base.DefaultProjectPath + UserSuffix; } + } - [ImportingConstructor] - public UserFileInterceptedProjectPropertiesProvider( - [Import(ContractNames.ProjectPropertyProviders.UserFile)] IProjectPropertiesProvider provider, - // We use project file here because in CPS, the UserFile instance provider is implemented by the same - // provider as the ProjectFile, and is exported as the ProjectFile provider. - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, - UnconfiguredProject project, - [ImportMany(ContractNames.ProjectPropertyProviders.UserFile)]IEnumerable> interceptingValueProviders) - : base(provider, instanceProvider, project, interceptingValueProviders) - { - } + [ImportingConstructor] + public UserFileInterceptedProjectPropertiesProvider( + [Import(ContractNames.ProjectPropertyProviders.UserFile)] IProjectPropertiesProvider provider, + // We use project file here because in CPS, the UserFile instance provider is implemented by the same + // provider as the ProjectFile, and is exported as the ProjectFile provider. + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, + UnconfiguredProject project, + [ImportMany(ContractNames.ProjectPropertyProviders.UserFile)]IEnumerable> interceptingValueProviders) + : base(provider, instanceProvider, project, interceptingValueProviders) + { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider.cs index 470730f14a..5fd00d7fd2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/InterceptedProjectProperties/UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider.cs @@ -1,25 +1,24 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[Export("UserFileWithXamlDefaultsWithInterception", typeof(IProjectPropertiesProvider))] +[Export(typeof(IProjectPropertiesProvider))] +[Export("UserFileWithXamlDefaultsWithInterception", typeof(IProjectInstancePropertiesProvider))] +[Export(typeof(IProjectInstancePropertiesProvider))] +[ExportMetadata("Name", "UserFileWithXamlDefaultsWithInterception")] +[AppliesTo(ProjectCapability.ProjectPropertyInterception)] +internal sealed class UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider : UserFileInterceptedProjectPropertiesProvider { - [Export("UserFileWithXamlDefaultsWithInterception", typeof(IProjectPropertiesProvider))] - [Export(typeof(IProjectPropertiesProvider))] - [Export("UserFileWithXamlDefaultsWithInterception", typeof(IProjectInstancePropertiesProvider))] - [Export(typeof(IProjectInstancePropertiesProvider))] - [ExportMetadata("Name", "UserFileWithXamlDefaultsWithInterception")] - [AppliesTo(ProjectCapability.ProjectPropertyInterception)] - internal sealed class UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider : UserFileInterceptedProjectPropertiesProvider + [ImportingConstructor] + public UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider( + [Import(ContractNames.ProjectPropertyProviders.UserFileWithXamlDefaults)] IProjectPropertiesProvider provider, + // We use project file here because in CPS, the UserFileWithXamlDefaults instance provider is implemented by the same + // provider as the ProjectFile, and is exported as the ProjectFile provider. + [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, + UnconfiguredProject project, + [ImportMany(ContractNames.ProjectPropertyProviders.UserFileWithXamlDefaults)]IEnumerable> interceptingValueProviders) + : base(provider, instanceProvider, project, interceptingValueProviders) { - [ImportingConstructor] - public UserFileWithXamlDefaultsInterceptedProjectPropertiesProvider( - [Import(ContractNames.ProjectPropertyProviders.UserFileWithXamlDefaults)] IProjectPropertiesProvider provider, - // We use project file here because in CPS, the UserFileWithXamlDefaults instance provider is implemented by the same - // provider as the ProjectFile, and is exported as the ProjectFile provider. - [Import(ContractNames.ProjectPropertyProviders.ProjectFile)] IProjectInstancePropertiesProvider instanceProvider, - UnconfiguredProject project, - [ImportMany(ContractNames.ProjectPropertyProviders.UserFileWithXamlDefaults)]IEnumerable> interceptingValueProviders) - : base(provider, instanceProvider, project, interceptingValueProviders) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderAttribute.cs index 67192bfe2e..cfeb7ae345 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderAttribute.cs @@ -1,44 +1,43 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Exports a or . +/// +[MetadataAttribute] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] +public sealed class ExportLaunchProfileExtensionValueProviderAttribute : ExportAttribute { + public string[] PropertyNames { get; } + /// - /// Exports a or . + /// Initializes a new instance of the + /// class for a single intercepted property. /// - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - public sealed class ExportLaunchProfileExtensionValueProviderAttribute : ExportAttribute + public ExportLaunchProfileExtensionValueProviderAttribute(string propertyName, ExportLaunchProfileExtensionValueProviderScope scope) + : this(new[] { propertyName }, scope) { - public string[] PropertyNames { get; } - - /// - /// Initializes a new instance of the - /// class for a single intercepted property. - /// - public ExportLaunchProfileExtensionValueProviderAttribute(string propertyName, ExportLaunchProfileExtensionValueProviderScope scope) - : this(new[] { propertyName }, scope) - { - } + } - /// - /// Initializes a new instance of the - /// class for multiple intercepted properties. - /// - public ExportLaunchProfileExtensionValueProviderAttribute(string[] propertyNames, ExportLaunchProfileExtensionValueProviderScope scope) - : base(GetType(scope)) - { - PropertyNames = propertyNames; - } + /// + /// Initializes a new instance of the + /// class for multiple intercepted properties. + /// + public ExportLaunchProfileExtensionValueProviderAttribute(string[] propertyNames, ExportLaunchProfileExtensionValueProviderScope scope) + : base(GetType(scope)) + { + PropertyNames = propertyNames; + } - private static Type? GetType(ExportLaunchProfileExtensionValueProviderScope scope) + private static Type? GetType(ExportLaunchProfileExtensionValueProviderScope scope) + { + return scope switch { - return scope switch - { - ExportLaunchProfileExtensionValueProviderScope.LaunchProfile => typeof(ILaunchProfileExtensionValueProvider), - ExportLaunchProfileExtensionValueProviderScope.GlobalSettings => typeof(IGlobalSettingExtensionValueProvider), + ExportLaunchProfileExtensionValueProviderScope.LaunchProfile => typeof(ILaunchProfileExtensionValueProvider), + ExportLaunchProfileExtensionValueProviderScope.GlobalSettings => typeof(IGlobalSettingExtensionValueProvider), - _ => null - }; - } + _ => null + }; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderScope.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderScope.cs index b991b4c923..f0093aa6fe 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderScope.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ExportLaunchProfileExtensionValueProviderScope.cs @@ -2,24 +2,23 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Specifies the "backing store" for an +/// or . This determines where the +/// property value is read from/stored to. +/// +public enum ExportLaunchProfileExtensionValueProviderScope { /// - /// Specifies the "backing store" for an - /// or . This determines where the - /// property value is read from/stored to. + /// Extension properties are backed by an . The related + /// type must implement . + /// + LaunchProfile, + /// + /// Extensions properties are backed by . + /// The related type must implement . /// - public enum ExportLaunchProfileExtensionValueProviderScope - { - /// - /// Extension properties are backed by an . The related - /// type must implement . - /// - LaunchProfile, - /// - /// Extensions properties are backed by . - /// The related type must implement . - /// - GlobalSettings - } + GlobalSettings } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/IGlobalSettingExtensionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/IGlobalSettingExtensionValueProvider.cs index 898a23866a..eb1e28b77a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/IGlobalSettingExtensionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/IGlobalSettingExtensionValueProvider.cs @@ -3,83 +3,82 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// +/// A property provider that extends to +/// support reading and writing arbitrary properties. +/// +/// +/// This is necessary to convert back and forth between the +/// representation of the property value used by the properties system and the +/// representation stored in the global settings. However, it can also be used for +/// validation and transformation of the property value, or to update other global +/// settings in response. +/// +/// +/// Implementations of this must be tagged with the +/// using the +/// scope. +/// +/// +/// +/// See also for the equivalent +/// interface for global launch settings, and +/// for a similar interface for intercepting callbacks for properties stored in +/// MSBuild files. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IGlobalSettingExtensionValueProvider { /// - /// - /// A property provider that extends to - /// support reading and writing arbitrary properties. - /// - /// - /// This is necessary to convert back and forth between the - /// representation of the property value used by the properties system and the - /// representation stored in the global settings. However, it can also be used for - /// validation and transformation of the property value, or to update other global - /// settings in response. - /// - /// - /// Implementations of this must be tagged with the - /// using the - /// scope. - /// + /// Reads the given property from the , converting + /// it to a . /// + /// + /// The name of the property, as known to the CPS properties system. + /// + /// + /// The current set of global launch settings. + /// + /// + /// An optional associated with the + /// calling this provider. + /// + /// + /// The value of the given property, as a string. + /// /// - /// See also for the equivalent - /// interface for global launch settings, and - /// for a similar interface for intercepting callbacks for properties stored in - /// MSBuild files. + /// The provides access to metadata that may influence the + /// conversion of the property to a . /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IGlobalSettingExtensionValueProvider - { - /// - /// Reads the given property from the , converting - /// it to a . - /// - /// - /// The name of the property, as known to the CPS properties system. - /// - /// - /// The current set of global launch settings. - /// - /// - /// An optional associated with the - /// calling this provider. - /// - /// - /// The value of the given property, as a string. - /// - /// - /// The provides access to metadata that may influence the - /// conversion of the property to a . - /// - string OnGetPropertyValue(string propertyName, ImmutableDictionary globalSettings, Rule? rule); + string OnGetPropertyValue(string propertyName, ImmutableDictionary globalSettings, Rule? rule); - /// - /// Converts the from a and - /// produces a set of changes to apply to the . - /// - /// - /// The name of the property, as known to the CPS properties system. - /// - /// - /// The new value of the property. - /// - /// - /// The current set of global launch settings. - /// - /// - /// An optional associated with the - /// calling this provider. - /// - /// A set of changes to apply to the global settings. If the value for a given key - /// is the corresponding global setting will be removed, - /// otherwise the setting will be updated. - /// - /// - /// The provides access to metadata that may influence the - /// conversion of the property from a . - /// - ImmutableDictionary OnSetPropertyValue(string propertyName, string propertyValue, ImmutableDictionary globalSettings, Rule? rule); - } + /// + /// Converts the from a and + /// produces a set of changes to apply to the . + /// + /// + /// The name of the property, as known to the CPS properties system. + /// + /// + /// The new value of the property. + /// + /// + /// The current set of global launch settings. + /// + /// + /// An optional associated with the + /// calling this provider. + /// + /// A set of changes to apply to the global settings. If the value for a given key + /// is the corresponding global setting will be removed, + /// otherwise the setting will be updated. + /// + /// + /// The provides access to metadata that may influence the + /// conversion of the property from a . + /// + ImmutableDictionary OnSetPropertyValue(string propertyName, string propertyValue, ImmutableDictionary globalSettings, Rule? rule); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProvider.cs index 313749e958..a0766fb052 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProvider.cs @@ -3,85 +3,84 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// +/// A property provider that extends a given to support +/// reading and writing arbitrary properties. +/// +/// +/// This is necessary to convert back and forth between the +/// representation of the property value used by the properties system and the +/// representation stored in the launch profile. However, it can also be used for +/// validation and transformation of the property value, or to update other launch +/// profile properties in response. +/// +/// +/// Implementations of this must be tagged with the +/// using the +/// scope. +/// +/// +/// +/// See also for the equivalent +/// interface for global launch settings, and +/// for a similar interface for intercepting callbacks for properties stored in +/// MSBuild files. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] +public interface ILaunchProfileExtensionValueProvider { /// - /// - /// A property provider that extends a given to support - /// reading and writing arbitrary properties. - /// - /// - /// This is necessary to convert back and forth between the - /// representation of the property value used by the properties system and the - /// representation stored in the launch profile. However, it can also be used for - /// validation and transformation of the property value, or to update other launch - /// profile properties in response. - /// - /// - /// Implementations of this must be tagged with the - /// using the - /// scope. - /// + /// Reads the given property from the , converting it + /// to a . /// + /// + /// The name of the property, as known to the CPS properties system. + /// + /// + /// The launch profile from which to read the property value. + /// + /// + /// The current set of global launch settings. + /// + /// + /// An optional associated with the + /// calling this provider. + /// + /// + /// The value of the given property, as a string. + /// /// - /// See also for the equivalent - /// interface for global launch settings, and - /// for a similar interface for intercepting callbacks for properties stored in - /// MSBuild files. + /// The provides access to metadata that may influence the + /// conversion of the property to a . /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Extension, Cardinality = ImportCardinality.ZeroOrMore)] - public interface ILaunchProfileExtensionValueProvider - { - /// - /// Reads the given property from the , converting it - /// to a . - /// - /// - /// The name of the property, as known to the CPS properties system. - /// - /// - /// The launch profile from which to read the property value. - /// - /// - /// The current set of global launch settings. - /// - /// - /// An optional associated with the - /// calling this provider. - /// - /// - /// The value of the given property, as a string. - /// - /// - /// The provides access to metadata that may influence the - /// conversion of the property to a . - /// - string OnGetPropertyValue(string propertyName, ILaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule); + string OnGetPropertyValue(string propertyName, ILaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule); - /// - /// Converts the from a and - /// updates the as appropriate. - /// - /// - /// The name of the property, as known to the CPS properties system. - /// - /// - /// The new value of the property. - /// - /// - /// The launch profile to update. - /// - /// - /// The current set of global launch settings. - /// - /// - /// An optional associated with the - /// calling this provider. - /// - /// - /// The provides access to metadata that may influence the - /// conversion of the property from a . - /// - void OnSetPropertyValue(string propertyName, string propertyValue, IWritableLaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule); - } + /// + /// Converts the from a and + /// updates the as appropriate. + /// + /// + /// The name of the property, as known to the CPS properties system. + /// + /// + /// The new value of the property. + /// + /// + /// The launch profile to update. + /// + /// + /// The current set of global launch settings. + /// + /// + /// An optional associated with the + /// calling this provider. + /// + /// + /// The provides access to metadata that may influence the + /// conversion of the property from a . + /// + void OnSetPropertyValue(string propertyName, string propertyValue, IWritableLaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProviderMetadata.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProviderMetadata.cs index 2c7d1870bf..41307ffb89 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProviderMetadata.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ILaunchProfileExtensionValueProviderMetadata.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Metadata mapping interface for the . +/// +public interface ILaunchProfileExtensionValueProviderMetadata { - /// - /// Metadata mapping interface for the . - /// - public interface ILaunchProfileExtensionValueProviderMetadata - { #pragma warning disable CA1819 // Properties should not return arrays - /// - /// Property names handled by the provider. - /// This must match . - /// - string[] PropertyNames { get; } + /// + /// Property names handled by the provider. + /// This must match . + /// + string[] PropertyNames { get; } #pragma warning restore CA1819 // Properties should not return arrays - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectItemProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectItemProvider.cs index 88f9145adf..cdf250d243 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectItemProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectItemProvider.cs @@ -3,347 +3,346 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Exposes launch profiles (from launchSettings.json files) as CPS s, +/// where each launch profile becomes its own . +/// +/// +/// +/// This type mostly delegates to the , and serves to translate +/// its abilities to the model. +/// +/// +/// support, like many things in CPS, is designed with MSBuild concepts +/// in mind (e.g., evaluated vs. unevaluated data, items/properties possibly being defined in +/// imported files rather than the project itself, etc.) and many of these do not translate +/// directly to launch profiles. Additional details can be found in the remarks on the individual +/// type members. +/// +/// +[Export(typeof(IProjectItemProvider))] +[ExportMetadata("Name", "LaunchProfile")] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal class LaunchProfileProjectItemProvider : IProjectItemProvider { - /// - /// Exposes launch profiles (from launchSettings.json files) as CPS s, - /// where each launch profile becomes its own . - /// /// /// - /// This type mostly delegates to the , and serves to translate - /// its abilities to the model. + /// Launch profiles will be exposed with this item type. /// /// - /// support, like many things in CPS, is designed with MSBuild concepts - /// in mind (e.g., evaluated vs. unevaluated data, items/properties possibly being defined in - /// imported files rather than the project itself, etc.) and many of these do not translate - /// directly to launch profiles. Additional details can be found in the remarks on the individual - /// type members. + /// As an alternative, we could use the debug command associated with + /// a launch profile as the basis for the item type. That is, rather than + /// all launch profiles having the same item type, all launch profiles + /// with the same debug command have the same item type. In that design, + /// the set of item types returned by + /// would be based on the set of debug command handlers. This approach + /// might make it easier to bind an item presenting a launch profile to + /// related Rules, but at this time it it not clear that it is necessary. /// /// - [Export(typeof(IProjectItemProvider))] - [ExportMetadata("Name", "LaunchProfile")] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal class LaunchProfileProjectItemProvider : IProjectItemProvider - { - /// - /// - /// Launch profiles will be exposed with this item type. - /// - /// - /// As an alternative, we could use the debug command associated with - /// a launch profile as the basis for the item type. That is, rather than - /// all launch profiles having the same item type, all launch profiles - /// with the same debug command have the same item type. In that design, - /// the set of item types returned by - /// would be based on the set of debug command handlers. This approach - /// might make it easier to bind an item presenting a launch profile to - /// related Rules, but at this time it it not clear that it is necessary. - /// - /// - public static string ItemType = "LaunchProfile"; + public static string ItemType = "LaunchProfile"; - private static readonly ImmutableSortedSet s_itemTypes = ImmutableSortedSet.Create(StringComparers.ItemTypes, ItemType); - private readonly UnconfiguredProject _project; - private readonly ILaunchSettingsProvider _launchSettingsProvider; + private static readonly ImmutableSortedSet s_itemTypes = ImmutableSortedSet.Create(StringComparers.ItemTypes, ItemType); + private readonly UnconfiguredProject _project; + private readonly ILaunchSettingsProvider _launchSettingsProvider; #pragma warning disable CS0067 // Unused event - /// - /// This would fire when an item is about to be renamed, but this provider does not - /// support renaming a launch profile. As such, this event will never be invoked. - /// - public event AsyncEventHandler? ItemIdentityChanging; + /// + /// This would fire when an item is about to be renamed, but this provider does not + /// support renaming a launch profile. As such, this event will never be invoked. + /// + public event AsyncEventHandler? ItemIdentityChanging; - /// - /// This would fire after an item is has been renamed, but this provider does not - /// support renaming a launch profile. As such, this event will never be invoked. - /// - public event AsyncEventHandler? ItemIdentityChangedOnWriter; + /// + /// This would fire after an item is has been renamed, but this provider does not + /// support renaming a launch profile. As such, this event will never be invoked. + /// + public event AsyncEventHandler? ItemIdentityChangedOnWriter; - /// - /// This would fire after an item is has been renamed, but this provider does not - /// support renaming a launch profile. As such, this event will never be invoked. - /// - public event AsyncEventHandler? ItemIdentityChanged; + /// + /// This would fire after an item is has been renamed, but this provider does not + /// support renaming a launch profile. As such, this event will never be invoked. + /// + public event AsyncEventHandler? ItemIdentityChanged; #pragma warning restore CS0067 - [ImportingConstructor] - public LaunchProfileProjectItemProvider(UnconfiguredProject project, ILaunchSettingsProvider launchSettingsProvider) + [ImportingConstructor] + public LaunchProfileProjectItemProvider(UnconfiguredProject project, ILaunchSettingsProvider launchSettingsProvider) + { + _project = project; + _launchSettingsProvider = launchSettingsProvider; + } + + public async Task AddAsync(string itemType, string include, IEnumerable>? metadata = null) + { + if (!StringComparers.ItemTypes.Equals(itemType, ItemType)) { - _project = project; - _launchSettingsProvider = launchSettingsProvider; + throw new ArgumentException($"The {nameof(LaunchProfileProjectItemProvider)} does not handle the '{itemType}' item type."); } - public async Task AddAsync(string itemType, string include, IEnumerable>? metadata = null) + WritableLaunchProfile newLaunchProfile = new() { Name = include }; + await _launchSettingsProvider.AddOrUpdateProfileAsync(newLaunchProfile.ToLaunchProfile(), addToFront: false); + + // TODO: set the metadata on the launch profile. + + return await FindItemByNameAsync(include); + } + + /// + /// As this adds multiple items we should consider adding them all as an atomic operation. + /// However, currently provides no way of doing that. + /// + public async Task> AddAsync(IEnumerable>?>> items) + { + List projectItems = new(); + + foreach ((string itemType, string include, IEnumerable>? metadata) in items) { - if (!StringComparers.ItemTypes.Equals(itemType, ItemType)) + IProjectItem? projectItem = await AddAsync(itemType, include, metadata); + if (projectItem is not null) { - throw new ArgumentException($"The {nameof(LaunchProfileProjectItemProvider)} does not handle the '{itemType}' item type."); + projectItems.Add(projectItem); } + } - WritableLaunchProfile newLaunchProfile = new() { Name = include }; - await _launchSettingsProvider.AddOrUpdateProfileAsync(newLaunchProfile.ToLaunchProfile(), addToFront: false); + return projectItems; + } - // TODO: set the metadata on the launch profile. + /// + /// Launch profiles do not represent a file on disk, so adding one via a path is + /// a meaningless operation. + /// + public Task AddAsync(string path) + { + throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support adding items as paths."); + } - return await FindItemByNameAsync(include); - } + /// + /// Launch profiles do not represent a file on disk, so adding one via a path is + /// a meaningless operation. + /// + public Task> AddAsync(IEnumerable paths) + { + throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support adding items as paths."); + } - /// - /// As this adds multiple items we should consider adding them all as an atomic operation. - /// However, currently provides no way of doing that. - /// - public async Task> AddAsync(IEnumerable>?>> items) - { - List projectItems = new(); + public async Task FindItemByNameAsync(string evaluatedInclude) + { + IEnumerable items = await GetItemsAsync(); + return items.FirstOrDefault(item => StringComparers.ItemNames.Equals(item.EvaluatedInclude, evaluatedInclude)); + } - foreach ((string itemType, string include, IEnumerable>? metadata) in items) - { - IProjectItem? projectItem = await AddAsync(itemType, include, metadata); - if (projectItem is not null) - { - projectItems.Add(projectItem); - } - } + public async Task> GetExistingItemTypesAsync() + { + ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); - return projectItems; - } + return snapshot.Profiles.Count > 0 + ? s_itemTypes + : ImmutableSortedSet.Empty; + } - /// - /// Launch profiles do not represent a file on disk, so adding one via a path is - /// a meaningless operation. - /// - public Task AddAsync(string path) + public async Task GetItemAsync(IProjectPropertiesContext context) + { + if (context.ItemType is not null + && !StringComparers.ItemTypes.Equals(ItemType, context.ItemType)) { - throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support adding items as paths."); + return null; } - /// - /// Launch profiles do not represent a file on disk, so adding one via a path is - /// a meaningless operation. - /// - public Task> AddAsync(IEnumerable paths) + IEnumerable items = await GetItemsAsync(); + return items.FirstOrDefault(item => StringComparers.ItemNames.Equals(item.EvaluatedInclude, context.ItemName)); + } + + public async Task> GetItemsAsync() + { + ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); + + if (snapshot.Profiles.Count == 0) { - throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support adding items as paths."); + return Enumerable.Empty(); } - public async Task FindItemByNameAsync(string evaluatedInclude) + return snapshot.Profiles.Select(p => new ProjectItem(p.Name ?? string.Empty, _project.FullPath, this)); + } + + public Task> GetItemsAsync(string itemType) + { + if (StringComparers.ItemTypes.Equals(itemType, ItemType)) { - IEnumerable items = await GetItemsAsync(); - return items.FirstOrDefault(item => StringComparers.ItemNames.Equals(item.EvaluatedInclude, evaluatedInclude)); + return GetItemsAsync(); } - public async Task> GetExistingItemTypesAsync() - { - ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); + return TaskResult.EmptyEnumerable(); + } - return snapshot.Profiles.Count > 0 - ? s_itemTypes - : ImmutableSortedSet.Empty; - } + public async Task> GetItemsAsync(string itemType, string evaluatedInclude) + { + IEnumerable items = await GetItemsAsync(itemType); - public async Task GetItemAsync(IProjectPropertiesContext context) - { - if (context.ItemType is not null - && !StringComparers.ItemTypes.Equals(ItemType, context.ItemType)) - { - return null; - } + return items.Where(item => StringComparers.ItemNames.Equals(item.EvaluatedInclude, evaluatedInclude)); + } - IEnumerable items = await GetItemsAsync(); - return items.FirstOrDefault(item => StringComparers.ItemNames.Equals(item.EvaluatedInclude, context.ItemName)); - } + public async Task> GetItemsAsync(IReadOnlyCollection contexts) + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: contexts.Count); - public async Task> GetItemsAsync() + foreach (IProjectPropertiesContext context in contexts) { - ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); + builder.Add(await GetItemAsync(context)); + } - if (snapshot.Profiles.Count == 0) - { - return Enumerable.Empty(); - } + return builder.MoveToImmutable(); + } - return snapshot.Profiles.Select(p => new ProjectItem(p.Name ?? string.Empty, _project.FullPath, this)); - } + public Task> GetItemTypesAsync() + { + return Task.FromResult>(s_itemTypes); + } - public Task> GetItemsAsync(string itemType) + public async Task RemoveAsync(string itemType, string include, DeleteOptions deleteOptions = DeleteOptions.None) + { + if (!StringComparers.ItemTypes.Equals(itemType, ItemType)) { - if (StringComparers.ItemTypes.Equals(itemType, ItemType)) - { - return GetItemsAsync(); - } - - return TaskResult.EmptyEnumerable(); + return; } - public async Task> GetItemsAsync(string itemType, string evaluatedInclude) - { - IEnumerable items = await GetItemsAsync(itemType); + await _launchSettingsProvider.RemoveProfileAsync(include); + } - return items.Where(item => StringComparers.ItemNames.Equals(item.EvaluatedInclude, evaluatedInclude)); - } + public Task RemoveAsync(IProjectItem item, DeleteOptions deleteOptions = DeleteOptions.None) + { + Assumes.NotNull(item.ItemType); + return RemoveAsync(item.ItemType, item.UnevaluatedInclude, deleteOptions); + } - public async Task> GetItemsAsync(IReadOnlyCollection contexts) + /// + /// As this removes multiple items we should consider adding them all as an atomic operation. + /// However, currently provides no way of doing that. + /// + public async Task RemoveAsync(IEnumerable items, DeleteOptions deleteOptions = DeleteOptions.None) + { + foreach (IProjectItem item in items) { - ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(initialCapacity: contexts.Count); + await RemoveAsync(item, deleteOptions); + } + } - foreach (IProjectPropertiesContext context in contexts) - { - builder.Add(await GetItemAsync(context)); - } + /// + /// Effectively, setting the Include on an item renames that item. does + /// not natively support renaming a profile; we should consider adding that functionality or "faking" it via + /// a remove and add. In the latter case we would want this to be an atomic operation. + /// + public Task SetUnevaluatedIncludesAsync(IReadOnlyCollection> renames) + { + throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support renaming items."); + } - return builder.MoveToImmutable(); - } + /// + /// An implementation of that represents an individual launch profile. + /// + private class ProjectItem : IProjectItem + { + private readonly string _name; + private readonly string _projectFilePath; + private readonly LaunchProfileProjectItemProvider _provider; - public Task> GetItemTypesAsync() + public ProjectItem(string name, string projectFilePath, LaunchProfileProjectItemProvider provider) { - return Task.FromResult>(s_itemTypes); + _name = name; + _projectFilePath = projectFilePath; + _provider = provider; + + PropertiesContext = new ProjectPropertiesContext(name, projectFilePath); } - public async Task RemoveAsync(string itemType, string include, DeleteOptions deleteOptions = DeleteOptions.None) - { - if (!StringComparers.ItemTypes.Equals(itemType, ItemType)) - { - return; - } + public string ItemType => LaunchProfileProjectItemProvider.ItemType; - await _launchSettingsProvider.RemoveProfileAsync(include); - } + /// + /// Launch profiles have no concept of evaluation, so the evaluated and unevaluated + /// names are the same. + /// + public string UnevaluatedInclude => _name; - public Task RemoveAsync(IProjectItem item, DeleteOptions deleteOptions = DeleteOptions.None) + /// + /// Launch profiles have no concept of evaluation, so the evaluated and unevaluated + /// names are the same. + /// + public string EvaluatedInclude => _name; + + /// + /// Launch profiles represent an entry in the launchSettings.json file rather than a + /// file on disk; as such there is no meaningful value we can return here. + /// + public string EvaluatedIncludeAsFullPath => string.Empty; + + /// + /// Launch profiles represent an entry in the launchSettings.json file rather than a + /// file on disk; as such there is no meaningful value we can return here. + /// + public string EvaluatedIncludeAsRelativePath => string.Empty; + + public IProjectPropertiesContext PropertiesContext { get; } + + public IProjectProperties Metadata => throw new NotImplementedException(); + + public Task RemoveAsync(DeleteOptions deleteOptions = DeleteOptions.None) { - Assumes.NotNull(item.ItemType); - return RemoveAsync(item.ItemType, item.UnevaluatedInclude, deleteOptions); + return _provider.RemoveAsync(this, deleteOptions); } /// - /// As this removes multiple items we should consider adding them all as an atomic operation. - /// However, currently provides no way of doing that. + /// The only supports one item type and + /// there is no meaningful "conversion" to other item types, so we don't allow this + /// operation. /// - public async Task RemoveAsync(IEnumerable items, DeleteOptions deleteOptions = DeleteOptions.None) + public Task SetItemTypeAsync(string value) { - foreach (IProjectItem item in items) - { - await RemoveAsync(item, deleteOptions); - } + throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support changing item types."); } /// - /// Effectively, setting the Include on an item renames that item. does - /// not natively support renaming a profile; we should consider adding that functionality or "faking" it via - /// a remove and add. In the latter case we would want this to be an atomic operation. + /// We don't support renaming a launch profile. /// - public Task SetUnevaluatedIncludesAsync(IReadOnlyCollection> renames) + public Task SetUnevaluatedIncludeAsync(string value) { throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support renaming items."); } /// - /// An implementation of that represents an individual launch profile. + /// Implementation of that represents a specific launch + /// profile. /// - private class ProjectItem : IProjectItem + private class ProjectPropertiesContext : IProjectPropertiesContext { private readonly string _name; private readonly string _projectFilePath; - private readonly LaunchProfileProjectItemProvider _provider; - public ProjectItem(string name, string projectFilePath, LaunchProfileProjectItemProvider provider) + public ProjectPropertiesContext(string name, string projectFilePath) { _name = name; _projectFilePath = projectFilePath; - _provider = provider; - - PropertiesContext = new ProjectPropertiesContext(name, projectFilePath); } - public string ItemType => LaunchProfileProjectItemProvider.ItemType; - - /// - /// Launch profiles have no concept of evaluation, so the evaluated and unevaluated - /// names are the same. - /// - public string UnevaluatedInclude => _name; - - /// - /// Launch profiles have no concept of evaluation, so the evaluated and unevaluated - /// names are the same. - /// - public string EvaluatedInclude => _name; - - /// - /// Launch profiles represent an entry in the launchSettings.json file rather than a - /// file on disk; as such there is no meaningful value we can return here. - /// - public string EvaluatedIncludeAsFullPath => string.Empty; - /// - /// Launch profiles represent an entry in the launchSettings.json file rather than a - /// file on disk; as such there is no meaningful value we can return here. + /// Launch profiles can only appear in the launchSettings.json, and there can only be + /// one launchSettings.json per project. As such, all profiles are logically part of + /// the project file. /// - public string EvaluatedIncludeAsRelativePath => string.Empty; - - public IProjectPropertiesContext PropertiesContext { get; } - - public IProjectProperties Metadata => throw new NotImplementedException(); - - public Task RemoveAsync(DeleteOptions deleteOptions = DeleteOptions.None) - { - return _provider.RemoveAsync(this, deleteOptions); - } + public bool IsProjectFile => true; /// - /// The only supports one item type and - /// there is no meaningful "conversion" to other item types, so we don't allow this - /// operation. + /// Logically, profiles belong to the project file and can only belong to the project + /// file, as opposed to an imported file. /// - public Task SetItemTypeAsync(string value) - { - throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support changing item types."); - } + public string File => _projectFilePath; - /// - /// We don't support renaming a launch profile. - /// - public Task SetUnevaluatedIncludeAsync(string value) - { - throw new InvalidOperationException($"The {nameof(LaunchProfileProjectItemProvider)} does not support renaming items."); - } + public string? ItemType => LaunchProfileProjectItemProvider.ItemType; - /// - /// Implementation of that represents a specific launch - /// profile. - /// - private class ProjectPropertiesContext : IProjectPropertiesContext - { - private readonly string _name; - private readonly string _projectFilePath; - - public ProjectPropertiesContext(string name, string projectFilePath) - { - _name = name; - _projectFilePath = projectFilePath; - } - - /// - /// Launch profiles can only appear in the launchSettings.json, and there can only be - /// one launchSettings.json per project. As such, all profiles are logically part of - /// the project file. - /// - public bool IsProjectFile => true; - - /// - /// Logically, profiles belong to the project file and can only belong to the project - /// file, as opposed to an imported file. - /// - public string File => _projectFilePath; - - public string? ItemType => LaunchProfileProjectItemProvider.ItemType; - - public string? ItemName => _name; - } + public string? ItemName => _name; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectProperties.cs index 6e28772c7d..b5220b91f4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectProperties.cs @@ -10,377 +10,376 @@ Microsoft.VisualStudio.ProjectSystem.Properties.ILaunchProfileExtensionValueProvider, Microsoft.VisualStudio.ProjectSystem.Properties.ILaunchProfileExtensionValueProviderMetadata>; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal class LaunchProfileProjectProperties : IProjectProperties, IRuleAwareProjectProperties { - internal class LaunchProfileProjectProperties : IProjectProperties, IRuleAwareProjectProperties + private const string CommandNamePropertyName = "CommandName"; + private const string ExecutablePathPropertyName = "ExecutablePath"; + private const string CommandLineArgumentsPropertyName = "CommandLineArguments"; + private const string WorkingDirectoryPropertyName = "WorkingDirectory"; + private const string LaunchBrowserPropertyName = "LaunchBrowser"; + private const string LaunchUrlPropertyName = "LaunchUrl"; + private const string EnvironmentVariablesPropertyName = "EnvironmentVariables"; + + private Rule? _rule; + + /// + /// These correspond to the properties explicitly declared on + /// and as such they are always considered to exist on the profile, though they may + /// not have a value. + /// + private static readonly string[] s_standardPropertyNames = new[] { - private const string CommandNamePropertyName = "CommandName"; - private const string ExecutablePathPropertyName = "ExecutablePath"; - private const string CommandLineArgumentsPropertyName = "CommandLineArguments"; - private const string WorkingDirectoryPropertyName = "WorkingDirectory"; - private const string LaunchBrowserPropertyName = "LaunchBrowser"; - private const string LaunchUrlPropertyName = "LaunchUrl"; - private const string EnvironmentVariablesPropertyName = "EnvironmentVariables"; - - private Rule? _rule; - - /// - /// These correspond to the properties explicitly declared on - /// and as such they are always considered to exist on the profile, though they may - /// not have a value. - /// - private static readonly string[] s_standardPropertyNames = new[] - { - CommandNamePropertyName, - ExecutablePathPropertyName, - CommandLineArgumentsPropertyName, - WorkingDirectoryPropertyName, - LaunchBrowserPropertyName, - LaunchUrlPropertyName, - EnvironmentVariablesPropertyName - }; + CommandNamePropertyName, + ExecutablePathPropertyName, + CommandLineArgumentsPropertyName, + WorkingDirectoryPropertyName, + LaunchBrowserPropertyName, + LaunchUrlPropertyName, + EnvironmentVariablesPropertyName + }; + + private readonly LaunchProfilePropertiesContext _context; + private readonly ILaunchSettingsProvider3 _launchSettingsProvider; + private readonly ImmutableDictionary _launchProfileValueProviders; + private readonly ImmutableDictionary _globalSettingValueProviders; + + public LaunchProfileProjectProperties( + string filePath, + string profileName, + ILaunchSettingsProvider3 launchSettingsProvider, + ImmutableArray launchProfileExtensionValueProviders, + ImmutableArray globalSettingExtensionValueProviders) + { + _context = new LaunchProfilePropertiesContext(filePath, profileName); + _launchSettingsProvider = launchSettingsProvider; - private readonly LaunchProfilePropertiesContext _context; - private readonly ILaunchSettingsProvider3 _launchSettingsProvider; - private readonly ImmutableDictionary _launchProfileValueProviders; - private readonly ImmutableDictionary _globalSettingValueProviders; - - public LaunchProfileProjectProperties( - string filePath, - string profileName, - ILaunchSettingsProvider3 launchSettingsProvider, - ImmutableArray launchProfileExtensionValueProviders, - ImmutableArray globalSettingExtensionValueProviders) + ImmutableDictionary.Builder launchProfileValueBuilder = + ImmutableDictionary.CreateBuilder(StringComparers.PropertyNames); + foreach (LaunchProfileValueProviderAndMetadata valueProvider in launchProfileExtensionValueProviders) { - _context = new LaunchProfilePropertiesContext(filePath, profileName); - _launchSettingsProvider = launchSettingsProvider; + string[] propertyNames = valueProvider.Metadata.PropertyNames; - ImmutableDictionary.Builder launchProfileValueBuilder = - ImmutableDictionary.CreateBuilder(StringComparers.PropertyNames); - foreach (LaunchProfileValueProviderAndMetadata valueProvider in launchProfileExtensionValueProviders) + foreach (string propertyName in propertyNames) { - string[] propertyNames = valueProvider.Metadata.PropertyNames; + Requires.Argument(!string.IsNullOrEmpty(propertyName), nameof(valueProvider), "A null or empty property name was found"); - foreach (string propertyName in propertyNames) - { - Requires.Argument(!string.IsNullOrEmpty(propertyName), nameof(valueProvider), "A null or empty property name was found"); + // CONSIDER: Allow duplicate intercepting property value providers for same property name. + Requires.Argument(!launchProfileValueBuilder.ContainsKey(propertyName), nameof(launchProfileValueBuilder), "Duplicate property value providers for same property name"); - // CONSIDER: Allow duplicate intercepting property value providers for same property name. - Requires.Argument(!launchProfileValueBuilder.ContainsKey(propertyName), nameof(launchProfileValueBuilder), "Duplicate property value providers for same property name"); - - launchProfileValueBuilder.Add(propertyName, valueProvider); - } + launchProfileValueBuilder.Add(propertyName, valueProvider); } - _launchProfileValueProviders = launchProfileValueBuilder.ToImmutable(); + } + _launchProfileValueProviders = launchProfileValueBuilder.ToImmutable(); - ImmutableDictionary.Builder globalSettingValueBuilder = - ImmutableDictionary.CreateBuilder(StringComparers.PropertyNames); - foreach (GlobalSettingValueProviderAndMetadata valueProvider in globalSettingExtensionValueProviders) - { - string[] propertyNames = valueProvider.Metadata.PropertyNames; + ImmutableDictionary.Builder globalSettingValueBuilder = + ImmutableDictionary.CreateBuilder(StringComparers.PropertyNames); + foreach (GlobalSettingValueProviderAndMetadata valueProvider in globalSettingExtensionValueProviders) + { + string[] propertyNames = valueProvider.Metadata.PropertyNames; - foreach (string propertyName in propertyNames) - { - Requires.Argument(!string.IsNullOrEmpty(propertyName), nameof(valueProvider), "A null or empty property name was found"); + foreach (string propertyName in propertyNames) + { + Requires.Argument(!string.IsNullOrEmpty(propertyName), nameof(valueProvider), "A null or empty property name was found"); - // CONSIDER: Allow duplicate intercepting property value providers for same property name. - Requires.Argument(!globalSettingValueBuilder.ContainsKey(propertyName), nameof(globalSettingValueBuilder), "Duplicate property value providers for same property name"); + // CONSIDER: Allow duplicate intercepting property value providers for same property name. + Requires.Argument(!globalSettingValueBuilder.ContainsKey(propertyName), nameof(globalSettingValueBuilder), "Duplicate property value providers for same property name"); - globalSettingValueBuilder.Add(propertyName, valueProvider); - } + globalSettingValueBuilder.Add(propertyName, valueProvider); } - _globalSettingValueProviders = globalSettingValueBuilder.ToImmutable(); } + _globalSettingValueProviders = globalSettingValueBuilder.ToImmutable(); + } - public IProjectPropertiesContext Context => _context; + public IProjectPropertiesContext Context => _context; - public string FileFullPath => _context.File; + public string FileFullPath => _context.File; - public PropertyKind PropertyKind => PropertyKind.ItemGroup; + public PropertyKind PropertyKind => PropertyKind.ItemGroup; - public Task DeleteDirectPropertiesAsync() - { - throw new NotImplementedException(); - } - - public Task DeletePropertyAsync(string propertyName, IReadOnlyDictionary? dimensionalConditions = null) - { - throw new NotImplementedException(); - } + public Task DeleteDirectPropertiesAsync() + { + throw new NotImplementedException(); + } - public Task> GetDirectPropertyNamesAsync() - { - return GetPropertyNamesAsync(); - } + public Task DeletePropertyAsync(string propertyName, IReadOnlyDictionary? dimensionalConditions = null) + { + throw new NotImplementedException(); + } - public async Task GetEvaluatedPropertyValueAsync(string propertyName) - { - return await GetUnevaluatedPropertyValueAsync(propertyName) ?? string.Empty; - } + public Task> GetDirectPropertyNamesAsync() + { + return GetPropertyNamesAsync(); + } - /// - /// If the profile exists we return all the standard property names (as they are - /// always considered defined) plus all of the defined properties supported by - /// extenders. - /// - public async Task> GetPropertyNamesAsync() - { - ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); + public async Task GetEvaluatedPropertyValueAsync(string propertyName) + { + return await GetUnevaluatedPropertyValueAsync(propertyName) ?? string.Empty; + } - ILaunchProfile? profile = snapshot.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, _context.ItemName)); - if (profile is null) - { - return Enumerable.Empty(); - } - ImmutableDictionary globalSettings = snapshot.GlobalSettings; + /// + /// If the profile exists we return all the standard property names (as they are + /// always considered defined) plus all of the defined properties supported by + /// extenders. + /// + public async Task> GetPropertyNamesAsync() + { + ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); - ImmutableSortedSet.Builder builder = ImmutableSortedSet.CreateBuilder(StringComparers.PropertyNames); - builder.UnionWith(s_standardPropertyNames); + ILaunchProfile? profile = snapshot.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, _context.ItemName)); + if (profile is null) + { + return Enumerable.Empty(); + } + ImmutableDictionary globalSettings = snapshot.GlobalSettings; - foreach ((string propertyName, LaunchProfileValueProviderAndMetadata provider) in _launchProfileValueProviders) - { - string propertyValue = provider.Value.OnGetPropertyValue(propertyName, profile, globalSettings, _rule); - if (!Strings.IsNullOrEmpty(propertyValue)) - { - builder.Add(propertyName); - } - } + ImmutableSortedSet.Builder builder = ImmutableSortedSet.CreateBuilder(StringComparers.PropertyNames); + builder.UnionWith(s_standardPropertyNames); - foreach ((string propertyName, GlobalSettingValueProviderAndMetadata provider) in _globalSettingValueProviders) + foreach ((string propertyName, LaunchProfileValueProviderAndMetadata provider) in _launchProfileValueProviders) + { + string propertyValue = provider.Value.OnGetPropertyValue(propertyName, profile, globalSettings, _rule); + if (!Strings.IsNullOrEmpty(propertyValue)) { - string propertyValue = provider.Value.OnGetPropertyValue(propertyName, globalSettings, _rule); - if (!Strings.IsNullOrEmpty(propertyValue)) - { - builder.Add(propertyName); - } + builder.Add(propertyName); } + } - foreach ((string propertyName, _) in profile.EnumerateOtherSettings()) + foreach ((string propertyName, GlobalSettingValueProviderAndMetadata provider) in _globalSettingValueProviders) + { + string propertyValue = provider.Value.OnGetPropertyValue(propertyName, globalSettings, _rule); + if (!Strings.IsNullOrEmpty(propertyValue)) { builder.Add(propertyName); } - - return builder.ToImmutable(); } - /// - /// If the profile does not exist, returns . Otherwise, returns the value - /// of the property if the property is not defined, or otherwise. The - /// standard properties are always considered to be defined. - /// - public async Task GetUnevaluatedPropertyValueAsync(string propertyName) + foreach ((string propertyName, _) in profile.EnumerateOtherSettings()) { - ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); + builder.Add(propertyName); + } - ILaunchProfile? profile = snapshot.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, _context.ItemName)); - if (profile is null) - { - return null; - } + return builder.ToImmutable(); + } - return propertyName switch - { - CommandNamePropertyName => profile.CommandName ?? string.Empty, - ExecutablePathPropertyName => profile.ExecutablePath ?? string.Empty, - CommandLineArgumentsPropertyName => profile.CommandLineArgs ?? string.Empty, - WorkingDirectoryPropertyName => profile.WorkingDirectory ?? string.Empty, - LaunchBrowserPropertyName => profile.LaunchBrowser ? "true" : "false", - LaunchUrlPropertyName => profile.LaunchUrl ?? string.Empty, - EnvironmentVariablesPropertyName => LaunchProfileEnvironmentVariableEncoding.Format(profile), - _ => GetExtensionPropertyValue(propertyName, profile, snapshot.GlobalSettings) - }; - } + /// + /// If the profile does not exist, returns . Otherwise, returns the value + /// of the property if the property is not defined, or otherwise. The + /// standard properties are always considered to be defined. + /// + public async Task GetUnevaluatedPropertyValueAsync(string propertyName) + { + ILaunchSettings snapshot = await _launchSettingsProvider.WaitForFirstSnapshot(); - public Task IsValueInheritedAsync(string propertyName) + ILaunchProfile? profile = snapshot.Profiles.FirstOrDefault(p => StringComparers.LaunchProfileNames.Equals(p.Name, _context.ItemName)); + if (profile is null) { - return TaskResult.False; + return null; } - public async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + return propertyName switch { - Action? profileUpdateAction = null; + CommandNamePropertyName => profile.CommandName ?? string.Empty, + ExecutablePathPropertyName => profile.ExecutablePath ?? string.Empty, + CommandLineArgumentsPropertyName => profile.CommandLineArgs ?? string.Empty, + WorkingDirectoryPropertyName => profile.WorkingDirectory ?? string.Empty, + LaunchBrowserPropertyName => profile.LaunchBrowser ? "true" : "false", + LaunchUrlPropertyName => profile.LaunchUrl ?? string.Empty, + EnvironmentVariablesPropertyName => LaunchProfileEnvironmentVariableEncoding.Format(profile), + _ => GetExtensionPropertyValue(propertyName, profile, snapshot.GlobalSettings) + }; + } - // If this is a standard property, handle it ourselves. + public Task IsValueInheritedAsync(string propertyName) + { + return TaskResult.False; + } - profileUpdateAction = propertyName switch - { - CommandNamePropertyName => profile => profile.CommandName = unevaluatedPropertyValue, - ExecutablePathPropertyName => profile => profile.ExecutablePath = unevaluatedPropertyValue, - CommandLineArgumentsPropertyName => profile => profile.CommandLineArgs = unevaluatedPropertyValue, - WorkingDirectoryPropertyName => profile => profile.WorkingDirectory = unevaluatedPropertyValue, - LaunchBrowserPropertyName => setLaunchBrowserProperty, - LaunchUrlPropertyName => profile => profile.LaunchUrl = unevaluatedPropertyValue, - EnvironmentVariablesPropertyName => profile => LaunchProfileEnvironmentVariableEncoding.ParseIntoDictionary(unevaluatedPropertyValue, profile.EnvironmentVariables), - _ => null - }; + public async Task SetPropertyValueAsync(string propertyName, string unevaluatedPropertyValue, IReadOnlyDictionary? dimensionalConditions = null) + { + Action? profileUpdateAction = null; - if (profileUpdateAction is not null) - { - await _launchSettingsProvider.TryUpdateProfileAsync(_context.ItemName, profileUpdateAction); - return; - } + // If this is a standard property, handle it ourselves. - // Next, check if a launch profile extender can handle it. + profileUpdateAction = propertyName switch + { + CommandNamePropertyName => profile => profile.CommandName = unevaluatedPropertyValue, + ExecutablePathPropertyName => profile => profile.ExecutablePath = unevaluatedPropertyValue, + CommandLineArgumentsPropertyName => profile => profile.CommandLineArgs = unevaluatedPropertyValue, + WorkingDirectoryPropertyName => profile => profile.WorkingDirectory = unevaluatedPropertyValue, + LaunchBrowserPropertyName => setLaunchBrowserProperty, + LaunchUrlPropertyName => profile => profile.LaunchUrl = unevaluatedPropertyValue, + EnvironmentVariablesPropertyName => profile => LaunchProfileEnvironmentVariableEncoding.ParseIntoDictionary(unevaluatedPropertyValue, profile.EnvironmentVariables), + _ => null + }; - profileUpdateAction = await GetPropertyValueSetterFromLaunchProfileExtendersAsync(propertyName, unevaluatedPropertyValue); - if (profileUpdateAction is not null) - { - await _launchSettingsProvider.TryUpdateProfileAsync(_context.ItemName, profileUpdateAction); - return; - } + if (profileUpdateAction is not null) + { + await _launchSettingsProvider.TryUpdateProfileAsync(_context.ItemName, profileUpdateAction); + return; + } - // Then, check if a global setting extender can handle it. + // Next, check if a launch profile extender can handle it. - Func, ImmutableDictionary>? globalSettingsUpdateFunction = GetPropertyValueSetterFromGlobalExtenders(propertyName, unevaluatedPropertyValue); - if (globalSettingsUpdateFunction is not null) - { - await _launchSettingsProvider.UpdateGlobalSettingsAsync(globalSettingsUpdateFunction); - return; - } + profileUpdateAction = await GetPropertyValueSetterFromLaunchProfileExtendersAsync(propertyName, unevaluatedPropertyValue); + if (profileUpdateAction is not null) + { + await _launchSettingsProvider.TryUpdateProfileAsync(_context.ItemName, profileUpdateAction); + return; + } - // Finally, store it in ILaunchProfile.OtherSettings. + // Then, check if a global setting extender can handle it. - object? valueObject = null; - if (_rule?.GetProperty(propertyName) is BaseProperty property) - { - valueObject = property switch - { - BoolProperty => bool.TryParse(unevaluatedPropertyValue, out bool result) ? result : null, - IntProperty => int.TryParse(unevaluatedPropertyValue, out int result) ? result : null, - StringProperty => unevaluatedPropertyValue, - EnumProperty => unevaluatedPropertyValue, - DynamicEnumProperty => unevaluatedPropertyValue, - _ => throw new InvalidOperationException($"{nameof(LaunchProfileProjectProperties)} does not know how to convert strings to `{property.GetType()}`.") - }; - } - else - { - valueObject = unevaluatedPropertyValue; - } + Func, ImmutableDictionary>? globalSettingsUpdateFunction = GetPropertyValueSetterFromGlobalExtenders(propertyName, unevaluatedPropertyValue); + if (globalSettingsUpdateFunction is not null) + { + await _launchSettingsProvider.UpdateGlobalSettingsAsync(globalSettingsUpdateFunction); + return; + } - if (valueObject is not null) - { - profileUpdateAction = p => p.OtherSettings[propertyName] = valueObject; - await _launchSettingsProvider.TryUpdateProfileAsync(_context.ItemName, profileUpdateAction); - } + // Finally, store it in ILaunchProfile.OtherSettings. - void setLaunchBrowserProperty(IWritableLaunchProfile profile) + object? valueObject = null; + if (_rule?.GetProperty(propertyName) is BaseProperty property) + { + valueObject = property switch { - if (bool.TryParse(unevaluatedPropertyValue, out bool result)) - { - profile.LaunchBrowser = result; - } - } + BoolProperty => bool.TryParse(unevaluatedPropertyValue, out bool result) ? result : null, + IntProperty => int.TryParse(unevaluatedPropertyValue, out int result) ? result : null, + StringProperty => unevaluatedPropertyValue, + EnumProperty => unevaluatedPropertyValue, + DynamicEnumProperty => unevaluatedPropertyValue, + _ => throw new InvalidOperationException($"{nameof(LaunchProfileProjectProperties)} does not know how to convert strings to `{property.GetType()}`.") + }; + } + else + { + valueObject = unevaluatedPropertyValue; } - public void SetRuleContext(Rule rule) + if (valueObject is not null) { - _rule = rule; + profileUpdateAction = p => p.OtherSettings[propertyName] = valueObject; + await _launchSettingsProvider.TryUpdateProfileAsync(_context.ItemName, profileUpdateAction); } - private string? GetExtensionPropertyValue(string propertyName, ILaunchProfile profile, ImmutableDictionary globalSettings) + void setLaunchBrowserProperty(IWritableLaunchProfile profile) { - if (_launchProfileValueProviders.TryGetValue(propertyName, out LaunchProfileValueProviderAndMetadata? launchProfileValueProvider)) + if (bool.TryParse(unevaluatedPropertyValue, out bool result)) { - return launchProfileValueProvider.Value.OnGetPropertyValue(propertyName, profile, globalSettings, rule: _rule); + profile.LaunchBrowser = result; } + } + } - if (_globalSettingValueProviders.TryGetValue(propertyName, out GlobalSettingValueProviderAndMetadata? globalSettingValueProvider)) - { - return globalSettingValueProvider.Value.OnGetPropertyValue(propertyName, globalSettings, rule: _rule); - } + public void SetRuleContext(Rule rule) + { + _rule = rule; + } - return GetOtherSettingsPropertyValue(propertyName, profile); + private string? GetExtensionPropertyValue(string propertyName, ILaunchProfile profile, ImmutableDictionary globalSettings) + { + if (_launchProfileValueProviders.TryGetValue(propertyName, out LaunchProfileValueProviderAndMetadata? launchProfileValueProvider)) + { + return launchProfileValueProvider.Value.OnGetPropertyValue(propertyName, profile, globalSettings, rule: _rule); } - private string? GetOtherSettingsPropertyValue(string propertyName, ILaunchProfile profile) + if (_globalSettingValueProviders.TryGetValue(propertyName, out GlobalSettingValueProviderAndMetadata? globalSettingValueProvider)) { - if (profile.TryGetSetting(propertyName, out object? valueObject)) - { - if (_rule?.GetProperty(propertyName) is BaseProperty property) - { - return property switch - { - BoolProperty => boolToString(valueObject), - IntProperty => intToString(valueObject), - StringProperty => valueObject as string, - EnumProperty => valueObject as string, - DynamicEnumProperty => valueObject as string, - _ => throw new InvalidOperationException($"{nameof(LaunchProfileProjectProperties)} does not know how to convert `{property.GetType()}` to a string.") - }; - } - - return valueObject as string; - } + return globalSettingValueProvider.Value.OnGetPropertyValue(propertyName, globalSettings, rule: _rule); + } - return null; + return GetOtherSettingsPropertyValue(propertyName, profile); + } - string? boolToString(object valueObject) + private string? GetOtherSettingsPropertyValue(string propertyName, ILaunchProfile profile) + { + if (profile.TryGetSetting(propertyName, out object? valueObject)) + { + if (_rule?.GetProperty(propertyName) is BaseProperty property) { - if (valueObject is bool value) + return property switch { - return value ? "true" : "false"; - } - - return null; + BoolProperty => boolToString(valueObject), + IntProperty => intToString(valueObject), + StringProperty => valueObject as string, + EnumProperty => valueObject as string, + DynamicEnumProperty => valueObject as string, + _ => throw new InvalidOperationException($"{nameof(LaunchProfileProjectProperties)} does not know how to convert `{property.GetType()}` to a string.") + }; } - string? intToString(object valueObject) - { - if (valueObject is int value) - { - return value.ToString(); - } - - return null; - } + return valueObject as string; } - private async Task?> GetPropertyValueSetterFromLaunchProfileExtendersAsync(string propertyName, string unevaluatedValue) + return null; + + string? boolToString(object valueObject) { - if (_launchProfileValueProviders.TryGetValue(propertyName, out LaunchProfileValueProviderAndMetadata? launchProfileValueProvider)) + if (valueObject is bool value) { - ILaunchSettings currentSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); - - ImmutableDictionary? globalSettings = currentSettings.GlobalSettings; - - return profile => - { - launchProfileValueProvider.Value.OnSetPropertyValue(propertyName, unevaluatedValue, profile, globalSettings, _rule); - }; + return value ? "true" : "false"; } return null; } - private Func, ImmutableDictionary>? GetPropertyValueSetterFromGlobalExtenders(string propertyName, string unevaluatedValue) + string? intToString(object valueObject) { - if (_globalSettingValueProviders.TryGetValue(propertyName, out GlobalSettingValueProviderAndMetadata? globalSettingValueProvider)) + if (valueObject is int value) { - return globalSettings => - { - return globalSettingValueProvider.Value.OnSetPropertyValue(propertyName, unevaluatedValue, globalSettings, _rule); - }; + return value.ToString(); } return null; } + } - private class LaunchProfilePropertiesContext : IProjectPropertiesContext + private async Task?> GetPropertyValueSetterFromLaunchProfileExtendersAsync(string propertyName, string unevaluatedValue) + { + if (_launchProfileValueProviders.TryGetValue(propertyName, out LaunchProfileValueProviderAndMetadata? launchProfileValueProvider)) { - public LaunchProfilePropertiesContext(string file, string itemName) + ILaunchSettings currentSettings = await _launchSettingsProvider.WaitForFirstSnapshot(); + + ImmutableDictionary? globalSettings = currentSettings.GlobalSettings; + + return profile => { - File = file; - ItemName = itemName; - } + launchProfileValueProvider.Value.OnSetPropertyValue(propertyName, unevaluatedValue, profile, globalSettings, _rule); + }; + } - public bool IsProjectFile => true; + return null; + } - public string File { get; } + private Func, ImmutableDictionary>? GetPropertyValueSetterFromGlobalExtenders(string propertyName, string unevaluatedValue) + { + if (_globalSettingValueProviders.TryGetValue(propertyName, out GlobalSettingValueProviderAndMetadata? globalSettingValueProvider)) + { + return globalSettings => + { + return globalSettingValueProvider.Value.OnSetPropertyValue(propertyName, unevaluatedValue, globalSettings, _rule); + }; + } - public string ItemType => LaunchProfileProjectItemProvider.ItemType; + return null; + } - public string ItemName { get; } + private class LaunchProfilePropertiesContext : IProjectPropertiesContext + { + public LaunchProfilePropertiesContext(string file, string itemName) + { + File = file; + ItemName = itemName; } + + public bool IsProjectFile => true; + + public string File { get; } + + public string ItemType => LaunchProfileProjectItemProvider.ItemType; + + public string ItemName { get; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectPropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectPropertiesProvider.cs index c7d8d91d78..427fc8b79f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectPropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/LaunchProfileProjectPropertiesProvider.cs @@ -3,97 +3,96 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[Export("LaunchProfile", typeof(IProjectPropertiesProvider))] +[Export(typeof(IProjectPropertiesProvider))] +[ExportMetadata("Name", "LaunchProfile")] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal partial class LaunchProfileProjectPropertiesProvider : IProjectPropertiesProvider { - [Export("LaunchProfile", typeof(IProjectPropertiesProvider))] - [Export(typeof(IProjectPropertiesProvider))] - [ExportMetadata("Name", "LaunchProfile")] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal partial class LaunchProfileProjectPropertiesProvider : IProjectPropertiesProvider - { - private readonly UnconfiguredProject _project; - private readonly ILaunchSettingsProvider3 _launchSettingsProvider; - private readonly ImmutableArray> _launchProfileExtensionValueProviders; - private readonly ImmutableArray> _globalSettingExtensionValueProviders; + private readonly UnconfiguredProject _project; + private readonly ILaunchSettingsProvider3 _launchSettingsProvider; + private readonly ImmutableArray> _launchProfileExtensionValueProviders; + private readonly ImmutableArray> _globalSettingExtensionValueProviders; - [ImportingConstructor] - public LaunchProfileProjectPropertiesProvider( - UnconfiguredProject project, - ILaunchSettingsProvider3 launchSettingsProvider, - [ImportMany]IEnumerable> launchProfileExtensionValueProviders, - [ImportMany]IEnumerable> globalSettingExtensionValueProviders) - { - _project = project; - _launchSettingsProvider = launchSettingsProvider; - _launchProfileExtensionValueProviders = launchProfileExtensionValueProviders.ToImmutableArray(); - _globalSettingExtensionValueProviders = globalSettingExtensionValueProviders.ToImmutableArray(); - } + [ImportingConstructor] + public LaunchProfileProjectPropertiesProvider( + UnconfiguredProject project, + ILaunchSettingsProvider3 launchSettingsProvider, + [ImportMany]IEnumerable> launchProfileExtensionValueProviders, + [ImportMany]IEnumerable> globalSettingExtensionValueProviders) + { + _project = project; + _launchSettingsProvider = launchSettingsProvider; + _launchProfileExtensionValueProviders = launchProfileExtensionValueProviders.ToImmutableArray(); + _globalSettingExtensionValueProviders = globalSettingExtensionValueProviders.ToImmutableArray(); + } - /// - /// There is a 1:1 mapping between launchSettings.json and the related project, and - /// launch profiles can't come from anywhere else (i.e., there's no equivalent of - /// imported .props and .targets files for launch settings). As such, launch profiles - /// are stored in the launchSettings.json file, but the logical context is the - /// project file. - /// - public string DefaultProjectPath => _project.FullPath; + /// + /// There is a 1:1 mapping between launchSettings.json and the related project, and + /// launch profiles can't come from anywhere else (i.e., there's no equivalent of + /// imported .props and .targets files for launch settings). As such, launch profiles + /// are stored in the launchSettings.json file, but the logical context is the + /// project file. + /// + public string DefaultProjectPath => _project.FullPath; #pragma warning disable CS0067 // Unused events - // Currently nothing consumes these, so we don't need to worry about firing them. - // This is great because they are supposed to offer guarantees to the event - // handlers about what locks are held--but the launch profiles don't use such - // locks. - // If in the future we determine that we need to fire these we will either need to - // work through the implications on the project locks, or we will need to decide - // if we can only fire a subset of these events. - public event AsyncEventHandler? ProjectPropertyChanging; - public event AsyncEventHandler? ProjectPropertyChangedOnWriter; - public event AsyncEventHandler? ProjectPropertyChanged; + // Currently nothing consumes these, so we don't need to worry about firing them. + // This is great because they are supposed to offer guarantees to the event + // handlers about what locks are held--but the launch profiles don't use such + // locks. + // If in the future we determine that we need to fire these we will either need to + // work through the implications on the project locks, or we will need to decide + // if we can only fire a subset of these events. + public event AsyncEventHandler? ProjectPropertyChanging; + public event AsyncEventHandler? ProjectPropertyChangedOnWriter; + public event AsyncEventHandler? ProjectPropertyChanged; #pragma warning restore CS0067 - /// - /// - /// There are no "project-level" properties supported by the launch profiles. We - /// return an implementation of for the sake of - /// completeness but there isn't much the consumer can do with it. We could also - /// consider just throwing a for this method, - /// but the impact of that isn't clear. - /// - /// - /// Note that the launch settings do support the concept of global properties, but - /// we're choosing to expose those as if they were properties on the individual - /// launch profiles. - /// - /// - public IProjectProperties GetCommonProperties() - { - return GetProperties(_project.FullPath, itemType: null, item: null); - } + /// + /// + /// There are no "project-level" properties supported by the launch profiles. We + /// return an implementation of for the sake of + /// completeness but there isn't much the consumer can do with it. We could also + /// consider just throwing a for this method, + /// but the impact of that isn't clear. + /// + /// + /// Note that the launch settings do support the concept of global properties, but + /// we're choosing to expose those as if they were properties on the individual + /// launch profiles. + /// + /// + public IProjectProperties GetCommonProperties() + { + return GetProperties(_project.FullPath, itemType: null, item: null); + } - public IProjectProperties GetItemProperties(string? itemType, string? item) - { - return GetProperties(_project.FullPath, itemType, item); - } + public IProjectProperties GetItemProperties(string? itemType, string? item) + { + return GetProperties(_project.FullPath, itemType, item); + } - public IProjectProperties GetItemTypeProperties(string? itemType) - { - return GetProperties(_project.FullPath, itemType, item: null); - } + public IProjectProperties GetItemTypeProperties(string? itemType) + { + return GetProperties(_project.FullPath, itemType, item: null); + } - public IProjectProperties GetProperties(string file, string? itemType, string? item) + public IProjectProperties GetProperties(string file, string? itemType, string? item) + { + if (item is null + || (itemType is not null + && itemType != LaunchProfileProjectItemProvider.ItemType) + || !StringComparers.Paths.Equals(_project.FullPath, file)) { - if (item is null - || (itemType is not null - && itemType != LaunchProfileProjectItemProvider.ItemType) - || !StringComparers.Paths.Equals(_project.FullPath, file)) - { - // The interface is CPS currently asserts that the Get*Properties methods return a - // non-null value, but this is incorrect--in practice the implementations do return - // null. - return null!; - } - - return new LaunchProfileProjectProperties(file, item, _launchSettingsProvider, _launchProfileExtensionValueProviders, _globalSettingExtensionValueProviders); + // The interface is CPS currently asserts that the Get*Properties methods return a + // non-null value, but this is incorrect--in practice the implementations do return + // null. + return null!; } + + return new LaunchProfileProjectProperties(file, item, _launchSettingsProvider, _launchProfileExtensionValueProviders, _globalSettingExtensionValueProviders); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProvider.cs index 9f73b4a812..0a052e108b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProvider.cs @@ -4,123 +4,122 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties -{ - /// - /// - /// Reads and writes "extension" properties in the given . - /// - /// "Extension" means properties that are stored in the - /// dictionary, rather than in named properties of - /// itself. Those are handled by . - /// - /// - [ExportLaunchProfileExtensionValueProvider( - new[] - { - AuthenticationModePropertyName, - HotReloadEnabledPropertyName, - NativeDebuggingPropertyName, - RemoteDebugEnabledPropertyName, - RemoteDebugMachinePropertyName, - SqlDebuggingPropertyName, - WebView2DebuggingPropertyName - }, - ExportLaunchProfileExtensionValueProviderScope.LaunchProfile)] - internal class ProjectLaunchProfileExtensionValueProvider : ILaunchProfileExtensionValueProvider +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// +/// Reads and writes "extension" properties in the given . +/// +/// "Extension" means properties that are stored in the +/// dictionary, rather than in named properties of +/// itself. Those are handled by . +/// +/// +[ExportLaunchProfileExtensionValueProvider( + new[] { - internal const string AuthenticationModePropertyName = "AuthenticationMode"; - internal const string HotReloadEnabledPropertyName = "HotReloadEnabled"; - internal const string NativeDebuggingPropertyName = "NativeDebugging"; - internal const string RemoteDebugEnabledPropertyName = "RemoteDebugEnabled"; - internal const string RemoteDebugMachinePropertyName = "RemoteDebugMachine"; - internal const string SqlDebuggingPropertyName = "SqlDebugging"; - internal const string WebView2DebuggingPropertyName = "WebView2Debugging"; + AuthenticationModePropertyName, + HotReloadEnabledPropertyName, + NativeDebuggingPropertyName, + RemoteDebugEnabledPropertyName, + RemoteDebugMachinePropertyName, + SqlDebuggingPropertyName, + WebView2DebuggingPropertyName + }, + ExportLaunchProfileExtensionValueProviderScope.LaunchProfile)] +internal class ProjectLaunchProfileExtensionValueProvider : ILaunchProfileExtensionValueProvider +{ + internal const string AuthenticationModePropertyName = "AuthenticationMode"; + internal const string HotReloadEnabledPropertyName = "HotReloadEnabled"; + internal const string NativeDebuggingPropertyName = "NativeDebugging"; + internal const string RemoteDebugEnabledPropertyName = "RemoteDebugEnabled"; + internal const string RemoteDebugMachinePropertyName = "RemoteDebugMachine"; + internal const string SqlDebuggingPropertyName = "SqlDebugging"; + internal const string WebView2DebuggingPropertyName = "WebView2Debugging"; - // The CPS property system will map "true" and "false" to the localized versions of - // "Yes" and "No" for display purposes, but not other casings like "True" and - // "False". To ensure consistency we need to map booleans to these constants. - private const string True = "true"; - private const string False = "false"; + // The CPS property system will map "true" and "false" to the localized versions of + // "Yes" and "No" for display purposes, but not other casings like "True" and + // "False". To ensure consistency we need to map booleans to these constants. + private const string True = "true"; + private const string False = "false"; - public string OnGetPropertyValue(string propertyName, ILaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule) + public string OnGetPropertyValue(string propertyName, ILaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule) + { + string propertyValue = propertyName switch { - string propertyValue = propertyName switch - { - AuthenticationModePropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteAuthenticationModeProperty, string.Empty), - HotReloadEnabledPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.HotReloadEnabledProperty, true) ? True : False, - NativeDebuggingPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.NativeDebuggingProperty, false) ? True : False, - RemoteDebugEnabledPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugEnabledProperty, false) ? True : False, - RemoteDebugMachinePropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugMachineProperty, string.Empty), - SqlDebuggingPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.SqlDebuggingProperty, false) ? True : False, - WebView2DebuggingPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.JSWebView2DebuggingProperty, false) ? True : False, + AuthenticationModePropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteAuthenticationModeProperty, string.Empty), + HotReloadEnabledPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.HotReloadEnabledProperty, true) ? True : False, + NativeDebuggingPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.NativeDebuggingProperty, false) ? True : False, + RemoteDebugEnabledPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugEnabledProperty, false) ? True : False, + RemoteDebugMachinePropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugMachineProperty, string.Empty), + SqlDebuggingPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.SqlDebuggingProperty, false) ? True : False, + WebView2DebuggingPropertyName => GetOtherProperty(launchProfile, LaunchProfileExtensions.JSWebView2DebuggingProperty, false) ? True : False, - _ => throw new InvalidOperationException($"{nameof(ProjectLaunchProfileExtensionValueProvider)} does not handle property '{propertyName}'.") - }; + _ => throw new InvalidOperationException($"{nameof(ProjectLaunchProfileExtensionValueProvider)} does not handle property '{propertyName}'.") + }; - return propertyValue; - } + return propertyValue; + } - public void OnSetPropertyValue(string propertyName, string propertyValue, IWritableLaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule) + public void OnSetPropertyValue(string propertyName, string propertyValue, IWritableLaunchProfile launchProfile, ImmutableDictionary globalSettings, Rule? rule) + { + // TODO: Should the result (success or failure) be ignored? + _ = propertyName switch { - // TODO: Should the result (success or failure) be ignored? - _ = propertyName switch - { - AuthenticationModePropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteAuthenticationModeProperty, propertyValue, string.Empty), - HotReloadEnabledPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.HotReloadEnabledProperty, bool.Parse(propertyValue), true), - NativeDebuggingPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.NativeDebuggingProperty, bool.Parse(propertyValue), false), - RemoteDebugEnabledPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugEnabledProperty, bool.Parse(propertyValue), false), - RemoteDebugMachinePropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugMachineProperty, propertyValue, string.Empty), - SqlDebuggingPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.SqlDebuggingProperty, bool.Parse(propertyValue), false), - WebView2DebuggingPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.JSWebView2DebuggingProperty, bool.Parse(propertyValue), false), - _ => throw new InvalidOperationException($"{nameof(ProjectLaunchProfileExtensionValueProvider)} does not handle property '{propertyName}'."), - }; - } + AuthenticationModePropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteAuthenticationModeProperty, propertyValue, string.Empty), + HotReloadEnabledPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.HotReloadEnabledProperty, bool.Parse(propertyValue), true), + NativeDebuggingPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.NativeDebuggingProperty, bool.Parse(propertyValue), false), + RemoteDebugEnabledPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugEnabledProperty, bool.Parse(propertyValue), false), + RemoteDebugMachinePropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.RemoteDebugMachineProperty, propertyValue, string.Empty), + SqlDebuggingPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.SqlDebuggingProperty, bool.Parse(propertyValue), false), + WebView2DebuggingPropertyName => TrySetOtherProperty(launchProfile, LaunchProfileExtensions.JSWebView2DebuggingProperty, bool.Parse(propertyValue), false), + _ => throw new InvalidOperationException($"{nameof(ProjectLaunchProfileExtensionValueProvider)} does not handle property '{propertyName}'."), + }; + } - private static T GetOtherProperty(ILaunchProfile launchProfile, string propertyName, T defaultValue) + private static T GetOtherProperty(ILaunchProfile launchProfile, string propertyName, T defaultValue) + { + if (launchProfile.TryGetSetting(propertyName, out object? value)) { - if (launchProfile.TryGetSetting(propertyName, out object? value)) + if (value is T b) { - if (value is T b) - { - return b; - } + return b; + } - if (value is string s && - TypeDescriptor.GetConverter(typeof(T)) is TypeConverter converter && - converter.CanConvertFrom(typeof(string))) + if (value is string s && + TypeDescriptor.GetConverter(typeof(T)) is TypeConverter converter && + converter.CanConvertFrom(typeof(string))) + { + try { - try + if (converter.ConvertFromString(s) is T o) { - if (converter.ConvertFromString(s) is T o) - { - return o; - } - } - catch (Exception) - { - // ignore bad data in the json file and just let them have the default value + return o; } } + catch (Exception) + { + // ignore bad data in the json file and just let them have the default value + } } - - return defaultValue; } - private static bool TrySetOtherProperty(IWritableLaunchProfile launchProfile, string propertyName, T value, T defaultValue) where T : notnull - { - if (!launchProfile.OtherSettings.TryGetValue(propertyName, out object? current)) - { - current = defaultValue; - } + return defaultValue; + } - if (current is not T currentTyped || !Equals(currentTyped, value)) - { - launchProfile.OtherSettings[propertyName] = value; - return true; - } + private static bool TrySetOtherProperty(IWritableLaunchProfile launchProfile, string propertyName, T value, T defaultValue) where T : notnull + { + if (!launchProfile.OtherSettings.TryGetValue(propertyName, out object? current)) + { + current = defaultValue; + } - return false; + if (current is not T currentTyped || !Equals(currentTyped, value)) + { + launchProfile.OtherSettings[propertyName] = value; + return true; } + + return false; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchTargetPropertyPageEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchTargetPropertyPageEnumProvider.cs index 22c2630167..e1d8b39351 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchTargetPropertyPageEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/LaunchTargetPropertyPageEnumProvider.cs @@ -3,68 +3,67 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Provides the names and display names of "debugger" property pages that specify a +/// launch target. +/// +/// +/// Specifically, we look for pages with a "commandNameBasedDebugger" +/// and a CommandName property in their metadata. +/// +[ExportDynamicEnumValuesProvider(nameof(LaunchTargetPropertyPageEnumProvider))] +[AppliesTo(ProjectCapability.LaunchProfiles)] +internal class LaunchTargetPropertyPageEnumProvider : IDynamicEnumValuesProvider { - /// - /// Provides the names and display names of "debugger" property pages that specify a - /// launch target. - /// - /// - /// Specifically, we look for pages with a "commandNameBasedDebugger" - /// and a CommandName property in their metadata. - /// - [ExportDynamicEnumValuesProvider(nameof(LaunchTargetPropertyPageEnumProvider))] - [AppliesTo(ProjectCapability.LaunchProfiles)] - internal class LaunchTargetPropertyPageEnumProvider : IDynamicEnumValuesProvider + private readonly ConfiguredProject _configuredProject; + + [ImportingConstructor] + public LaunchTargetPropertyPageEnumProvider(ConfiguredProject configuredProject) + { + _configuredProject = configuredProject; + } + + public Task GetProviderAsync(IList? options) + { + return Task.FromResult(new LaunchTargetPropertyPageEnumValuesGenerator(_configuredProject)); + } + + internal class LaunchTargetPropertyPageEnumValuesGenerator : IDynamicEnumValuesGenerator { private readonly ConfiguredProject _configuredProject; - [ImportingConstructor] - public LaunchTargetPropertyPageEnumProvider(ConfiguredProject configuredProject) + public LaunchTargetPropertyPageEnumValuesGenerator(ConfiguredProject configuredProject) { _configuredProject = configuredProject; } - public Task GetProviderAsync(IList? options) - { - return Task.FromResult(new LaunchTargetPropertyPageEnumValuesGenerator(_configuredProject)); - } + public bool AllowCustomValues => false; - internal class LaunchTargetPropertyPageEnumValuesGenerator : IDynamicEnumValuesGenerator + public async Task> GetListedValuesAsync() { - private readonly ConfiguredProject _configuredProject; + var catalogProvider = _configuredProject.Services.PropertyPagesCatalog; - public LaunchTargetPropertyPageEnumValuesGenerator(ConfiguredProject configuredProject) + if (catalogProvider is null) { - _configuredProject = configuredProject; + return Array.Empty(); } - public bool AllowCustomValues => false; - - public async Task> GetListedValuesAsync() - { - var catalogProvider = _configuredProject.Services.PropertyPagesCatalog; - - if (catalogProvider is null) + IPropertyPagesCatalog catalog = await catalogProvider.GetCatalogAsync(PropertyPageContexts.Project); + return catalog.GetPropertyPagesSchemas() + .Select(catalog.GetSchema) + .WhereNotNull() + .Where(rule => string.Equals(rule.PageTemplate, "CommandNameBasedDebugger", StringComparison.OrdinalIgnoreCase) + && rule.Metadata.TryGetValue("CommandName", out object? pageCommandNameObj)) + .Select(rule => new PageEnumValue(new EnumValue { - return Array.Empty(); - } - - IPropertyPagesCatalog catalog = await catalogProvider.GetCatalogAsync(PropertyPageContexts.Project); - return catalog.GetPropertyPagesSchemas() - .Select(catalog.GetSchema) - .WhereNotNull() - .Where(rule => string.Equals(rule.PageTemplate, "CommandNameBasedDebugger", StringComparison.OrdinalIgnoreCase) - && rule.Metadata.TryGetValue("CommandName", out object? pageCommandNameObj)) - .Select(rule => new PageEnumValue(new EnumValue - { - Name = rule.Name, - DisplayName = rule.DisplayName - })) - .ToArray(); - } - - public Task TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); + Name = rule.Name, + DisplayName = rule.DisplayName + })) + .ToArray(); } + + public Task TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/MetadataExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/MetadataExtensions.cs index 4db8cbcf86..dd20fee209 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/MetadataExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/MetadataExtensions.cs @@ -2,120 +2,119 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static class MetadataExtensions { - internal static class MetadataExtensions + /// + /// Finds the resolved reference item for a given unresolved reference. + /// + /// Resolved reference project items snapshot to search. + /// The unresolved reference item name. + /// The key is item name and the value is the metadata dictionary. + public static IImmutableDictionary? GetProjectItemProperties(this IProjectRuleSnapshot projectRuleSnapshot, string itemSpec) { - /// - /// Finds the resolved reference item for a given unresolved reference. - /// - /// Resolved reference project items snapshot to search. - /// The unresolved reference item name. - /// The key is item name and the value is the metadata dictionary. - public static IImmutableDictionary? GetProjectItemProperties(this IProjectRuleSnapshot projectRuleSnapshot, string itemSpec) - { - Requires.NotNull(projectRuleSnapshot); - Requires.NotNullOrEmpty(itemSpec); - - return projectRuleSnapshot.Items.TryGetValue(itemSpec, out IImmutableDictionary? properties) - ? properties - : null; - } + Requires.NotNull(projectRuleSnapshot); + Requires.NotNullOrEmpty(itemSpec); - /// - /// Attempts to get the string value corresponding to . - /// - /// - /// Missing and empty string values are treated in the same fashion. - /// - /// The property dictionary to query. - /// The key that identifies the property to look up. - /// The value of the string if found and non-empty, otherwise . - /// if the property was found with a non-empty value, otherwise . - public static bool TryGetStringProperty(this IImmutableDictionary properties, string key, [NotNullWhen(returnValue: true)] out string? stringValue) - { - if (properties is not null && - properties.TryGetValue(key, out stringValue) && - !string.IsNullOrEmpty(stringValue)) - { - return true; - } - - stringValue = null; - return false; - } + return projectRuleSnapshot.Items.TryGetValue(itemSpec, out IImmutableDictionary? properties) + ? properties + : null; + } - /// - /// Gets the string value corresponding to , or if the property was not found or had an empty value. - /// - /// The property dictionary to query. - /// The key that identifies the property to look up. - /// The string value if found and non-empty, otherwise . - public static string? GetStringProperty(this IImmutableDictionary properties, string key) + /// + /// Attempts to get the string value corresponding to . + /// + /// + /// Missing and empty string values are treated in the same fashion. + /// + /// The property dictionary to query. + /// The key that identifies the property to look up. + /// The value of the string if found and non-empty, otherwise . + /// if the property was found with a non-empty value, otherwise . + public static bool TryGetStringProperty(this IImmutableDictionary properties, string key, [NotNullWhen(returnValue: true)] out string? stringValue) + { + if (properties is not null && + properties.TryGetValue(key, out stringValue) && + !string.IsNullOrEmpty(stringValue)) { - return properties.TryGetStringProperty(key, out string? value) && !string.IsNullOrEmpty(value) ? value : null; + return true; } - /// - /// Attempts to get the boolean interpretation of the value corresponding to . - /// - /// The property dictionary to query. - /// The key that identifies the property to look up. - /// The boolean value of the property if found and successfully parsed, otherwise . - /// if the property was found with successfully parsed as a boolean, otherwise . - public static bool TryGetBoolProperty(this IImmutableDictionary properties, string key, out bool boolValue) - { - if (properties.TryGetStringProperty(key, out string? valueString) && - bool.TryParse(valueString, out boolValue)) - { - return true; - } + stringValue = null; + return false; + } - boolValue = default; - return false; - } + /// + /// Gets the string value corresponding to , or if the property was not found or had an empty value. + /// + /// The property dictionary to query. + /// The key that identifies the property to look up. + /// The string value if found and non-empty, otherwise . + public static string? GetStringProperty(this IImmutableDictionary properties, string key) + { + return properties.TryGetStringProperty(key, out string? value) && !string.IsNullOrEmpty(value) ? value : null; + } - /// - /// Gets the boolean value corresponding to , or if the property was missing or could not be parsed as a boolean. - /// - /// The property dictionary to query. - /// The key that identifies the property to look up. - /// The boolean value if found and successfully parsed as a boolean, otherwise . - public static bool? GetBoolProperty(this IImmutableDictionary properties, string key) + /// + /// Attempts to get the boolean interpretation of the value corresponding to . + /// + /// The property dictionary to query. + /// The key that identifies the property to look up. + /// The boolean value of the property if found and successfully parsed, otherwise . + /// if the property was found with successfully parsed as a boolean, otherwise . + public static bool TryGetBoolProperty(this IImmutableDictionary properties, string key, out bool boolValue) + { + if (properties.TryGetStringProperty(key, out string? valueString) && + bool.TryParse(valueString, out boolValue)) { - return properties.TryGetBoolProperty(key, out bool value) ? value : null; + return true; } - /// - /// Attempts to get the enum type interpretation of the value corresponding to . - /// - /// The property dictionary to query. - /// The key that identifies the property to look up. - /// The enum value of the property if found and successfully parsed, otherwise . - /// if the property was found with successfully parsed as enum type , otherwise . - /// The enum type. - public static bool TryGetEnumProperty(this IImmutableDictionary properties, string key, out T enumValue) where T : struct, Enum - { - if (properties.TryGetStringProperty(key, out string? valueString) && - Enum.TryParse(valueString, ignoreCase: true, out enumValue)) - { - return true; - } + boolValue = default; + return false; + } - enumValue = default; - return false; - } + /// + /// Gets the boolean value corresponding to , or if the property was missing or could not be parsed as a boolean. + /// + /// The property dictionary to query. + /// The key that identifies the property to look up. + /// The boolean value if found and successfully parsed as a boolean, otherwise . + public static bool? GetBoolProperty(this IImmutableDictionary properties, string key) + { + return properties.TryGetBoolProperty(key, out bool value) ? value : null; + } - /// - /// Gets the enum value corresponding to , or if the property was missing or could not be parsed as an enum. - /// - /// The property dictionary to query. - /// The key that identifies the property to look up. - /// The enum value if found and successfully parsed as enum type , otherwise . - /// The enum type. - public static T? GetEnumProperty(this IImmutableDictionary properties, string key) where T : struct, Enum + /// + /// Attempts to get the enum type interpretation of the value corresponding to . + /// + /// The property dictionary to query. + /// The key that identifies the property to look up. + /// The enum value of the property if found and successfully parsed, otherwise . + /// if the property was found with successfully parsed as enum type , otherwise . + /// The enum type. + public static bool TryGetEnumProperty(this IImmutableDictionary properties, string key, out T enumValue) where T : struct, Enum + { + if (properties.TryGetStringProperty(key, out string? valueString) && + Enum.TryParse(valueString, ignoreCase: true, out enumValue)) { - return properties.TryGetEnumProperty(key, out T value) ? value : null; + return true; } + + enumValue = default; + return false; + } + + /// + /// Gets the enum value corresponding to , or if the property was missing or could not be parsed as an enum. + /// + /// The property dictionary to query. + /// The key that identifies the property to look up. + /// The enum value if found and successfully parsed as enum type , otherwise . + /// The enum type. + public static T? GetEnumProperty(this IImmutableDictionary properties, string key) where T : struct, Enum + { + return properties.TryGetEnumProperty(key, out T value) ? value : null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/NeutralLanguageEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/NeutralLanguageEnumProvider.cs index c4d10ae339..04ac752533 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/NeutralLanguageEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/NeutralLanguageEnumProvider.cs @@ -5,35 +5,34 @@ using Microsoft.VisualStudio.ProjectSystem.Properties.Package; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[ExportDynamicEnumValuesProvider(nameof(NeutralLanguageEnumProvider))] +[AppliesTo(ProjectCapability.CSharpOrVisualBasic)] +internal class NeutralLanguageEnumProvider : IDynamicEnumValuesProvider { - [ExportDynamicEnumValuesProvider(nameof(NeutralLanguageEnumProvider))] - [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] - internal class NeutralLanguageEnumProvider : IDynamicEnumValuesProvider + public Task GetProviderAsync(IList? options) { - public Task GetProviderAsync(IList? options) - { - return Task.FromResult(new NeutralLanguageEnumGenerator()); - } - - private class NeutralLanguageEnumGenerator : IDynamicEnumValuesGenerator - { - public bool AllowCustomValues => false; + return Task.FromResult(new NeutralLanguageEnumGenerator()); + } - public Task> GetListedValuesAsync() - { - var values = Enumerable.Empty() - .Append(new PageEnumValue(new EnumValue() { Name = NeutralLanguageValueProvider.NoneValue, DisplayName = Resources.Property_NoneValue })) - .Concat(CultureInfo.GetCultures(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures | CultureTypes.InstalledWin32Cultures) - .Where(info => info.Name.Length != 0) - .OrderBy(info => info.Name, StringComparer.OrdinalIgnoreCase) - .Select(info => new PageEnumValue(new EnumValue() { Name = info.Name, DisplayName = string.Format(Resources.NeutralLanguage_DisplayNameFormatString, info.DisplayName, info.Name) }))) - .ToArray(); + private class NeutralLanguageEnumGenerator : IDynamicEnumValuesGenerator + { + public bool AllowCustomValues => false; - return Task.FromResult>(values); - } + public Task> GetListedValuesAsync() + { + var values = Enumerable.Empty() + .Append(new PageEnumValue(new EnumValue() { Name = NeutralLanguageValueProvider.NoneValue, DisplayName = Resources.Property_NoneValue })) + .Concat(CultureInfo.GetCultures(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures | CultureTypes.InstalledWin32Cultures) + .Where(info => info.Name.Length != 0) + .OrderBy(info => info.Name, StringComparer.OrdinalIgnoreCase) + .Select(info => new PageEnumValue(new EnumValue() { Name = info.Name, DisplayName = string.Format(Resources.NeutralLanguage_DisplayNameFormatString, info.DisplayName, info.Name) }))) + .ToArray(); - public Task TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); + return Task.FromResult>(values); } + + public Task TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PlatformTargetBuildPropertyPageEnumProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PlatformTargetBuildPropertyPageEnumProvider.cs index 69d475bd49..2f916ad172 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PlatformTargetBuildPropertyPageEnumProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PlatformTargetBuildPropertyPageEnumProvider.cs @@ -5,53 +5,52 @@ using Microsoft.VisualStudio.Threading; using EnumCollection = System.Collections.Generic.ICollection; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Responsible for producing valid values for the TargetPlatform property from a design-time build. +/// +[ExportDynamicEnumValuesProvider("PlatformTargetEnumProvider")] +[AppliesTo(ProjectCapability.DotNet)] +internal class PlatformTargetBuildPropertyPageEnumProvider : IDynamicEnumValuesProvider, IDynamicEnumValuesGenerator { - /// - /// Responsible for producing valid values for the TargetPlatform property from a design-time build. - /// - [ExportDynamicEnumValuesProvider("PlatformTargetEnumProvider")] - [AppliesTo(ProjectCapability.DotNet)] - internal class PlatformTargetBuildPropertyPageEnumProvider : IDynamicEnumValuesProvider, IDynamicEnumValuesGenerator - { - private const string AnyCpuPlatformName = "AnyCPU"; - private const string AnyCpuDisplayName = "Any CPU"; - - private readonly ProjectProperties _properties; - - [ImportingConstructor] - public PlatformTargetBuildPropertyPageEnumProvider(ProjectProperties properties) - { - _properties = properties; - } + private const string AnyCpuPlatformName = "AnyCPU"; + private const string AnyCpuDisplayName = "Any CPU"; - public bool AllowCustomValues => false; + private readonly ProjectProperties _properties; - public async Task GetListedValuesAsync() - { - var result = new List(); + [ImportingConstructor] + public PlatformTargetBuildPropertyPageEnumProvider(ProjectProperties properties) + { + _properties = properties; + } - ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); + public bool AllowCustomValues => false; - string availablePlatformsTargets = await configuration.AvailablePlatforms.GetDisplayValueAsync(); + public async Task GetListedValuesAsync() + { + var result = new List(); - foreach (string platformTarget in new LazyStringSplit(availablePlatformsTargets, ',')) - { - result.Add(new PageEnumValue(new EnumValue() { Name = platformTarget, DisplayName = platformTarget.Equals(AnyCpuPlatformName, StringComparisons.ConfigurationDimensionValues) ? AnyCpuDisplayName : platformTarget })); - } + ConfigurationGeneral configuration = await _properties.GetConfigurationGeneralPropertiesAsync(); - return result; - } + string availablePlatformsTargets = await configuration.AvailablePlatforms.GetDisplayValueAsync(); - public Task GetProviderAsync(IList? options) + foreach (string platformTarget in new LazyStringSplit(availablePlatformsTargets, ',')) { - return Task.FromResult(this); + result.Add(new PageEnumValue(new EnumValue() { Name = platformTarget, DisplayName = platformTarget.Equals(AnyCpuPlatformName, StringComparisons.ConfigurationDimensionValues) ? AnyCpuDisplayName : platformTarget })); } - public Task TryCreateEnumValueAsync(string userSuppliedValue) - { - return TaskResult.Null(); - } + return result; + } + + public Task GetProviderAsync(IList? options) + { + return Task.FromResult(this); + } + + public Task TryCreateEnumValueAsync(string userSuppliedValue) + { + return TaskResult.Null(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectPropertiesExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectPropertiesExtensions.cs index 17a47348d1..354fc377c8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectPropertiesExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectPropertiesExtensions.cs @@ -1,38 +1,37 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static class ProjectPropertiesExtensions { - internal static class ProjectPropertiesExtensions + /// + /// Saves the unevaluated value of , if defined in context, to . + /// + public static async Task SaveValueIfCurrentlySetAsync(this IProjectProperties properties, string propertyName, ITemporaryPropertyStorage storage) { - /// - /// Saves the unevaluated value of , if defined in context, to . - /// - public static async Task SaveValueIfCurrentlySetAsync(this IProjectProperties properties, string propertyName, ITemporaryPropertyStorage storage) + if (!await properties.IsValueInheritedAsync(propertyName)) { - if (!await properties.IsValueInheritedAsync(propertyName)) + string? currentPropertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); + if (!Strings.IsNullOrEmpty(currentPropertyValue)) { - string? currentPropertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); - if (!Strings.IsNullOrEmpty(currentPropertyValue)) - { - storage.AddOrUpdatePropertyValue(propertyName, currentPropertyValue); - } + storage.AddOrUpdatePropertyValue(propertyName, currentPropertyValue); } } + } - /// - /// Restores the saved value of , if not defined in context, from . - /// - public static async Task RestoreValueIfNotCurrentlySetAsync(this IProjectProperties properties, string propertyName, ITemporaryPropertyStorage storage, IReadOnlyDictionary? dimensionalConditions = null) + /// + /// Restores the saved value of , if not defined in context, from . + /// + public static async Task RestoreValueIfNotCurrentlySetAsync(this IProjectProperties properties, string propertyName, ITemporaryPropertyStorage storage, IReadOnlyDictionary? dimensionalConditions = null) + { + if (storage.GetPropertyValue(propertyName) is string previousValue) { - if (storage.GetPropertyValue(propertyName) is string previousValue) - { - bool inherited = await properties.IsValueInheritedAsync(propertyName); - string? currentPropertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); + bool inherited = await properties.IsValueInheritedAsync(propertyName); + string? currentPropertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); - if (inherited || string.IsNullOrEmpty(currentPropertyValue)) - { - await properties.SetPropertyValueAsync(propertyName, previousValue, dimensionalConditions); - } + if (inherited || string.IsNullOrEmpty(currentPropertyValue)) + { + await properties.SetPropertyValueAsync(propertyName, previousValue, dimensionalConditions); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.EmptyProjectRuleSnapshot.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.EmptyProjectRuleSnapshot.cs index 63718aa495..68fb0dd8a7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.EmptyProjectRuleSnapshot.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.EmptyProjectRuleSnapshot.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static partial class ProjectRuleSnapshotExtensions { - internal static partial class ProjectRuleSnapshotExtensions + private class EmptyProjectRuleSnapshot : IProjectRuleSnapshot { - private class EmptyProjectRuleSnapshot : IProjectRuleSnapshot - { - public static readonly EmptyProjectRuleSnapshot Instance = new(); + public static readonly EmptyProjectRuleSnapshot Instance = new(); - public string RuleName - { - get { throw new NotSupportedException(); } - } + public string RuleName + { + get { throw new NotSupportedException(); } + } - public IImmutableDictionary> Items - { - get { return ImmutableDictionary>.Empty; } - } + public IImmutableDictionary> Items + { + get { return ImmutableDictionary>.Empty; } + } - public IImmutableDictionary Properties - { - get { return ImmutableDictionary.Empty; } - } + public IImmutableDictionary Properties + { + get { return ImmutableDictionary.Empty; } } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.cs index a60e9b9894..eb8f1900ac 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/ProjectRuleSnapshotExtensions.cs @@ -2,152 +2,151 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Contains common extensions for instances. +/// +internal static partial class ProjectRuleSnapshotExtensions { /// - /// Contains common extensions for instances. + /// Gets the value that is associated with specified name, or an empty string ("") if it does not exist. /// - internal static partial class ProjectRuleSnapshotExtensions + public static string GetPropertyOrEmpty(this IImmutableDictionary properties, string name) { - /// - /// Gets the value that is associated with specified name, or an empty string ("") if it does not exist. - /// - public static string GetPropertyOrEmpty(this IImmutableDictionary properties, string name) - { - Requires.NotNull(properties); - Requires.NotNullOrEmpty(name); + Requires.NotNull(properties); + Requires.NotNullOrEmpty(name); - return properties.GetValueOrDefault(name, string.Empty); - } + return properties.GetValueOrDefault(name, string.Empty); + } - /// - /// Gets the value that is associated with the specified rule and property. - /// - [return: NotNullIfNotNull(parameterName: nameof(defaultValue))] - public static string? GetPropertyOrDefault(this IImmutableDictionary snapshots, string ruleName, string propertyName, string? defaultValue) - { - Requires.NotNull(snapshots); - Requires.NotNullOrEmpty(ruleName); - Requires.NotNullOrEmpty(propertyName); + /// + /// Gets the value that is associated with the specified rule and property. + /// + [return: NotNullIfNotNull(parameterName: nameof(defaultValue))] + public static string? GetPropertyOrDefault(this IImmutableDictionary snapshots, string ruleName, string propertyName, string? defaultValue) + { + Requires.NotNull(snapshots); + Requires.NotNullOrEmpty(ruleName); + Requires.NotNullOrEmpty(propertyName); - if (snapshots.TryGetValue(ruleName, out IProjectRuleSnapshot? snapshot) && snapshot.Properties.TryGetValue(propertyName, out string? value)) + if (snapshots.TryGetValue(ruleName, out IProjectRuleSnapshot? snapshot) && snapshot.Properties.TryGetValue(propertyName, out string? value)) + { + // Similar to MSBuild, we treat the absence of a property the same as an empty property + if (!string.IsNullOrEmpty(value)) { - // Similar to MSBuild, we treat the absence of a property the same as an empty property - if (!string.IsNullOrEmpty(value)) - { - return value; - } + return value; } - - return defaultValue; } - /// - /// Returns a value indicating if the value that is associated with the specified rule and property is . - /// - public static bool IsPropertyTrue(this IImmutableDictionary snapshots, string ruleName, string propertyName, bool defaultValue) - { - Requires.NotNull(snapshots); - Requires.NotNull(ruleName); - Requires.NotNull(propertyName); + return defaultValue; + } - string value = snapshots.GetPropertyOrDefault(ruleName, propertyName, defaultValue ? "true" : "false"); + /// + /// Returns a value indicating if the value that is associated with the specified rule and property is . + /// + public static bool IsPropertyTrue(this IImmutableDictionary snapshots, string ruleName, string propertyName, bool defaultValue) + { + Requires.NotNull(snapshots); + Requires.NotNull(ruleName); + Requires.NotNull(propertyName); - return StringComparers.PropertyLiteralValues.Equals(value, "true"); - } + string value = snapshots.GetPropertyOrDefault(ruleName, propertyName, defaultValue ? "true" : "false"); - /// - /// Gets the bool value of a property, or if it is empty or cannot otherwise be parsed as a bool. - /// - public static bool? GetBooleanPropertyValue(this IImmutableDictionary snapshots, string ruleName, string propertyName) - { - Requires.NotNull(snapshots); - Requires.NotNull(ruleName); - Requires.NotNull(propertyName); + return StringComparers.PropertyLiteralValues.Equals(value, "true"); + } - string? value = snapshots.GetPropertyOrDefault(ruleName, propertyName, defaultValue: null); + /// + /// Gets the bool value of a property, or if it is empty or cannot otherwise be parsed as a bool. + /// + public static bool? GetBooleanPropertyValue(this IImmutableDictionary snapshots, string ruleName, string propertyName) + { + Requires.NotNull(snapshots); + Requires.NotNull(ruleName); + Requires.NotNull(propertyName); - return bool.TryParse(value, out bool b) ? b : null; - } + string? value = snapshots.GetPropertyOrDefault(ruleName, propertyName, defaultValue: null); - /// - /// Gets the snapshot associated with the specified rule, or an empty snapshot if it does not exist. - /// - public static IProjectRuleSnapshot GetSnapshotOrEmpty(this IImmutableDictionary snapshots, string ruleName) - { - Requires.NotNull(snapshots); + return bool.TryParse(value, out bool b) ? b : null; + } - if (snapshots.TryGetValue(ruleName, out IProjectRuleSnapshot? result)) - { - return result; - } + /// + /// Gets the snapshot associated with the specified rule, or an empty snapshot if it does not exist. + /// + public static IProjectRuleSnapshot GetSnapshotOrEmpty(this IImmutableDictionary snapshots, string ruleName) + { + Requires.NotNull(snapshots); - return EmptyProjectRuleSnapshot.Instance; + if (snapshots.TryGetValue(ruleName, out IProjectRuleSnapshot? result)) + { + return result; } - public static bool HasChange(this IImmutableDictionary changesByRule) - { - Requires.NotNull(changesByRule); + return EmptyProjectRuleSnapshot.Instance; + } + + public static bool HasChange(this IImmutableDictionary changesByRule) + { + Requires.NotNull(changesByRule); - foreach ((_, IProjectChangeDescription change) in changesByRule) + foreach ((_, IProjectChangeDescription change) in changesByRule) + { + if (change.Difference.AnyChanges) { - if (change.Difference.AnyChanges) - { - return true; - } + return true; } - - return false; } - /// - /// Returns snapshot data in its original order. If the original order cannot be determined, and exception is thrown. - /// Requires the snapshot to have come from CPS's build data sources. Evaluation data is currently not orderable. - /// - /// - /// You may use instead, which will return unordered data - /// rather than throw. - /// - public static IReadOnlyCollection>> GetOrderedItems(this IProjectRuleSnapshot snapshot) + return false; + } + + /// + /// Returns snapshot data in its original order. If the original order cannot be determined, and exception is thrown. + /// Requires the snapshot to have come from CPS's build data sources. Evaluation data is currently not orderable. + /// + /// + /// You may use instead, which will return unordered data + /// rather than throw. + /// + public static IReadOnlyCollection>> GetOrderedItems(this IProjectRuleSnapshot snapshot) + { + if (snapshot.Items.Count == 0) { - if (snapshot.Items.Count == 0) - { - // Some empty snapshot data can leak through here that doesn't implement IDataWithOriginalSource. - // Protect against that and return an empty collection. There's no order to empty data, so there's - // no behavior issue here. - // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1761872 - return ImmutableList>>.Empty; - } + // Some empty snapshot data can leak through here that doesn't implement IDataWithOriginalSource. + // Protect against that and return an empty collection. There's no order to empty data, so there's + // no behavior issue here. + // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1761872 + return ImmutableList>>.Empty; + } - var orderedSource = snapshot.Items as IDataWithOriginalSource>>; + var orderedSource = snapshot.Items as IDataWithOriginalSource>>; - // CPS build data is always supposed to implement this interface. If you see this exception, it's - // most likely that the snapshot this extension method was called on contains evaluation data, for - // which data ordering is currently not supported. If seen on build data however, this is likely - // an issue in CPS. - Assumes.NotNull(orderedSource); + // CPS build data is always supposed to implement this interface. If you see this exception, it's + // most likely that the snapshot this extension method was called on contains evaluation data, for + // which data ordering is currently not supported. If seen on build data however, this is likely + // an issue in CPS. + Assumes.NotNull(orderedSource); #pragma warning disable RS0030 // Do not used banned APIs - return orderedSource.SourceData; + return orderedSource.SourceData; #pragma warning restore RS0030 // Do not used banned APIs - } + } - /// - /// Returns snapshot items in order, if possible. - /// If the order cannot be determined, they are returned in a "random" order. - /// - public static IEnumerable>> TryGetOrderedItems(this IProjectRuleSnapshot snapshot) + /// + /// Returns snapshot items in order, if possible. + /// If the order cannot be determined, they are returned in a "random" order. + /// + public static IEnumerable>> TryGetOrderedItems(this IProjectRuleSnapshot snapshot) + { + if (snapshot.Items is IDataWithOriginalSource>> dataWithOriginalSource) { - if (snapshot.Items is IDataWithOriginalSource>> dataWithOriginalSource) - { #pragma warning disable RS0030 // Do not used banned APIs - return dataWithOriginalSource.SourceData; + return dataWithOriginalSource.SourceData; #pragma warning restore RS0030 // Do not used banned APIs - } - - // We couldn't obtain ordered items for some reason. - // Return the items in whatever order the backing collection from CPS models them in. - return snapshot.Items; } + + // We couldn't obtain ordered items for some reason. + // Return the items in whatever order the backing collection from CPS models them in. + return snapshot.Items; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PropertyExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PropertyExtensions.cs index ea63cbcbd0..b77d4b5b24 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PropertyExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/PropertyExtensions.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Provides extension methods for instances. +/// +internal static class PropertyExtensions { /// - /// Provides extension methods for instances. + /// Returns the value of the specific as + /// or if the value cannot be parsed. /// - internal static class PropertyExtensions + /// + /// is . + /// + public static async Task GetValueAsGuidAsync(this IProperty property) { - /// - /// Returns the value of the specific as - /// or if the value cannot be parsed. - /// - /// - /// is . - /// - public static async Task GetValueAsGuidAsync(this IProperty property) - { - Requires.NotNull(property); - - string? value = (string?)await property.GetValueAsync(); + Requires.NotNull(property); - if (Guid.TryParse(value, out Guid result)) - { - return result; - } + string? value = (string?)await property.GetValueAsync(); - return Guid.Empty; + if (Guid.TryParse(value, out Guid result)) + { + return result; } + + return Guid.Empty; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SingleRuleSupportedValuesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SingleRuleSupportedValuesProvider.cs index cf73ccb994..d03b71b9eb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SingleRuleSupportedValuesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SingleRuleSupportedValuesProvider.cs @@ -3,44 +3,43 @@ using Microsoft.Build.Framework.XamlTypes; using EnumCollection = System.Collections.Generic.ICollection; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal abstract class SingleRuleSupportedValuesProvider : SupportedValuesProvider { - internal abstract class SingleRuleSupportedValuesProvider : SupportedValuesProvider - { - /// - /// Specifies if a 'None' value should be added to the resulting list. - /// - private readonly bool _useNoneValue; + /// + /// Specifies if a 'None' value should be added to the resulting list. + /// + private readonly bool _useNoneValue; - private readonly string _ruleName; + private readonly string _ruleName; - protected sealed override string[] RuleNames => new string[] { _ruleName }; + protected sealed override string[] RuleNames => new string[] { _ruleName }; - protected SingleRuleSupportedValuesProvider(ConfiguredProject project, IProjectSubscriptionService subscriptionService, string ruleName, bool useNoneValue = false) : base(project, subscriptionService) - { - _ruleName = ruleName; - _useNoneValue = useNoneValue; - } + protected SingleRuleSupportedValuesProvider(ConfiguredProject project, IProjectSubscriptionService subscriptionService, string ruleName, bool useNoneValue = false) : base(project, subscriptionService) + { + _ruleName = ruleName; + _useNoneValue = useNoneValue; + } - protected override EnumCollection Transform(IProjectSubscriptionUpdate input) - { - IProjectRuleSnapshot snapshot = input.CurrentState[_ruleName]; + protected override EnumCollection Transform(IProjectSubscriptionUpdate input) + { + IProjectRuleSnapshot snapshot = input.CurrentState[_ruleName]; - int capacity = snapshot.Items.Count + (_useNoneValue ? 1 : 0); - var list = new List(capacity); + int capacity = snapshot.Items.Count + (_useNoneValue ? 1 : 0); + var list = new List(capacity); - if (_useNoneValue) + if (_useNoneValue) + { + list.Add(new PageEnumValue(new EnumValue { - list.Add(new PageEnumValue(new EnumValue - { - Name = string.Empty, - DisplayName = Resources.Property_NoneValue - })); - } - - list.AddRange(snapshot.Items.Select(ToEnumValue)); - list.Sort(SortValues); // TODO: This is a hotfix for item ordering. Remove this when completing: https://github.com/dotnet/project-system/issues/7025 - return list; + Name = string.Empty, + DisplayName = Resources.Property_NoneValue + })); } + + list.AddRange(snapshot.Items.Select(ToEnumValue)); + list.Sort(SortValues); // TODO: This is a hotfix for item ordering. Remove this when completing: https://github.com/dotnet/project-system/issues/7025 + return list; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SupportedValuesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SupportedValuesProvider.cs index 86a0845511..e967a694af 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SupportedValuesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/SupportedValuesProvider.cs @@ -6,136 +6,135 @@ using EnumCollection = System.Collections.Generic.ICollection; using EnumCollectionProjectValue = Microsoft.VisualStudio.ProjectSystem.IProjectVersionedValue>; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +/// +/// Abstract class for providers that process values from evaluation. +/// +internal abstract class SupportedValuesProvider : ChainedProjectValueDataSourceBase, IDynamicEnumValuesProvider, IDynamicEnumValuesGenerator { - /// - /// Abstract class for providers that process values from evaluation. - /// - internal abstract class SupportedValuesProvider : ChainedProjectValueDataSourceBase, IDynamicEnumValuesProvider, IDynamicEnumValuesGenerator - { - protected IProjectSubscriptionService SubscriptionService { get; } + protected IProjectSubscriptionService SubscriptionService { get; } - protected abstract string[] RuleNames { get; } + protected abstract string[] RuleNames { get; } - protected SupportedValuesProvider( - ConfiguredProject project, - IProjectSubscriptionService subscriptionService) - : base(project, synchronousDisposal: false, registerDataSource: false) - { - SubscriptionService = subscriptionService; - } + protected SupportedValuesProvider( + ConfiguredProject project, + IProjectSubscriptionService subscriptionService) + : base(project, synchronousDisposal: false, registerDataSource: false) + { + SubscriptionService = subscriptionService; + } - protected override IDisposable? LinkExternalInput(ITargetBlock targetBlock) - { - IProjectValueDataSource source = SubscriptionService.ProjectRuleSource; + protected override IDisposable? LinkExternalInput(ITargetBlock targetBlock) + { + IProjectValueDataSource source = SubscriptionService.ProjectRuleSource; - // Transform the values from evaluation to structure from the rule schema. - DisposableValue> transformBlock = source.SourceBlock.TransformWithNoDelta( - update => update.Derive(Transform), - suppressVersionOnlyUpdates: false, - ruleNames: RuleNames); + // Transform the values from evaluation to structure from the rule schema. + DisposableValue> transformBlock = source.SourceBlock.TransformWithNoDelta( + update => update.Derive(Transform), + suppressVersionOnlyUpdates: false, + ruleNames: RuleNames); - // Set the link up so that we publish changes to target block. - transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); + // Set the link up so that we publish changes to target block. + transformBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); - // Join the source blocks, so if they need to switch to UI thread to complete - // and someone is blocked on us on the same thread, the call proceeds. - JoinUpstreamDataSources(source); + // Join the source blocks, so if they need to switch to UI thread to complete + // and someone is blocked on us on the same thread, the call proceeds. + JoinUpstreamDataSources(source); - return transformBlock; - } + return transformBlock; + } - protected abstract EnumCollection Transform(IProjectSubscriptionUpdate input); + protected abstract EnumCollection Transform(IProjectSubscriptionUpdate input); - protected abstract int SortValues(IEnumValue a, IEnumValue b); + protected abstract int SortValues(IEnumValue a, IEnumValue b); - protected abstract IEnumValue ToEnumValue(KeyValuePair> item); + protected abstract IEnumValue ToEnumValue(KeyValuePair> item); - bool IDynamicEnumValuesGenerator.AllowCustomValues => false; + bool IDynamicEnumValuesGenerator.AllowCustomValues => false; - Task IDynamicEnumValuesGenerator.TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); + Task IDynamicEnumValuesGenerator.TryCreateEnumValueAsync(string userSuppliedValue) => TaskResult.Null(); - public Task GetProviderAsync(IList? options) - { - return Task.FromResult(this); - } + public Task GetProviderAsync(IList? options) + { + return Task.FromResult(this); + } - public async Task GetListedValuesAsync() + public async Task GetListedValuesAsync() + { + using (JoinableCollection.Join()) { - using (JoinableCollection.Join()) - { - EnumCollectionProjectValue snapshot = await SourceBlock.ReceiveAsync(); + EnumCollectionProjectValue snapshot = await SourceBlock.ReceiveAsync(); - return snapshot.Value; - } + return snapshot.Value; } + } + + protected sealed class NaturalStringComparer : IComparer + { + public static NaturalStringComparer Instance { get; } = new NaturalStringComparer(); - protected sealed class NaturalStringComparer : IComparer + public int Compare(string? x, string? y) { - public static NaturalStringComparer Instance { get; } = new NaturalStringComparer(); + // sort nulls to the start + if (x is null) + return y is null ? 0 : -1; + if (y is null) + return 1; + + var ix = 0; + var iy = 0; - public int Compare(string? x, string? y) + while (true) { - // sort nulls to the start - if (x is null) - return y is null ? 0 : -1; - if (y is null) + // sort shorter strings to the start + if (ix >= x.Length) + return iy >= y.Length ? 0 : -1; + if (iy >= y.Length) return 1; - var ix = 0; - var iy = 0; + var cx = x[ix]; + var cy = y[iy]; - while (true) - { - // sort shorter strings to the start - if (ix >= x.Length) - return iy >= y.Length ? 0 : -1; - if (iy >= y.Length) - return 1; + int result; + if (char.IsDigit(cx) && char.IsDigit(cy)) + result = CompareInteger(x, y, ref ix, ref iy); + else + result = cx.CompareTo(y[iy]); - var cx = x[ix]; - var cy = y[iy]; + if (result != 0) + return result; - int result; - if (char.IsDigit(cx) && char.IsDigit(cy)) - result = CompareInteger(x, y, ref ix, ref iy); - else - result = cx.CompareTo(y[iy]); - - if (result != 0) - return result; - - ix++; - iy++; - } + ix++; + iy++; } + } - private static int CompareInteger(string x, string y, ref int ix, ref int iy) - { - var lx = GetNumLength(x, ix); - var ly = GetNumLength(y, iy); - - // shorter number first (note, doesn't handle leading zeroes) - if (lx != ly) - return lx.CompareTo(ly); - - for (var i = 0; i < lx; i++) - { - var result = x[ix++].CompareTo(y[iy++]); - if (result != 0) - return result; - } + private static int CompareInteger(string x, string y, ref int ix, ref int iy) + { + var lx = GetNumLength(x, ix); + var ly = GetNumLength(y, iy); - return 0; - } + // shorter number first (note, doesn't handle leading zeroes) + if (lx != ly) + return lx.CompareTo(ly); - private static int GetNumLength(string s, int i) + for (var i = 0; i < lx; i++) { - var length = 0; - while (i < s.Length && char.IsDigit(s[i++])) - length++; - return length; + var result = x[ix++].CompareTo(y[iy++]); + if (result != 0) + return result; } + + return 0; + } + + private static int GetNumLength(string s, int i) + { + var length = 0; + while (i < s.Length && char.IsDigit(s[i++])) + length++; + return length; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/TemporaryPropertyStorage.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/TemporaryPropertyStorage.cs index 2eda55459e..8a052b6169 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/TemporaryPropertyStorage.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Properties/TemporaryPropertyStorage.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +[Export(typeof(ITemporaryPropertyStorage))] +internal sealed class TemporaryPropertyStorage : ITemporaryPropertyStorage { - [Export(typeof(ITemporaryPropertyStorage))] - internal sealed class TemporaryPropertyStorage : ITemporaryPropertyStorage - { - private ImmutableDictionary _properties = ImmutableDictionary.Empty; + private ImmutableDictionary _properties = ImmutableDictionary.Empty; - /// - /// We only need to force the creation of one of these per - /// . Otherwise we end up sharing them between - /// configurations/projects when we don't want to. - /// - [ImportingConstructor] - public TemporaryPropertyStorage(ConfiguredProject project) - { - } + /// + /// We only need to force the creation of one of these per + /// . Otherwise we end up sharing them between + /// configurations/projects when we don't want to. + /// + [ImportingConstructor] + public TemporaryPropertyStorage(ConfiguredProject project) + { + } - public void AddOrUpdatePropertyValue(string propertyName, string propertyValue) - { - ImmutableInterlocked.Update( - ref _properties, - static (dic, pair) => dic.SetItem(pair.Key, pair.Value), - (Key: propertyName, Value: propertyValue)); - } + public void AddOrUpdatePropertyValue(string propertyName, string propertyValue) + { + ImmutableInterlocked.Update( + ref _properties, + static (dic, pair) => dic.SetItem(pair.Key, pair.Value), + (Key: propertyName, Value: propertyValue)); + } - public string? GetPropertyValue(string propertyName) - { - return _properties.TryGetValue(propertyName, out string? value) ? value : null; - } + public string? GetPropertyValue(string propertyName) + { + return _properties.TryGetValue(propertyName, out string? value) ? value : null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Refactor/IRefactorNotifyService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Refactor/IRefactorNotifyService.cs index 007fe38eb9..783e265e27 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Refactor/IRefactorNotifyService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Refactor/IRefactorNotifyService.cs @@ -1,11 +1,10 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Refactor +namespace Microsoft.VisualStudio.ProjectSystem.Refactor; + +internal interface IRefactorNotifyService { - internal interface IRefactorNotifyService - { - void OnBeforeGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName); + void OnBeforeGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName); - void OnAfterGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName); - } + void OnAfterGlobalSymbolRenamed(string projectPath, IEnumerable filePaths, string rqName, string newName); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/References/AlwaysAllowValidProjectReferenceChecker.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/References/AlwaysAllowValidProjectReferenceChecker.cs index bf795765d9..dc8338b385 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/References/AlwaysAllowValidProjectReferenceChecker.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/References/AlwaysAllowValidProjectReferenceChecker.cs @@ -1,55 +1,54 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.References +namespace Microsoft.VisualStudio.ProjectSystem.References; + +/// +/// Provides an implementation of that always allows a reference. +/// +/// +/// Unlike the old project system, we do zero validation when we get added as a project reference, or when +/// we reference another project. Instead, we push off all validation to MSBuild targets that do the validation +/// during builds and design-time builds (ResolveProjectReferences). This gives a consistent behavior between +/// adding the project reference via the UI and adding the project via the editor - in the end they result in the +/// same behavior, inside and outside of Visual Studio. +/// +[Export(typeof(IValidProjectReferenceChecker))] +[AppliesTo(ProjectCapability.DotNet)] +[Order(Order.Default)] // Before the default checker, which delegates onto normal P-2-P rules +internal class AlwaysAllowValidProjectReferenceChecker : IValidProjectReferenceChecker { - /// - /// Provides an implementation of that always allows a reference. - /// - /// - /// Unlike the old project system, we do zero validation when we get added as a project reference, or when - /// we reference another project. Instead, we push off all validation to MSBuild targets that do the validation - /// during builds and design-time builds (ResolveProjectReferences). This gives a consistent behavior between - /// adding the project reference via the UI and adding the project via the editor - in the end they result in the - /// same behavior, inside and outside of Visual Studio. - /// - [Export(typeof(IValidProjectReferenceChecker))] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] // Before the default checker, which delegates onto normal P-2-P rules - internal class AlwaysAllowValidProjectReferenceChecker : IValidProjectReferenceChecker - { - private static readonly Task s_supported = Task.FromResult(SupportedCheckResult.Supported); + private static readonly Task s_supported = Task.FromResult(SupportedCheckResult.Supported); - [ImportingConstructor] - public AlwaysAllowValidProjectReferenceChecker() - { - } - - public Task CanAddProjectReferenceAsync(object referencedProject) - { - Requires.NotNull(referencedProject); + [ImportingConstructor] + public AlwaysAllowValidProjectReferenceChecker() + { + } - return s_supported; - } + public Task CanAddProjectReferenceAsync(object referencedProject) + { + Requires.NotNull(referencedProject); - public Task CanAddProjectReferencesAsync(IImmutableSet referencedProjects) - { - Requires.NotNullEmptyOrNullElements(referencedProjects); + return s_supported; + } - IImmutableDictionary results = ImmutableDictionary.Create(); + public Task CanAddProjectReferencesAsync(IImmutableSet referencedProjects) + { + Requires.NotNullEmptyOrNullElements(referencedProjects); - foreach (object referencedProject in referencedProjects) - { - results = results.Add(referencedProject, SupportedCheckResult.Supported); - } + IImmutableDictionary results = ImmutableDictionary.Create(); - return Task.FromResult(new CanAddProjectReferencesResult(results, null)); + foreach (object referencedProject in referencedProjects) + { + results = results.Add(referencedProject, SupportedCheckResult.Supported); } - public Task CanBeReferencedAsync(object referencingProject) - { - Requires.NotNull(referencingProject); + return Task.FromResult(new CanAddProjectReferencesResult(results, null)); + } - return s_supported; - } + public Task CanBeReferencedAsync(object referencingProject) + { + Requires.NotNull(referencingProject); + + return s_supported; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/AppDesigner.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/AppDesigner.xaml.cs index 029cf03c3f..aed03c5ccd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/AppDesigner.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/AppDesigner.xaml.cs @@ -2,12 +2,11 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class AppDesigner { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class AppDesigner - { - internal static string[] SchemaNameArray = new string[] { SchemaName }; - } + internal static string[] SchemaNameArray = new string[] { SchemaName }; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedFrameworkReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedFrameworkReference.cs index f7796c4a51..92ce52fe13 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedFrameworkReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedFrameworkReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CollectedFrameworkReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CollectedFrameworkReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedNuGetAuditSuppressions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedNuGetAuditSuppressions.cs index f6b4bbe993..a5efa56dc8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedNuGetAuditSuppressions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedNuGetAuditSuppressions.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CollectedNuGetAuditSuppressions { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CollectedNuGetAuditSuppressions - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageDownload.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageDownload.cs index c4d22fffae..1eeb86d845 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageDownload.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageDownload.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CollectedPackageDownload { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CollectedPackageDownload - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageReference.cs index c06804892f..92b4499ae9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CollectedPackageReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CollectedPackageReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageVersion.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageVersion.cs index 540b31d14e..5d2ea338b1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageVersion.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPackageVersion.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CollectedPackageVersion { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CollectedPackageVersion - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPrunePackageReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPrunePackageReference.cs index 37171966b4..88bbdb9a95 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPrunePackageReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CollectedPrunePackageReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CollectedPrunePackageReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CollectedPrunePackageReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CompilerCommandLineArgs.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CompilerCommandLineArgs.cs index 8fcc831485..0584f70384 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CompilerCommandLineArgs.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/CompilerCommandLineArgs.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class CompilerCommandLineArgs { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class CompilerCommandLineArgs - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml.cs index 0e831ff9ed..339ca0affc 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfigurationGeneral.xaml.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ConfigurationGeneral { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ConfigurationGeneral - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfiguredBrowseObject.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfiguredBrowseObject.cs index cbed179a3b..79c504fa25 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfiguredBrowseObject.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ConfiguredBrowseObject.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ConfiguredBrowseObject { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ConfiguredBrowseObject - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AnalyzerReference.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AnalyzerReference.xaml.cs index e36630d8df..3c6251afd4 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AnalyzerReference.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AnalyzerReference.xaml.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class AnalyzerReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class AnalyzerReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AssemblyReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AssemblyReference.cs index 8e66bf4385..f6e6974067 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AssemblyReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/AssemblyReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class AssemblyReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class AssemblyReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/COMReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/COMReference.cs index 9b1ca3bced..5929230579 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/COMReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/COMReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ComReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ComReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/FrameworkReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/FrameworkReference.cs index 59137ab9a5..8497def22d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/FrameworkReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/FrameworkReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class FrameworkReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class FrameworkReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/PackageReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/PackageReference.cs index 8c0fc93762..2d4f1968cf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/PackageReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/PackageReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class PackageReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class PackageReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ProjectReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ProjectReference.cs index c3ebc8a4ea..a23507fe30 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ProjectReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ProjectReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ProjectReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ProjectReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAnalyzerReference.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAnalyzerReference.xaml.cs index 248dc148d0..2c48cd6d2f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAnalyzerReference.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAnalyzerReference.xaml.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedAnalyzerReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedAnalyzerReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAssemblyReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAssemblyReference.cs index ce2dcefcad..76348c384e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAssemblyReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedAssemblyReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedAssemblyReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedAssemblyReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedCOMReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedCOMReference.cs index 7208cd90cd..3ae696e1e8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedCOMReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedCOMReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedCOMReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedCOMReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedFrameworkReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedFrameworkReference.cs index 526437fb99..84cea9de7a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedFrameworkReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedFrameworkReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedFrameworkReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedFrameworkReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedPackageReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedPackageReference.cs index a63777aa0d..c274538b1f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedPackageReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedPackageReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedPackageReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedPackageReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedProjectReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedProjectReference.cs index 3de2befdc5..de9b9f7a55 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedProjectReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedProjectReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedProjectReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedProjectReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedSdkReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedSdkReference.cs index b8659e3bf5..c0aad74713 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedSdkReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/ResolvedSdkReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ResolvedSdkReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ResolvedSdkReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/SdkReference.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/SdkReference.xaml.cs index 822390eee2..00b8dfefbf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/SdkReference.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Dependencies/SdkReference.xaml.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SdkReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SdkReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/DotNetCliToolReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/DotNetCliToolReference.cs index ed42e3e1ca..ce774fbce7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/DotNetCliToolReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/DotNetCliToolReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class DotNetCliToolReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class DotNetCliToolReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/EvaluatedProjectReference.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/EvaluatedProjectReference.cs index 7a56ba44a6..e464fb8df3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/EvaluatedProjectReference.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/EvaluatedProjectReference.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class EvaluatedProjectReference { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class EvaluatedProjectReference - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ExportRuleAttribute.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ExportRuleAttribute.cs index b9192f57cd..3403dad662 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ExportRuleAttribute.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ExportRuleAttribute.cs @@ -2,38 +2,37 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.Rules +namespace Microsoft.VisualStudio.ProjectSystem.Rules; + +/// +/// Exports a XAML-based embedded rule. +/// +[AttributeUsage(AttributeTargets.Field)] +internal sealed class ExportRuleAttribute : ExportPropertyXamlRuleDefinitionAttribute { + // TODO: If reflection is insufficient, this will also work. + //private const string AssemblyFullName = $"{ThisAssembly.AssemblyName}, Version = {ThisAssembly.AssemblyVersion}, Culture = neutral, PublicKeyToken = {ThisAssembly.PublicKeyToken}"; + /// - /// Exports a XAML-based embedded rule. + /// Initializes the class with the specified rule name and context. /// - [AttributeUsage(AttributeTargets.Field)] - internal sealed class ExportRuleAttribute : ExportPropertyXamlRuleDefinitionAttribute + /// + /// The name of the rule without '.xaml', for example, 'ConfigurationGeneral'. + /// + /// + /// One or more of . + /// + public ExportRuleAttribute(string ruleName, params string[] contexts) + : base(typeof(ExportRuleAttribute).Assembly.FullName, $"XamlRuleToCode:{ruleName}.xaml", string.Join(";", contexts)) { - // TODO: If reflection is insufficient, this will also work. - //private const string AssemblyFullName = $"{ThisAssembly.AssemblyName}, Version = {ThisAssembly.AssemblyVersion}, Culture = neutral, PublicKeyToken = {ThisAssembly.PublicKeyToken}"; - - /// - /// Initializes the class with the specified rule name and context. - /// - /// - /// The name of the rule without '.xaml', for example, 'ConfigurationGeneral'. - /// - /// - /// One or more of . - /// - public ExportRuleAttribute(string ruleName, params string[] contexts) - : base(typeof(ExportRuleAttribute).Assembly.FullName, $"XamlRuleToCode:{ruleName}.xaml", string.Join(";", contexts)) - { - RuleName = ruleName; - } + RuleName = ruleName; + } - /// - /// Gets the name of the rule without '.xaml', for example, 'ConfigurationGeneral'. - /// - public string RuleName - { - get; - } + /// + /// Gets the name of the rule without '.xaml', for example, 'ConfigurationGeneral'. + /// + public string RuleName + { + get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/GeneralBrowseObject.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/GeneralBrowseObject.cs index ed928d8640..f0bcbab012 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/GeneralBrowseObject.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/GeneralBrowseObject.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ConfigurationGeneralBrowseObject { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ConfigurationGeneralBrowseObject - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/AdditionalFiles.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/AdditionalFiles.cs index 1c148ef7e5..f2af05cf45 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/AdditionalFiles.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/AdditionalFiles.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class AdditionalFiles { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class AdditionalFiles - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Compile.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Compile.cs index 9ff0ee5daf..c585ba8732 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Compile.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Compile.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class Compile { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class Compile - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Content.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Content.cs index 23b807ee65..69d7a436de 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Content.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Content.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class Content { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class Content - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Folder.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Folder.cs index 6d4a433d94..029121f1a2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Folder.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Folder.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class Folder { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class Folder - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/None.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/None.cs index 4559bd122b..de68bbf52d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/None.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/None.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class None { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class None - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Resource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Resource.cs index 5d38d689ee..25e4250a2f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Resource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/Items/Resource.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class Resource { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class Resource - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/LanguageService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/LanguageService.cs index da6701d6f9..558952e84c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/LanguageService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/LanguageService.cs @@ -2,14 +2,13 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Models properties to be sent to the language service. +/// +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class LanguageService { - /// - /// Models properties to be sent to the language service. - /// - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class LanguageService - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/NuGetRestore.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/NuGetRestore.cs index 5ce624c4d2..8a0d0428ef 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/NuGetRestore.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/NuGetRestore.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class NuGetRestore { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class NuGetRestore - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectDebugger.xaml.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectDebugger.xaml.cs index a4bceba3be..80933003a5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectDebugger.xaml.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectDebugger.xaml.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class ProjectDebugger { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class ProjectDebugger - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectProperties.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectProperties.cs index 299a28bf82..71ee63fca2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectProperties.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/ProjectProperties.cs @@ -4,52 +4,51 @@ using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides rule-based property access. +/// +[Export] +[ExcludeFromCodeCoverage] +[ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal partial class ProjectProperties : StronglyTypedPropertyAccess { /// - /// Provides rule-based property access. + /// Initializes a new instance of the class. /// - [Export] - [ExcludeFromCodeCoverage] - [ProjectSystemContract(ProjectSystemContractScope.ConfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal partial class ProjectProperties : StronglyTypedPropertyAccess + [ImportingConstructor] + public ProjectProperties([Import] ConfiguredProject configuredProject) + : base(configuredProject) { - /// - /// Initializes a new instance of the class. - /// - [ImportingConstructor] - public ProjectProperties([Import] ConfiguredProject configuredProject) - : base(configuredProject) - { - } + } - /// - /// Initializes a new instance of the class. - /// - public ProjectProperties(ConfiguredProject configuredProject, string file, string itemType, string itemName) - : base(configuredProject, file, itemType, itemName) - { - } + /// + /// Initializes a new instance of the class. + /// + public ProjectProperties(ConfiguredProject configuredProject, string file, string itemType, string itemName) + : base(configuredProject, file, itemType, itemName) + { + } - /// - /// Initializes a new instance of the class. - /// - public ProjectProperties(ConfiguredProject configuredProject, IProjectPropertiesContext projectPropertiesContext) - : base(configuredProject, projectPropertiesContext) - { - } + /// + /// Initializes a new instance of the class. + /// + public ProjectProperties(ConfiguredProject configuredProject, IProjectPropertiesContext projectPropertiesContext) + : base(configuredProject, projectPropertiesContext) + { + } - /// - /// Initializes a new instance of the class. - /// - public ProjectProperties(ConfiguredProject configuredProject, UnconfiguredProject project) - : base(configuredProject, project) - { - } + /// + /// Initializes a new instance of the class. + /// + public ProjectProperties(ConfiguredProject configuredProject, UnconfiguredProject project) + : base(configuredProject, project) + { + } - public new ConfiguredProject ConfiguredProject - { - get { return base.ConfiguredProject; } - } + public new ConfiguredProject ConfiguredProject + { + get { return base.ConfiguredProject; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/RuleExporter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/RuleExporter.cs index 2f8624160b..0570786ec9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/RuleExporter.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/RuleExporter.cs @@ -4,228 +4,227 @@ #pragma warning disable 0649 -namespace Microsoft.VisualStudio.ProjectSystem.Rules +namespace Microsoft.VisualStudio.ProjectSystem.Rules; + +/// +/// Responsible for exporting our embedded rules so that CPS can pick them. +/// +internal static class RuleExporter { + private static class ProjectRules + { + /// + /// Represents the evaluation properties representing source control bindings, + /// typically used in projects connected to Team Foundation Source Control. + /// + [ExportRule(nameof(SourceControl), PropertyPageContexts.Invisible)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SourceControlRule; + + /// + /// Represents the evaluation items containing the supported (possible) target frameworks + /// for a project. + /// + [ExportRule(nameof(SupportedTargetFrameworkAlias), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SupportedTargetFrameworkAliasRule; + + /// + /// Represents the evaluation items containing the supported (possible) target frameworks + /// for a project. + /// + [ExportRule(nameof(SupportedTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SupportedTargetFrameworkRule; + + /// + /// Represents the evaluation items containing the supported (possible) .NET Core target frameworks + /// for a project. + /// + [ExportRule(nameof(SupportedNETCoreAppTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SupportedNETCoreAppTargetFrameworkRule; + + /// + /// Represents the evaluation items containing the supported (possible) .NET Framework target frameworks + /// for a project. + /// + [ExportRule(nameof(SupportedNETFrameworkTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SupportedNETFrameworkTargetFrameworkRule; + + /// + /// Represents the evaluation items containing the supported (possible) .NET Standard target frameworks + /// for a project. + /// + [ExportRule(nameof(SupportedNETStandardTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SupportedNETStandardTargetFrameworkRule; + + /// + /// Represents the evaluation items containing the supported (possible) target platforms + /// for a project. + /// + [ExportRule(nameof(SdkSupportedTargetPlatformIdentifier), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SdkSupportedTargetPlatformIdentifierRule; + + /// + /// Represents the evaluation items containing the supported (possible) target platforms + /// versions for a project. + /// + [ExportRule(nameof(SdkSupportedTargetPlatformVersion), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SdkSupportedTargetPlatformVersionRule; + + /// + /// Represents the evaluation properties containing the general configuration for a project. + /// + [ExportRule(nameof(ConfigurationGeneral), PropertyPageContexts.Project, PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int ConfigurationGeneralRule; + } + + private static class AppDesignerRules + { + /// + /// Represents the evaluation properties that is used for AppDesigner folder services. + /// + [ExportRule(nameof(AppDesigner), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.AppDesigner)] + [Order(Order.Default)] + public static int AppDesignerRule; + } + /// - /// Responsible for exporting our embedded rules so that CPS can pick them. + /// Contains rules for the PackageRestoreDataSource. /// - internal static class RuleExporter + private static class PackageRestoreRules { - private static class ProjectRules - { - /// - /// Represents the evaluation properties representing source control bindings, - /// typically used in projects connected to Team Foundation Source Control. - /// - [ExportRule(nameof(SourceControl), PropertyPageContexts.Invisible)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SourceControlRule; - - /// - /// Represents the evaluation items containing the supported (possible) target frameworks - /// for a project. - /// - [ExportRule(nameof(SupportedTargetFrameworkAlias), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SupportedTargetFrameworkAliasRule; - - /// - /// Represents the evaluation items containing the supported (possible) target frameworks - /// for a project. - /// - [ExportRule(nameof(SupportedTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SupportedTargetFrameworkRule; - - /// - /// Represents the evaluation items containing the supported (possible) .NET Core target frameworks - /// for a project. - /// - [ExportRule(nameof(SupportedNETCoreAppTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SupportedNETCoreAppTargetFrameworkRule; - - /// - /// Represents the evaluation items containing the supported (possible) .NET Framework target frameworks - /// for a project. - /// - [ExportRule(nameof(SupportedNETFrameworkTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SupportedNETFrameworkTargetFrameworkRule; - - /// - /// Represents the evaluation items containing the supported (possible) .NET Standard target frameworks - /// for a project. - /// - [ExportRule(nameof(SupportedNETStandardTargetFramework), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SupportedNETStandardTargetFrameworkRule; - - /// - /// Represents the evaluation items containing the supported (possible) target platforms - /// for a project. - /// - [ExportRule(nameof(SdkSupportedTargetPlatformIdentifier), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SdkSupportedTargetPlatformIdentifierRule; - - /// - /// Represents the evaluation items containing the supported (possible) target platforms - /// versions for a project. - /// - [ExportRule(nameof(SdkSupportedTargetPlatformVersion), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SdkSupportedTargetPlatformVersionRule; - - /// - /// Represents the evaluation properties containing the general configuration for a project. - /// - [ExportRule(nameof(ConfigurationGeneral), PropertyPageContexts.Project, PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int ConfigurationGeneralRule; - } - - private static class AppDesignerRules - { - /// - /// Represents the evaluation properties that is used for AppDesigner folder services. - /// - [ExportRule(nameof(AppDesigner), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.AppDesigner)] - [Order(Order.Default)] - public static int AppDesignerRule; - } - - /// - /// Contains rules for the PackageRestoreDataSource. - /// - private static class PackageRestoreRules - { - /// - /// Represents the evaluated ProjectReference items that are passed to restore. - /// - [ExportRule(nameof(EvaluatedProjectReference), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapabilities.PackageReferences)] - [Order(Order.Default)] - public static int EvaluatedProjectReferenceRule; - - /// - /// Represents the design-time build items containing CLI tool references (legacy) that are passed to restore. - /// - [ExportRule(nameof(DotNetCliToolReference), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int DotNetCliToolReferenceRule; - - /// - /// Represents the design-time build items containing references to frameworks that are passed to restore. - /// - [ExportRule(nameof(CollectedFrameworkReference), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int CollectedFrameworkReferenceRule; - - /// - /// Represents the design-time build items containing packages to be downloaded that are passed to restore. - /// - [ExportRule(nameof(CollectedPackageDownload), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int CollectedPackageDownloadRule; - - /// - /// Represents the design-time build items containing the packages that the project references that are passed to restore. - /// - [ExportRule(nameof(CollectedPackageReference), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int CollectedPackageReferenceRule; - - /// - /// Represents the design-time build items containing the versions of direct and indirect package references that are passed to restore. - /// - [ExportRule(nameof(CollectedPackageVersion), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int CollectedPackageVersionRule; - - /// - /// Represents the design-time build items containing the versions of direct and indirect package references that are passed to restore. - /// - [ExportRule(nameof(CollectedNuGetAuditSuppressions), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int CollectedNuGetAuditSuppressionsRule; - - /// - /// Represents the design-time build items containing the versions of packages to be pruned that are passed to restore. - /// - [ExportRule(nameof(CollectedPrunePackageReference), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int CollectedPrunePackageReferencesRule; - - /// - /// Represents the evaluation properties that are passed that are passed to restore. - /// - [ExportRule(nameof(NuGetRestore), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.PackageReferences)] - [Order(Order.Default)] - public static int NuGetRestoreRule; - } - - /// - /// Contains rules for the language service implementations (e.g. implementations of IWorkspaceUpdateHandler). - /// - private static class LanguageServiceRules - { - /// - /// Represents the design-time build items containing the compiler command-line that is passed to Roslyn. - /// - [ExportRule(nameof(CompilerCommandLineArgs), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNetLanguageService)] - [Order(Order.Default)] - public static int CompilerCommandLineArgsRule; - - /// - /// Represents the evaluation properties that are passed to Roslyn. - /// - [ExportRule(nameof(LanguageService), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNetLanguageService)] - [Order(Order.Default)] - public static int LanguageServiceRule; - } - - /// - /// Contains rules for the Windows Forms designer component. - /// - private static class WindowsFormsConfigurationRules - { - [ExportRule(nameof(WindowsFormsConfiguration), PropertyPageContexts.Project)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int WindowsFormsConfigurationRule; - } - - private static class InProductAcquisitionRules - { - /// - /// Represents items that indicate which Visual Studio components should - /// be installed for the project to work correctly. - /// - [ExportRule(nameof(SuggestedVisualStudioComponentId), PropertyPageContexts.ProjectSubscriptionService)] - [AppliesTo(ProjectCapability.DotNet)] - [Order(Order.Default)] - public static int SuggestedVisualStudioComponentIdRule; - } + /// + /// Represents the evaluated ProjectReference items that are passed to restore. + /// + [ExportRule(nameof(EvaluatedProjectReference), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapabilities.PackageReferences)] + [Order(Order.Default)] + public static int EvaluatedProjectReferenceRule; + + /// + /// Represents the design-time build items containing CLI tool references (legacy) that are passed to restore. + /// + [ExportRule(nameof(DotNetCliToolReference), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int DotNetCliToolReferenceRule; + + /// + /// Represents the design-time build items containing references to frameworks that are passed to restore. + /// + [ExportRule(nameof(CollectedFrameworkReference), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int CollectedFrameworkReferenceRule; + + /// + /// Represents the design-time build items containing packages to be downloaded that are passed to restore. + /// + [ExportRule(nameof(CollectedPackageDownload), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int CollectedPackageDownloadRule; + + /// + /// Represents the design-time build items containing the packages that the project references that are passed to restore. + /// + [ExportRule(nameof(CollectedPackageReference), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int CollectedPackageReferenceRule; + + /// + /// Represents the design-time build items containing the versions of direct and indirect package references that are passed to restore. + /// + [ExportRule(nameof(CollectedPackageVersion), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int CollectedPackageVersionRule; + + /// + /// Represents the design-time build items containing the versions of direct and indirect package references that are passed to restore. + /// + [ExportRule(nameof(CollectedNuGetAuditSuppressions), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int CollectedNuGetAuditSuppressionsRule; + + /// + /// Represents the design-time build items containing the versions of packages to be pruned that are passed to restore. + /// + [ExportRule(nameof(CollectedPrunePackageReference), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int CollectedPrunePackageReferencesRule; + + /// + /// Represents the evaluation properties that are passed that are passed to restore. + /// + [ExportRule(nameof(NuGetRestore), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.PackageReferences)] + [Order(Order.Default)] + public static int NuGetRestoreRule; + } + + /// + /// Contains rules for the language service implementations (e.g. implementations of IWorkspaceUpdateHandler). + /// + private static class LanguageServiceRules + { + /// + /// Represents the design-time build items containing the compiler command-line that is passed to Roslyn. + /// + [ExportRule(nameof(CompilerCommandLineArgs), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNetLanguageService)] + [Order(Order.Default)] + public static int CompilerCommandLineArgsRule; + + /// + /// Represents the evaluation properties that are passed to Roslyn. + /// + [ExportRule(nameof(LanguageService), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNetLanguageService)] + [Order(Order.Default)] + public static int LanguageServiceRule; + } + + /// + /// Contains rules for the Windows Forms designer component. + /// + private static class WindowsFormsConfigurationRules + { + [ExportRule(nameof(WindowsFormsConfiguration), PropertyPageContexts.Project)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int WindowsFormsConfigurationRule; + } + + private static class InProductAcquisitionRules + { + /// + /// Represents items that indicate which Visual Studio components should + /// be installed for the project to work correctly. + /// + [ExportRule(nameof(SuggestedVisualStudioComponentId), PropertyPageContexts.ProjectSubscriptionService)] + [AppliesTo(ProjectCapability.DotNet)] + [Order(Order.Default)] + public static int SuggestedVisualStudioComponentIdRule; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformIdentifier.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformIdentifier.cs index 6261e94c75..9189f43a3c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformIdentifier.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformIdentifier.cs @@ -2,10 +2,9 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SdkSupportedTargetPlatformIdentifier { - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SdkSupportedTargetPlatformIdentifier - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformVersion.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformVersion.cs index e44d68e63e..d3dc7c435f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformVersion.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SdkSupportedTargetPlatformVersion.cs @@ -2,10 +2,9 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SdkSupportedTargetPlatformVersion { - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SdkSupportedTargetPlatformVersion - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETCoreAppTargetFramework.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETCoreAppTargetFramework.cs index 2278157e32..ecc184dc9e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETCoreAppTargetFramework.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETCoreAppTargetFramework.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SupportedNETCoreAppTargetFramework { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SupportedNETCoreAppTargetFramework - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETFrameworkTargetFramework.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETFrameworkTargetFramework.cs index c21cbae2ca..753820529a 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETFrameworkTargetFramework.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETFrameworkTargetFramework.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SupportedNETFrameworkTargetFramework { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SupportedNETFrameworkTargetFramework - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETStandardTargetFramework.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETStandardTargetFramework.cs index 3d44925a91..8ca996b426 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETStandardTargetFramework.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedNETStandardTargetFramework.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SupportedNETStandardTargetFramework { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SupportedNETStandardTargetFramework - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFramework.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFramework.cs index 000214803d..ed24c621f2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFramework.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFramework.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SupportedTargetFramework { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SupportedTargetFramework - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFrameworkAlias.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFrameworkAlias.cs index a9d5bfb6ce..1fabf459c8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFrameworkAlias.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/SupportedTargetFrameworkAlias.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class SupportedTargetFrameworkAlias { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class SupportedTargetFrameworkAlias - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/WindowsFormsConfiguration.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/WindowsFormsConfiguration.cs index 04f07a9d7a..c5b75409ca 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/WindowsFormsConfiguration.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Rules/WindowsFormsConfiguration.cs @@ -2,11 +2,10 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[ExcludeFromCodeCoverage] +[SuppressMessage("Style", "IDE0016:Use 'throw' expression")] +internal partial class WindowsFormsConfiguration { - [ExcludeFromCodeCoverage] - [SuppressMessage("Style", "IDE0016:Use 'throw' expression")] - internal partial class WindowsFormsConfiguration - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ServiceCapability.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ServiceCapability.cs index ac46179842..7e680d87ce 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ServiceCapability.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/ServiceCapability.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Well-known capabilities. +/// +internal static class ServiceCapability { /// - /// Well-known capabilities. + /// Indicates that CPS has its diagnostic runtime enabled. /// - internal static class ServiceCapability - { - /// - /// Indicates that CPS has its diagnostic runtime enabled. - /// - public const string DiagnosticRuntimeServiceCapability = "Microsoft.VisualStudio.ProjectSystem.DiagnosticRuntime"; - } + public const string DiagnosticRuntimeServiceCapability = "Microsoft.VisualStudio.ProjectSystem.DiagnosticRuntime"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProvider.cs index 857cd162ff..d1ddd9cd6f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProvider.cs @@ -1,42 +1,41 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides the base class for instances +/// that handle the WPF Application Definition file. +/// +internal abstract class AbstractAppXamlSpecialFileProvider : AbstractFindByNameSpecialFileProvider { - /// - /// Provides the base class for instances - /// that handle the WPF Application Definition file. - /// - internal abstract class AbstractAppXamlSpecialFileProvider : AbstractFindByNameSpecialFileProvider + protected AbstractAppXamlSpecialFileProvider(string fileName, IPhysicalProjectTree projectTree) + : base(fileName, projectTree) { - protected AbstractAppXamlSpecialFileProvider(string fileName, IPhysicalProjectTree projectTree) - : base(fileName, projectTree) - { - } + } - protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) + protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + // First look for the actual App.xaml first + IProjectTree? node = FindAppXamlFile(root); + if (node is null) { - // First look for the actual App.xaml first - IProjectTree? node = FindAppXamlFile(root); - if (node is null) - { - // Otherwise, find a candidate that we might be able to add to the project - node = await base.FindFileAsync(provider, root); - } - - return node; + // Otherwise, find a candidate that we might be able to add to the project + node = await base.FindFileAsync(provider, root); } - private static IProjectTree? FindAppXamlFile(IProjectTree root) + return node; + } + + private static IProjectTree? FindAppXamlFile(IProjectTree root) + { + foreach (IProjectItemTree item in root.GetSelfAndDescendentsBreadthFirst().OfType()) { - foreach (IProjectItemTree item in root.GetSelfAndDescendentsBreadthFirst().OfType()) + if (StringComparers.ItemTypes.Equals(item.Item?.ItemType, "ApplicationDefinition")) { - if (StringComparers.ItemTypes.Equals(item.Item?.ItemType, "ApplicationDefinition")) - { - return item; - } + return item; } - - return null; } + + return null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProvider.cs index ab7b263f58..375c02f672 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProvider.cs @@ -2,43 +2,42 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides base class for instances +/// that find their special file by file name in the root of the project. +/// +internal abstract class AbstractFindByNameSpecialFileProvider : AbstractSpecialFileProvider { - /// - /// Provides base class for instances - /// that find their special file by file name in the root of the project. - /// - internal abstract class AbstractFindByNameSpecialFileProvider : AbstractSpecialFileProvider - { - private readonly string _fileName; + private readonly string _fileName; - protected AbstractFindByNameSpecialFileProvider(string fileName, IPhysicalProjectTree projectTree) - : base(projectTree) - { - _fileName = fileName; - } + protected AbstractFindByNameSpecialFileProvider(string fileName, IPhysicalProjectTree projectTree) + : base(projectTree) + { + _fileName = fileName; + } - protected override Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) - { - root.TryFindImmediateChild(_fileName, out IProjectTree? node); + protected override Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + root.TryFindImmediateChild(_fileName, out IProjectTree? node); - return Task.FromResult(node); - } + return Task.FromResult(node); + } - protected override Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root) - { - string? projectPath = provider.GetRootedAddNewItemDirectory(root); - if (projectPath is null) // Root has DisableAddItem - return TaskResult.Null(); + protected override Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + string? projectPath = provider.GetRootedAddNewItemDirectory(root); + if (projectPath is null) // Root has DisableAddItem + return TaskResult.Null(); - string path = Path.Combine(projectPath, _fileName); + string path = Path.Combine(projectPath, _fileName); - return Task.FromResult(path); - } + return Task.FromResult(path); + } - protected string? GetDefaultFile(string rootPath) - { - return Path.Combine(rootPath, _fileName); - } + protected string? GetDefaultFile(string rootPath) + { + return Path.Combine(rootPath, _fileName); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProvider.cs index 27858ea02c..ea447606ab 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProvider.cs @@ -1,83 +1,82 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides base class for instances +/// that find their special file by file name under the AppDesigner folder, falling back +/// to the root folder if it doesn't exist. +/// +internal abstract class AbstractFindByNameUnderAppDesignerSpecialFileProvider : AbstractFindByNameSpecialFileProvider { - /// - /// Provides base class for instances - /// that find their special file by file name under the AppDesigner folder, falling back - /// to the root folder if it doesn't exist. - /// - internal abstract class AbstractFindByNameUnderAppDesignerSpecialFileProvider : AbstractFindByNameSpecialFileProvider + private readonly ISpecialFilesManager _specialFilesManager; + + protected AbstractFindByNameUnderAppDesignerSpecialFileProvider(string fileName, ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + : base(fileName, projectTree) { - private readonly ISpecialFilesManager _specialFilesManager; + _specialFilesManager = specialFilesManager; + } - protected AbstractFindByNameUnderAppDesignerSpecialFileProvider(string fileName, ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - : base(fileName, projectTree) + protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + // Search AppDesigner folder first if it exists + IProjectTree? appDesignerFolder = await GetAppDesignerFolderAsync(provider, root); + if (appDesignerFolder is not null) { - _specialFilesManager = specialFilesManager; + IProjectTree? node = await base.FindFileAsync(provider, appDesignerFolder); + if (node is not null) + return node; } - protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) - { - // Search AppDesigner folder first if it exists - IProjectTree? appDesignerFolder = await GetAppDesignerFolderAsync(provider, root); - if (appDesignerFolder is not null) - { - IProjectTree? node = await base.FindFileAsync(provider, appDesignerFolder); - if (node is not null) - return node; - } - - // Then fallback to project root - return await base.FindFileAsync(provider, root); - } + // Then fallback to project root + return await base.FindFileAsync(provider, root); + } - protected override async Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root) + protected override async Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + // AppDesigner folder first if it exists + string? appDesignerPath = await GetAppDesignerFolderPathAsync(); + if (appDesignerPath is not null) { - // AppDesigner folder first if it exists - string? appDesignerPath = await GetAppDesignerFolderPathAsync(); - if (appDesignerPath is not null) - { - return GetDefaultFile(appDesignerPath); - } - - // Then fallback to project root - return await base.GetDefaultFileAsync(provider, root); + return GetDefaultFile(appDesignerPath); } - private async Task GetAppDesignerFolderAsync(IProjectTreeProvider provider, IProjectTree root) - { - string? appDesignerPath = await GetAppDesignerFolderPathAsync(); - if (appDesignerPath is null) - return null; + // Then fallback to project root + return await base.GetDefaultFileAsync(provider, root); + } - return provider.FindByPath(root, appDesignerPath); - } + private async Task GetAppDesignerFolderAsync(IProjectTreeProvider provider, IProjectTree root) + { + string? appDesignerPath = await GetAppDesignerFolderPathAsync(); + if (appDesignerPath is null) + return null; - private Task GetAppDesignerFolderPathAsync(bool createIfNotExists = false) - { - SpecialFileFlags flags = SpecialFileFlags.FullPath; - if (createIfNotExists) - flags |= SpecialFileFlags.CreateIfNotExist; + return provider.FindByPath(root, appDesignerPath); + } - return _specialFilesManager.GetFileAsync(SpecialFiles.AppDesigner, flags); - } + private Task GetAppDesignerFolderPathAsync(bool createIfNotExists = false) + { + SpecialFileFlags flags = SpecialFileFlags.FullPath; + if (createIfNotExists) + flags |= SpecialFileFlags.CreateIfNotExist; - protected sealed override async Task CreateFileAsync(string path) - { - await EnsureAppDesignerFolderAsync(); + return _specialFilesManager.GetFileAsync(SpecialFiles.AppDesigner, flags); + } - await CreateFileCoreAsync(path); - } + protected sealed override async Task CreateFileAsync(string path) + { + await EnsureAppDesignerFolderAsync(); - protected virtual Task CreateFileCoreAsync(string path) - { - return base.CreateFileAsync(path); - } + await CreateFileCoreAsync(path); + } - private Task EnsureAppDesignerFolderAsync() - { - return GetAppDesignerFolderPathAsync(createIfNotExists: true); - } + protected virtual Task CreateFileCoreAsync(string path) + { + return base.CreateFileAsync(path); + } + + private Task EnsureAppDesignerFolderAsync() + { + return GetAppDesignerFolderPathAsync(createIfNotExists: true); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractSpecialFileProvider.cs index 8dffb97b0c..f9743fe48e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AbstractSpecialFileProvider.cs @@ -3,104 +3,103 @@ using System.Globalization; using Microsoft.VisualStudio.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides the base class for all instances. +/// +internal abstract class AbstractSpecialFileProvider : ISpecialFileProvider { - /// - /// Provides the base class for all instances. - /// - internal abstract class AbstractSpecialFileProvider : ISpecialFileProvider - { - private readonly IProjectTreeService _treeService; - private readonly IPhysicalProjectTreeStorage _storage; - private readonly bool _isFolder; + private readonly IProjectTreeService _treeService; + private readonly IPhysicalProjectTreeStorage _storage; + private readonly bool _isFolder; - protected AbstractSpecialFileProvider(IPhysicalProjectTree projectTree, bool isFolder = false) - { - _treeService = projectTree.TreeService; - _storage = projectTree.TreeStorage; - _isFolder = isFolder; - } + protected AbstractSpecialFileProvider(IPhysicalProjectTree projectTree, bool isFolder = false) + { + _treeService = projectTree.TreeService; + _storage = projectTree.TreeStorage; + _isFolder = isFolder; + } - public virtual async Task GetFileAsync(SpecialFiles fileId, SpecialFileFlags flags, CancellationToken cancellationToken = default) - { - // Make sure at least have a tree before we start searching it - IProjectTreeServiceState state = await _treeService.PublishAnyNonLoadingTreeAsync(cancellationToken); - - // Attempt to find an existing file/folder first - string? path = await FindFileAsync(state.TreeProvider, state.Tree, flags); - if (path is null) - { - // Otherwise, fall back and create it - path = await CreateDefaultFileAsync(state.TreeProvider, state.Tree, flags); - } - - return path; - } + public virtual async Task GetFileAsync(SpecialFiles fileId, SpecialFileFlags flags, CancellationToken cancellationToken = default) + { + // Make sure at least have a tree before we start searching it + IProjectTreeServiceState state = await _treeService.PublishAnyNonLoadingTreeAsync(cancellationToken); - private async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root, SpecialFileFlags flags) + // Attempt to find an existing file/folder first + string? path = await FindFileAsync(state.TreeProvider, state.Tree, flags); + if (path is null) { - IProjectTree? node = await FindFileAsync(provider, root); - if (node is null) - return null; - - string? path = GetFilePath(provider, node); - if (path is not null && flags.HasFlag(SpecialFileFlags.CreateIfNotExist)) - { - // Similar to legacy, we only verify state if we've been asked to create it - await VerifyStateAsync(node, path); - } - - return path; + // Otherwise, fall back and create it + path = await CreateDefaultFileAsync(state.TreeProvider, state.Tree, flags); } - private async Task CreateDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root, SpecialFileFlags flags) - { - string? path = await GetDefaultFileAsync(provider, root); + return path; + } - if (path is not null && flags.HasFlag(SpecialFileFlags.CreateIfNotExist)) - { - await CreateFileAsync(path); - } + private async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root, SpecialFileFlags flags) + { + IProjectTree? node = await FindFileAsync(provider, root); + if (node is null) + return null; - // We always return the default path, regardless of whether we created it or it exists, as per contract - return path; + string? path = GetFilePath(provider, node); + if (path is not null && flags.HasFlag(SpecialFileFlags.CreateIfNotExist)) + { + // Similar to legacy, we only verify state if we've been asked to create it + await VerifyStateAsync(node, path); } - protected abstract Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root); + return path; + } - protected abstract Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root); + private async Task CreateDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root, SpecialFileFlags flags) + { + string? path = await GetDefaultFileAsync(provider, root); - protected virtual Task CreateFileAsync(string path) + if (path is not null && flags.HasFlag(SpecialFileFlags.CreateIfNotExist)) { - return _isFolder ? _storage.CreateFolderAsync(path) : _storage.CreateEmptyFileAsync(path); + await CreateFileAsync(path); } - private Task AddFileAsync(string path) - { - return _isFolder ? _storage.AddFolderAsync(path) : _storage.AddFileAsync(path); - } + // We always return the default path, regardless of whether we created it or it exists, as per contract + return path; + } - private Task VerifyStateAsync(IProjectTree node, string path) - { - if (_isFolder != node.IsFolder) - throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.SpecialFileProvider_FileOrFolderAlreadyExists, node.Caption), Win32Interop.HResultFromWin32(Win32Interop.ERROR_FILE_EXISTS)); + protected abstract Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root); + + protected abstract Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root); - if (!node.Flags.IsIncludedInProject()) - { // Excluded from project - return AddFileAsync(path); - } + protected virtual Task CreateFileAsync(string path) + { + return _isFolder ? _storage.CreateFolderAsync(path) : _storage.CreateEmptyFileAsync(path); + } - if (node.Flags.IsMissingOnDisk()) - { // Project includes it, but missing from disk - return CreateFileAsync(path); - } + private Task AddFileAsync(string path) + { + return _isFolder ? _storage.AddFolderAsync(path) : _storage.AddFileAsync(path); + } - return Task.CompletedTask; + private Task VerifyStateAsync(IProjectTree node, string path) + { + if (_isFolder != node.IsFolder) + throw new IOException(string.Format(CultureInfo.CurrentCulture, Resources.SpecialFileProvider_FileOrFolderAlreadyExists, node.Caption), Win32Interop.HResultFromWin32(Win32Interop.ERROR_FILE_EXISTS)); + + if (!node.Flags.IsIncludedInProject()) + { // Excluded from project + return AddFileAsync(path); } - private string? GetFilePath(IProjectTreeProvider provider, IProjectTree node) - { - return _isFolder ? provider.GetRootedAddNewItemDirectory(node) : provider.GetPath(node); + if (node.Flags.IsMissingOnDisk()) + { // Project includes it, but missing from disk + return CreateFileAsync(path); } + + return Task.CompletedTask; + } + + private string? GetFilePath(IProjectTreeProvider provider, IProjectTree node) + { + return _isFolder ? provider.GetRootedAddNewItemDirectory(node) : provider.GetPath(node); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProvider.cs index d9b7ede2e8..01b24cd924 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProvider.cs @@ -1,71 +1,70 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides a that handles the AppDesigner folder; +/// called "Properties" in C# and "My Project" in Visual Basic. +/// +[ExportSpecialFileProvider(SpecialFiles.AppDesigner)] +[Export(typeof(IAppDesignerFolderSpecialFileProvider))] +[AppliesTo(ProjectCapability.AppDesigner)] +internal class AppDesignerFolderSpecialFileProvider : AbstractSpecialFileProvider, IAppDesignerFolderSpecialFileProvider { - /// - /// Provides a that handles the AppDesigner folder; - /// called "Properties" in C# and "My Project" in Visual Basic. - /// - [ExportSpecialFileProvider(SpecialFiles.AppDesigner)] - [Export(typeof(IAppDesignerFolderSpecialFileProvider))] - [AppliesTo(ProjectCapability.AppDesigner)] - internal class AppDesignerFolderSpecialFileProvider : AbstractSpecialFileProvider, IAppDesignerFolderSpecialFileProvider + private readonly ProjectProperties _properties; + + [ImportingConstructor] + public AppDesignerFolderSpecialFileProvider(IPhysicalProjectTree projectTree, ProjectProperties properties) + : base(projectTree, isFolder: true) { - private readonly ProjectProperties _properties; + _properties = properties; + } - [ImportingConstructor] - public AppDesignerFolderSpecialFileProvider(IPhysicalProjectTree projectTree, ProjectProperties properties) - : base(projectTree, isFolder: true) + protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + // First look for the actual AppDesigner folder + IProjectTree? folder = FindAppDesignerFolder(root); + if (folder is null) { - _properties = properties; + // Otherwise, find a location that is a candidate + folder = await FindAppDesignerFolderCandidateAsync(provider, root); } - protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) - { - // First look for the actual AppDesigner folder - IProjectTree? folder = FindAppDesignerFolder(root); - if (folder is null) - { - // Otherwise, find a location that is a candidate - folder = await FindAppDesignerFolderCandidateAsync(provider, root); - } - - return folder; - } + return folder; + } - protected override async Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root) - { - string? projectPath = provider.GetRootedAddNewItemDirectory(root); - if (projectPath is null) // Root has DisableAddItem - return null; + protected override async Task GetDefaultFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + string? projectPath = provider.GetRootedAddNewItemDirectory(root); + if (projectPath is null) // Root has DisableAddItem + return null; - string? folderName = await GetDefaultAppDesignerFolderNameAsync(); - if (string.IsNullOrEmpty(folderName)) - return null; // Developer has set the AppDesigner path to empty + string? folderName = await GetDefaultAppDesignerFolderNameAsync(); + if (string.IsNullOrEmpty(folderName)) + return null; // Developer has set the AppDesigner path to empty - return Path.Combine(projectPath, folderName); - } + return Path.Combine(projectPath, folderName); + } - private async Task GetDefaultAppDesignerFolderNameAsync() - { - AppDesigner general = await _properties.GetAppDesignerPropertiesAsync(); + private async Task GetDefaultAppDesignerFolderNameAsync() + { + AppDesigner general = await _properties.GetAppDesignerPropertiesAsync(); - return (string?)await general.FolderName.GetValueAsync(); - } + return (string?)await general.FolderName.GetValueAsync(); + } - private static IProjectTree? FindAppDesignerFolder(IProjectTree root) - { - return root.GetSelfAndDescendentsBreadthFirst() - .FirstOrDefault(child => child.Flags.HasFlag(ProjectTreeFlags.Common.AppDesignerFolder)); - } + private static IProjectTree? FindAppDesignerFolder(IProjectTree root) + { + return root.GetSelfAndDescendentsBreadthFirst() + .FirstOrDefault(child => child.Flags.HasFlag(ProjectTreeFlags.Common.AppDesignerFolder)); + } - private async Task FindAppDesignerFolderCandidateAsync(IProjectTreeProvider provider, IProjectTree root) - { - string? path = await GetDefaultFileAsync(provider, root); - if (path is null) - return null; + private async Task FindAppDesignerFolderCandidateAsync(IProjectTreeProvider provider, IProjectTree root) + { + string? path = await GetDefaultFileAsync(provider, root); + if (path is null) + return null; - return provider.FindByPath(root, path); - } + return provider.FindByPath(root, path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProvider.cs index 49339179d9..890096f492 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProvider.cs @@ -1,60 +1,59 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides a that handles the default 'app.manifest' file; +/// which contains Win32 directives for assembly binding, compatibility and elevation and is +/// typically found under the 'AppDesigner' folder. +/// +[ExportSpecialFileProvider(SpecialFiles.AppManifest)] +[AppliesTo(ProjectCapability.DotNet)] +internal class AppManifestSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider { - /// - /// Provides a that handles the default 'app.manifest' file; - /// which contains Win32 directives for assembly binding, compatibility and elevation and is - /// typically found under the 'AppDesigner' folder. - /// - [ExportSpecialFileProvider(SpecialFiles.AppManifest)] - [AppliesTo(ProjectCapability.DotNet)] - internal class AppManifestSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider + private readonly ProjectProperties _properties; + private readonly ICreateFileFromTemplateService _templateFileCreationService; + + [ImportingConstructor] + public AppManifestSpecialFileProvider( + ISpecialFilesManager specialFilesManager, + IPhysicalProjectTree projectTree, + ICreateFileFromTemplateService templateFileCreationService, + ProjectProperties properties) + : base("app.manifest", specialFilesManager, projectTree) { - private readonly ProjectProperties _properties; - private readonly ICreateFileFromTemplateService _templateFileCreationService; - - [ImportingConstructor] - public AppManifestSpecialFileProvider( - ISpecialFilesManager specialFilesManager, - IPhysicalProjectTree projectTree, - ICreateFileFromTemplateService templateFileCreationService, - ProjectProperties properties) - : base("app.manifest", specialFilesManager, projectTree) - { - _templateFileCreationService = templateFileCreationService; - _properties = properties; - } - - protected override async Task CreateFileCoreAsync(string path) - { - await _templateFileCreationService.CreateFileAsync("AppManifestInternal.zip", path); - } - - protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) - { - string? path = await GetAppManifestPathFromPropertiesAsync(); - if (path is null) - return await base.FindFileAsync(provider, root); - - return provider.FindByPath(root, path); - } - - private async Task GetAppManifestPathFromPropertiesAsync() - { - ConfigurationGeneralBrowseObject configurationGeneral = await _properties.GetConfigurationGeneralBrowseObjectPropertiesAsync(); - - string? value = (string?)await configurationGeneral.ApplicationManifest.GetValueAsync(); - if (Strings.IsNullOrEmpty(value)) - return null; - - if (StringComparers.PropertyLiteralValues.Equals(value, "DefaultManifest")) - return null; - - if (StringComparers.PropertyLiteralValues.Equals(value, "NoManifest")) - return null; - - return value; - } + _templateFileCreationService = templateFileCreationService; + _properties = properties; + } + + protected override async Task CreateFileCoreAsync(string path) + { + await _templateFileCreationService.CreateFileAsync("AppManifestInternal.zip", path); + } + + protected override async Task FindFileAsync(IProjectTreeProvider provider, IProjectTree root) + { + string? path = await GetAppManifestPathFromPropertiesAsync(); + if (path is null) + return await base.FindFileAsync(provider, root); + + return provider.FindByPath(root, path); + } + + private async Task GetAppManifestPathFromPropertiesAsync() + { + ConfigurationGeneralBrowseObject configurationGeneral = await _properties.GetConfigurationGeneralBrowseObjectPropertiesAsync(); + + string? value = (string?)await configurationGeneral.ApplicationManifest.GetValueAsync(); + if (Strings.IsNullOrEmpty(value)) + return null; + + if (StringComparers.PropertyLiteralValues.Equals(value, "DefaultManifest")) + return null; + + if (StringComparers.PropertyLiteralValues.Equals(value, "NoManifest")) + return null; + + return value; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProvider.cs index c9501e5515..2304ebd915 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProvider.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides a that handles the default 'Settings.settings' file; +/// which contains applications settings for a project and is typically found under the 'AppDesigner' +/// folder. +/// +[ExportSpecialFileProvider(SpecialFiles.AppSettings)] +[AppliesTo(ProjectCapability.DotNet + " & " + ProjectCapability.AppSettings)] +internal class AppSettingsSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider { - /// - /// Provides a that handles the default 'Settings.settings' file; - /// which contains applications settings for a project and is typically found under the 'AppDesigner' - /// folder. - /// - [ExportSpecialFileProvider(SpecialFiles.AppSettings)] - [AppliesTo(ProjectCapability.DotNet + " & " + ProjectCapability.AppSettings)] - internal class AppSettingsSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public AppSettingsSpecialFileProvider( - ISpecialFilesManager specialFilesManager, - IPhysicalProjectTree projectTree, - ICreateFileFromTemplateService templateFileCreationService) - : base("Settings.settings", specialFilesManager, projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public AppSettingsSpecialFileProvider( + ISpecialFilesManager specialFilesManager, + IPhysicalProjectTree projectTree, + ICreateFileFromTemplateService templateFileCreationService) + : base("Settings.settings", specialFilesManager, projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileCoreAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("SettingsInternal.zip", path); - } + protected override Task CreateFileCoreAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("SettingsInternal.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProvider.cs index 7062d7c56f..ce39f67112 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProvider.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides a that handles the default 'Resources.resx' file; +/// which contains localized resources for a project and is typically found under the 'AppDesigner' +/// folder. +/// +[ExportSpecialFileProvider(SpecialFiles.AssemblyResource)] +[AppliesTo(ProjectCapability.DotNet)] +internal class AssemblyResourcesSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider { - /// - /// Provides a that handles the default 'Resources.resx' file; - /// which contains localized resources for a project and is typically found under the 'AppDesigner' - /// folder. - /// - [ExportSpecialFileProvider(SpecialFiles.AssemblyResource)] - [AppliesTo(ProjectCapability.DotNet)] - internal class AssemblyResourcesSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public AssemblyResourcesSpecialFileProvider( - ISpecialFilesManager specialFilesManager, - IPhysicalProjectTree projectTree, - ICreateFileFromTemplateService templateFileCreationService) - : base("Resources.resx", specialFilesManager, projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public AssemblyResourcesSpecialFileProvider( + ISpecialFilesManager specialFilesManager, + IPhysicalProjectTree projectTree, + ICreateFileFromTemplateService templateFileCreationService) + : base("Resources.resx", specialFilesManager, projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileCoreAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("ResourceInternal.zip", path); - } + protected override Task CreateFileCoreAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("ResourceInternal.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProvider.cs index ea73d425a0..1405e33e64 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProvider.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp; + +/// +/// Provides a that handles the default 'App.config' file. +/// +[ExportSpecialFileProvider(SpecialFiles.AppConfig)] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpAppConfigSpecialFileProvider : AbstractFindByNameSpecialFileProvider { - /// - /// Provides a that handles the default 'App.config' file. - /// - [ExportSpecialFileProvider(SpecialFiles.AppConfig)] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpAppConfigSpecialFileProvider : AbstractFindByNameSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public CSharpAppConfigSpecialFileProvider(IPhysicalProjectTree projectTree, ICreateFileFromTemplateService templateFileCreationService) - : base("App.config", projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public CSharpAppConfigSpecialFileProvider(IPhysicalProjectTree projectTree, ICreateFileFromTemplateService templateFileCreationService) + : base("App.config", projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("AppConfigInternal.zip", path); - } + protected override Task CreateFileAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("AppConfigInternal.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProvider.cs index 9086e0c8e2..b4a1ec04bd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProvider.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp; + +/// +/// Provides a that handles the WPF Application Definition file, +/// typically called "App.xaml" in C# projects. +/// +[ExportSpecialFileProvider(SpecialFiles.AppXaml)] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpAppXamlSpecialFileProvider : AbstractAppXamlSpecialFileProvider { - /// - /// Provides a that handles the WPF Application Definition file, - /// typically called "App.xaml" in C# projects. - /// - [ExportSpecialFileProvider(SpecialFiles.AppXaml)] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpAppXamlSpecialFileProvider : AbstractAppXamlSpecialFileProvider + [ImportingConstructor] + public CSharpAppXamlSpecialFileProvider(IPhysicalProjectTree projectTree) + : base("App.xaml", projectTree) { - [ImportingConstructor] - public CSharpAppXamlSpecialFileProvider(IPhysicalProjectTree projectTree) - : base("App.xaml", projectTree) - { - } + } - protected override Task CreateFileAsync(string path) - { - // We don't have a template for C# for App.xaml, deliberately - // throw NotImplementedException (which gets mapped to E_NOTIMPL) to - // indicate we don't support this. - throw new NotImplementedException(); - } + protected override Task CreateFileAsync(string path) + { + // We don't have a template for C# for App.xaml, deliberately + // throw NotImplementedException (which gets mapped to E_NOTIMPL) to + // indicate we don't support this. + throw new NotImplementedException(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProvider.cs index f279bbfa05..25985afb5b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProvider.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp; + +/// +/// Provides a that handles the default 'AssemblyInfo.cs' file; +/// which contains attributes for assembly versioning, COM exposure, and other assembly-level +/// directives and typically found under the 'AppDesigner' folder. +/// +[ExportSpecialFileProvider(SpecialFiles.AssemblyInfo)] +[AppliesTo(ProjectCapability.CSharp)] +internal class CSharpAssemblyInfoSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider { - /// - /// Provides a that handles the default 'AssemblyInfo.cs' file; - /// which contains attributes for assembly versioning, COM exposure, and other assembly-level - /// directives and typically found under the 'AppDesigner' folder. - /// - [ExportSpecialFileProvider(SpecialFiles.AssemblyInfo)] - [AppliesTo(ProjectCapability.CSharp)] - internal class CSharpAssemblyInfoSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public CSharpAssemblyInfoSpecialFileProvider( - ISpecialFilesManager specialFilesManager, - IPhysicalProjectTree projectTree, - ICreateFileFromTemplateService templateFileCreationService) - : base("AssemblyInfo.cs", specialFilesManager, projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public CSharpAssemblyInfoSpecialFileProvider( + ISpecialFilesManager specialFilesManager, + IPhysicalProjectTree projectTree, + ICreateFileFromTemplateService templateFileCreationService) + : base("AssemblyInfo.cs", specialFilesManager, projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileCoreAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("AssemblyInfoInternal.zip", path); - } + protected override Task CreateFileCoreAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("AssemblyInfoInternal.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/IAppDesignerFolderSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/IAppDesignerFolderSpecialFileProvider.cs index d5f1e810d3..20311a1de5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/IAppDesignerFolderSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/IAppDesignerFolderSpecialFileProvider.cs @@ -1,8 +1,7 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +internal interface IAppDesignerFolderSpecialFileProvider : ISpecialFileProvider { - internal interface IAppDesignerFolderSpecialFileProvider : ISpecialFileProvider - { - } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/ISpecialFilesManager.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/ISpecialFilesManager.cs index 51fcfdeaf1..794e95cf8c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/ISpecialFilesManager.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/ISpecialFilesManager.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Queries the project for special files, such as the application config file or application designer folder, and optionally creates them and checks them out from source control. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface ISpecialFilesManager { /// - /// Queries the project for special files, such as the application config file or application designer folder, and optionally creates them and checks them out from source control. + /// Returns the path to a special file, optionally creating it and + /// checking it out from source control. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface ISpecialFilesManager - { - /// - /// Returns the path to a special file, optionally creating it and - /// checking it out from source control. - /// - /// - /// One of the values indicating the special file to return. - /// - /// - /// One or more of the - /// - /// - /// The file name of the special file, or if special file is not - /// handled by the project. - /// - Task GetFileAsync(SpecialFiles fileId, SpecialFileFlags flags); - } + /// + /// One of the values indicating the special file to return. + /// + /// + /// One or more of the + /// + /// + /// The file name of the special file, or if special file is not + /// handled by the project. + /// + Task GetFileAsync(SpecialFiles fileId, SpecialFileFlags flags); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProvider.cs index 3abc3b8cbe..72ddc165e7 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProvider.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +/// +/// Provides a that handles the 'licenses.licx' file; +/// a file that contains a list of licensed (typically Windows Forms) components used by +/// a project and is typically found under the 'AppDesigner' folder. +/// +[ExportSpecialFileProvider(SpecialFiles.Licenses)] +[AppliesTo(ProjectCapability.DotNet)] +internal class LicensesSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider { - /// - /// Provides a that handles the 'licenses.licx' file; - /// a file that contains a list of licensed (typically Windows Forms) components used by - /// a project and is typically found under the 'AppDesigner' folder. - /// - [ExportSpecialFileProvider(SpecialFiles.Licenses)] - [AppliesTo(ProjectCapability.DotNet)] - internal class LicensesSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider + [ImportingConstructor] + public LicensesSpecialFileProvider(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + : base("licenses.licx", specialFilesManager, projectTree) { - [ImportingConstructor] - public LicensesSpecialFileProvider(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - : base("licenses.licx", specialFilesManager, projectTree) - { - } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProvider.cs index 14d075442e..9a06b65332 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProvider.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic; + +/// +/// Provides a that handles the default 'App.config' file. +/// +[ExportSpecialFileProvider(SpecialFiles.AppConfig)] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicAppConfigSpecialFileProvider : AbstractFindByNameSpecialFileProvider { - /// - /// Provides a that handles the default 'App.config' file. - /// - [ExportSpecialFileProvider(SpecialFiles.AppConfig)] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicAppConfigSpecialFileProvider : AbstractFindByNameSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public VisualBasicAppConfigSpecialFileProvider(IPhysicalProjectTree projectTree, ICreateFileFromTemplateService templateFileCreationService) - : base("App.config", projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public VisualBasicAppConfigSpecialFileProvider(IPhysicalProjectTree projectTree, ICreateFileFromTemplateService templateFileCreationService) + : base("App.config", projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("AppConfigurationInternal.zip", path); - } + protected override Task CreateFileAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("AppConfigurationInternal.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProvider.cs index 6c47de556e..5d078bc4bd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProvider.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic; + +/// +/// Provides a that handles the WPF Application Definition, +/// typically called "Application.xaml" in Visual Basic projects. +/// +[ExportSpecialFileProvider(SpecialFiles.AppXaml)] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicAppXamlSpecialFileProvider : AbstractAppXamlSpecialFileProvider { - /// - /// Provides a that handles the WPF Application Definition, - /// typically called "Application.xaml" in Visual Basic projects. - /// - [ExportSpecialFileProvider(SpecialFiles.AppXaml)] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicAppXamlSpecialFileProvider : AbstractAppXamlSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public VisualBasicAppXamlSpecialFileProvider(IPhysicalProjectTree projectTree, ICreateFileFromTemplateService templateFileCreationService) - : base("Application.xaml", projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public VisualBasicAppXamlSpecialFileProvider(IPhysicalProjectTree projectTree, ICreateFileFromTemplateService templateFileCreationService) + : base("Application.xaml", projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("InternalWPFApplicationDefinition.zip", path); - } + protected override Task CreateFileAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("InternalWPFApplicationDefinition.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProvider.cs index 7025640fb7..34249b0b7f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProvider.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic; + +/// +/// Provides a that handles the default 'AssemblyInfo.vb' file; +/// which contains attributes for assembly versioning, COM exposure, and other assembly-level +/// directives and is typically found under the 'AppDesigner' folder. +/// +[ExportSpecialFileProvider(SpecialFiles.AssemblyInfo)] +[AppliesTo(ProjectCapability.VisualBasic)] +internal class VisualBasicAssemblyInfoSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider { - /// - /// Provides a that handles the default 'AssemblyInfo.vb' file; - /// which contains attributes for assembly versioning, COM exposure, and other assembly-level - /// directives and is typically found under the 'AppDesigner' folder. - /// - [ExportSpecialFileProvider(SpecialFiles.AssemblyInfo)] - [AppliesTo(ProjectCapability.VisualBasic)] - internal class VisualBasicAssemblyInfoSpecialFileProvider : AbstractFindByNameUnderAppDesignerSpecialFileProvider - { - private readonly ICreateFileFromTemplateService _templateFileCreationService; + private readonly ICreateFileFromTemplateService _templateFileCreationService; - [ImportingConstructor] - public VisualBasicAssemblyInfoSpecialFileProvider( - ISpecialFilesManager specialFilesManager, - IPhysicalProjectTree projectTree, - ICreateFileFromTemplateService templateFileCreationService) - : base("AssemblyInfo.vb", specialFilesManager, projectTree) - { - _templateFileCreationService = templateFileCreationService; - } + [ImportingConstructor] + public VisualBasicAssemblyInfoSpecialFileProvider( + ISpecialFilesManager specialFilesManager, + IPhysicalProjectTree projectTree, + ICreateFileFromTemplateService templateFileCreationService) + : base("AssemblyInfo.vb", specialFilesManager, projectTree) + { + _templateFileCreationService = templateFileCreationService; + } - protected override Task CreateFileCoreAsync(string path) - { - return _templateFileCreationService.CreateFileAsync("AssemblyInfoInternal.zip", path); - } + protected override Task CreateFileCoreAsync(string path) + { + return _templateFileCreationService.CreateFileAsync("AssemblyInfoInternal.zip", path); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/TargetFrameworkIdentifiers.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/TargetFrameworkIdentifiers.cs index 958f9e4046..eb936747b0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/TargetFrameworkIdentifiers.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/TargetFrameworkIdentifiers.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Contains the string constants for the most common target framework identifiers. +/// Should be compared using +/// +internal static class TargetFrameworkIdentifiers { - /// - /// Contains the string constants for the most common target framework identifiers. - /// Should be compared using - /// - internal static class TargetFrameworkIdentifiers - { - // This is the most common identifiers, but it is not a complete list: i.e. .NETPortable. + // This is the most common identifiers, but it is not a complete list: i.e. .NETPortable. - public const string NetCoreApp = ".NETCoreApp"; + public const string NetCoreApp = ".NETCoreApp"; - public const string NetFramework = ".NETFramework"; + public const string NetFramework = ".NETFramework"; - public const string NetStandard = ".NETStandard"; - } + public const string NetStandard = ".NETStandard"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AbstractSpecialFolderProjectTreePropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AbstractSpecialFolderProjectTreePropertiesProvider.cs index 48d90f2b23..53950173b8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AbstractSpecialFolderProjectTreePropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AbstractSpecialFolderProjectTreePropertiesProvider.cs @@ -2,105 +2,104 @@ using Microsoft.VisualStudio.ProjectSystem.Imaging; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides the base class for objects that handle special items, such as the AppDesigner folder. +/// +internal abstract class AbstractSpecialFolderProjectTreePropertiesProvider : IProjectTreePropertiesProvider { + private readonly IProjectImageProvider _imageProvider; + + protected AbstractSpecialFolderProjectTreePropertiesProvider(IProjectImageProvider imageProvider) + { + Assumes.NotNull(imageProvider); + + _imageProvider = imageProvider; + } + /// - /// Provides the base class for objects that handle special items, such as the AppDesigner folder. + /// Gets the image key that represents the image that will be applied to the special folder. /// - internal abstract class AbstractSpecialFolderProjectTreePropertiesProvider : IProjectTreePropertiesProvider - { - private readonly IProjectImageProvider _imageProvider; + public abstract string FolderImageKey { get; } - protected AbstractSpecialFolderProjectTreePropertiesProvider(IProjectImageProvider imageProvider) - { - Assumes.NotNull(imageProvider); + /// + /// Gets the image key that represents the image that will be applied to the special folder when expanded. + /// + public abstract string ExpandedFolderImageKey { get; } - _imageProvider = imageProvider; - } + /// + /// Gets the default flags that will be applied to the special folder. + /// + public abstract ProjectTreeFlags FolderFlags { get; } - /// - /// Gets the image key that represents the image that will be applied to the special folder. - /// - public abstract string FolderImageKey { get; } + /// + /// Gets a value indicating whether the special folder is supported in this project. + /// + public abstract bool IsSupported { get; } - /// - /// Gets the image key that represents the image that will be applied to the special folder when expanded. - /// - public abstract string ExpandedFolderImageKey { get; } + public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + { + Requires.NotNull(propertyContext); + Requires.NotNull(propertyValues); - /// - /// Gets the default flags that will be applied to the special folder. - /// - public abstract ProjectTreeFlags FolderFlags { get; } + if (!IsSupported) + return; - /// - /// Gets a value indicating whether the special folder is supported in this project. - /// - public abstract bool IsSupported { get; } + if (IsCandidateSpecialFolder(propertyContext, propertyValues.Flags)) + { + ApplySpecialFolderProperties(propertyValues); + return; + } - public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + if (AreContentsVisibleOnlyInShowAllFiles(propertyContext.ProjectTreeSettings) && IsCandidateSpecialFolderItem(propertyContext, propertyValues.Flags)) { - Requires.NotNull(propertyContext); - Requires.NotNull(propertyValues); - - if (!IsSupported) - return; - - if (IsCandidateSpecialFolder(propertyContext, propertyValues.Flags)) - { - ApplySpecialFolderProperties(propertyValues); - return; - } - - if (AreContentsVisibleOnlyInShowAllFiles(propertyContext.ProjectTreeSettings) && IsCandidateSpecialFolderItem(propertyContext, propertyValues.Flags)) - { - ApplySpecialFolderItemProperties(propertyValues); - return; - } + ApplySpecialFolderItemProperties(propertyValues); + return; } + } - /// - /// Returns a value indicating whether the specified property context represents the candidate special folder. - /// - protected abstract bool IsCandidateSpecialFolder(IProjectTreeCustomizablePropertyContext propertyContext, ProjectTreeFlags flags); + /// + /// Returns a value indicating whether the specified property context represents the candidate special folder. + /// + protected abstract bool IsCandidateSpecialFolder(IProjectTreeCustomizablePropertyContext propertyContext, ProjectTreeFlags flags); - /// - /// Returns a value indicating whether the contents of the special folder are only visible in Show All Files. - /// - protected abstract bool AreContentsVisibleOnlyInShowAllFiles(IImmutableDictionary projectTreeSettings); + /// + /// Returns a value indicating whether the contents of the special folder are only visible in Show All Files. + /// + protected abstract bool AreContentsVisibleOnlyInShowAllFiles(IImmutableDictionary projectTreeSettings); - private bool IsCandidateSpecialFolderItem(IProjectTreeCustomizablePropertyContext propertyContext, ProjectTreeFlags flags) + private bool IsCandidateSpecialFolderItem(IProjectTreeCustomizablePropertyContext propertyContext, ProjectTreeFlags flags) + { + // We're a special folder item if our parent is the special folder. We rely on + // the fact that "VisibleOnlyInShowAllFiles" is transitive; that is, if a parent + // is marked with it, its children are also implicitly marked with it. + if (propertyContext.ParentNodeFlags.Contains(FolderFlags)) { - // We're a special folder item if our parent is the special folder. We rely on - // the fact that "VisibleOnlyInShowAllFiles" is transitive; that is, if a parent - // is marked with it, its children are also implicitly marked with it. - if (propertyContext.ParentNodeFlags.Contains(FolderFlags)) - { - return flags.IsIncludedInProject(); - } - - return false; + return flags.IsIncludedInProject(); } - private void ApplySpecialFolderProperties(IProjectTreeCustomizablePropertyValues propertyValues) - { - propertyValues.Flags = propertyValues.Flags.Union(FolderFlags); - - // Use default icon if missing - if (!propertyValues.Flags.IsMissingOnDisk()) - { - ProjectImageMoniker? icon = _imageProvider.GetProjectImage(FolderImageKey); - ProjectImageMoniker? expandedIcon = _imageProvider.GetProjectImage(ExpandedFolderImageKey); - - // Avoid overwriting icon if the image provider didn't provide one - propertyValues.Icon = icon ?? propertyValues.Icon; - propertyValues.ExpandedIcon = expandedIcon ?? propertyValues.ExpandedIcon; - } - } + return false; + } + + private void ApplySpecialFolderProperties(IProjectTreeCustomizablePropertyValues propertyValues) + { + propertyValues.Flags = propertyValues.Flags.Union(FolderFlags); - private static void ApplySpecialFolderItemProperties(IProjectTreeCustomizablePropertyValues propertyValues) + // Use default icon if missing + if (!propertyValues.Flags.IsMissingOnDisk()) { - propertyValues.Flags = propertyValues.Flags.Add(ProjectTreeFlags.Common.VisibleOnlyInShowAllFiles); + ProjectImageMoniker? icon = _imageProvider.GetProjectImage(FolderImageKey); + ProjectImageMoniker? expandedIcon = _imageProvider.GetProjectImage(ExpandedFolderImageKey); + + // Avoid overwriting icon if the image provider didn't provide one + propertyValues.Icon = icon ?? propertyValues.Icon; + propertyValues.ExpandedIcon = expandedIcon ?? propertyValues.ExpandedIcon; } } + + private static void ApplySpecialFolderItemProperties(IProjectTreeCustomizablePropertyValues propertyValues) + { + propertyValues.Flags = propertyValues.Flags.Add(ProjectTreeFlags.Common.VisibleOnlyInShowAllFiles); + } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProvider.cs index 5b4f2954e3..4d924dc76c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProvider.cs @@ -4,87 +4,86 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using ManagedPriorityOrder = Microsoft.VisualStudio.ProjectSystem.Order; -namespace Microsoft.VisualStudio.ProjectSystem.Tree +namespace Microsoft.VisualStudio.ProjectSystem.Tree; + +/// +/// Provides a that handles the AppDesigner +/// folder, called "Properties" in C# and "My Project" in Visual Basic. +/// +[Export(typeof(IProjectTreePropertiesProvider))] +[Export(typeof(IProjectTreeSettingsProvider))] +[AppliesTo(ProjectCapability.AppDesigner)] +[Order(ManagedPriorityOrder.Default)] +internal class AppDesignerFolderProjectTreePropertiesProvider : AbstractSpecialFolderProjectTreePropertiesProvider, IProjectTreeSettingsProvider { - /// - /// Provides a that handles the AppDesigner - /// folder, called "Properties" in C# and "My Project" in Visual Basic. - /// - [Export(typeof(IProjectTreePropertiesProvider))] - [Export(typeof(IProjectTreeSettingsProvider))] - [AppliesTo(ProjectCapability.AppDesigner)] - [Order(ManagedPriorityOrder.Default)] - internal class AppDesignerFolderProjectTreePropertiesProvider : AbstractSpecialFolderProjectTreePropertiesProvider, IProjectTreeSettingsProvider - { - private static readonly ProjectTreeFlags s_defaultFolderFlags = ProjectTreeFlags.Create(ProjectTreeFlags.Common.AppDesignerFolder | ProjectTreeFlags.Common.BubbleUp); + private static readonly ProjectTreeFlags s_defaultFolderFlags = ProjectTreeFlags.Create(ProjectTreeFlags.Common.AppDesignerFolder | ProjectTreeFlags.Common.BubbleUp); - private readonly IProjectDesignerService? _designerService; + private readonly IProjectDesignerService? _designerService; - [ImportingConstructor] - public AppDesignerFolderProjectTreePropertiesProvider( - [Import(typeof(ProjectImageProviderAggregator))]IProjectImageProvider imageProvider, - [Import(AllowDefault = true)] IProjectDesignerService? designerService) - : base(imageProvider) - { - _designerService = designerService; - } + [ImportingConstructor] + public AppDesignerFolderProjectTreePropertiesProvider( + [Import(typeof(ProjectImageProviderAggregator))]IProjectImageProvider imageProvider, + [Import(AllowDefault = true)] IProjectDesignerService? designerService) + : base(imageProvider) + { + _designerService = designerService; + } - public override bool IsSupported - { - get { return _designerService?.SupportsProjectDesigner != false; } - } + public override bool IsSupported + { + get { return _designerService?.SupportsProjectDesigner != false; } + } - public override ProjectTreeFlags FolderFlags - { - get { return s_defaultFolderFlags; } - } + public override ProjectTreeFlags FolderFlags + { + get { return s_defaultFolderFlags; } + } - public override string FolderImageKey - { - get { return ProjectImageKey.AppDesignerFolder; } - } + public override string FolderImageKey + { + get { return ProjectImageKey.AppDesignerFolder; } + } - public override string ExpandedFolderImageKey - { - get { return ProjectImageKey.ExpandedAppDesignerFolder; } - } + public override string ExpandedFolderImageKey + { + get { return ProjectImageKey.ExpandedAppDesignerFolder; } + } - public ICollection ProjectPropertiesRules - { - get { return AppDesigner.SchemaNameArray; } - } + public ICollection ProjectPropertiesRules + { + get { return AppDesigner.SchemaNameArray; } + } - public void UpdateProjectTreeSettings(IImmutableDictionary ruleSnapshots, ref IImmutableDictionary projectTreeSettings) - { - Requires.NotNull(ruleSnapshots); - Requires.NotNull(projectTreeSettings); - - // Retrieves the and properties from the project file - // - // TODO: Read these default values from the rules themselves - // See: https://github.com/dotnet/project-system/issues/209 - string folderName = ruleSnapshots.GetPropertyOrDefault(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, "Properties"); - string contextsVisibleOnlyInShowAllFiles = ruleSnapshots.GetPropertyOrDefault(AppDesigner.SchemaName, AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty, "false"); - - projectTreeSettings = projectTreeSettings.SetItem($"{AppDesigner.SchemaName}.{AppDesigner.FolderNameProperty}", folderName); - projectTreeSettings = projectTreeSettings.SetItem($"{AppDesigner.SchemaName}.{AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty}", contextsVisibleOnlyInShowAllFiles); - } + public void UpdateProjectTreeSettings(IImmutableDictionary ruleSnapshots, ref IImmutableDictionary projectTreeSettings) + { + Requires.NotNull(ruleSnapshots); + Requires.NotNull(projectTreeSettings); + + // Retrieves the and properties from the project file + // + // TODO: Read these default values from the rules themselves + // See: https://github.com/dotnet/project-system/issues/209 + string folderName = ruleSnapshots.GetPropertyOrDefault(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, "Properties"); + string contextsVisibleOnlyInShowAllFiles = ruleSnapshots.GetPropertyOrDefault(AppDesigner.SchemaName, AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty, "false"); + + projectTreeSettings = projectTreeSettings.SetItem($"{AppDesigner.SchemaName}.{AppDesigner.FolderNameProperty}", folderName); + projectTreeSettings = projectTreeSettings.SetItem($"{AppDesigner.SchemaName}.{AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty}", contextsVisibleOnlyInShowAllFiles); + } - protected sealed override bool IsCandidateSpecialFolder(IProjectTreeCustomizablePropertyContext propertyContext, ProjectTreeFlags flags) + protected sealed override bool IsCandidateSpecialFolder(IProjectTreeCustomizablePropertyContext propertyContext, ProjectTreeFlags flags) + { + if (propertyContext.ParentNodeFlags.IsProjectRoot() && flags.IsFolder() && flags.IsIncludedInProject()) { - if (propertyContext.ParentNodeFlags.IsProjectRoot() && flags.IsFolder() && flags.IsIncludedInProject()) - { - string folderName = propertyContext.ProjectTreeSettings[$"{AppDesigner.SchemaName}.{AppDesigner.FolderNameProperty}"]; + string folderName = propertyContext.ProjectTreeSettings[$"{AppDesigner.SchemaName}.{AppDesigner.FolderNameProperty}"]; - return StringComparers.Paths.Equals(folderName, propertyContext.ItemName); - } - - return false; + return StringComparers.Paths.Equals(folderName, propertyContext.ItemName); } - protected override bool AreContentsVisibleOnlyInShowAllFiles(IImmutableDictionary projectTreeSettings) - { - return StringComparers.PropertyLiteralValues.Equals(projectTreeSettings[$"{AppDesigner.SchemaName}.{AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty}"], "true"); - } + return false; + } + + protected override bool AreContentsVisibleOnlyInShowAllFiles(IImmutableDictionary projectTreeSettings) + { + return StringComparers.PropertyLiteralValues.Equals(projectTreeSettings[$"{AppDesigner.SchemaName}.{AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty}"], "true"); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderRenameHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderRenameHandler.cs index 89e4989e8b..44e0d1762e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderRenameHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/AppDesignerFolderRenameHandler.cs @@ -2,35 +2,34 @@ using ManagedPriorityOrder = Microsoft.VisualStudio.ProjectSystem.Order; -namespace Microsoft.VisualStudio.ProjectSystem.Tree +namespace Microsoft.VisualStudio.ProjectSystem.Tree; + +/// +/// Responsible for setting the AppDesignerFolder property for when the AppDesigner +/// folder ("Properties" in C# and "My Project" in Visual Basic) is renamed. +/// +[Order(ManagedPriorityOrder.Default)] +[Export(typeof(IProjectTreeActionHandler))] +[AppliesTo(ProjectCapability.AppDesigner)] +internal class AppDesignerFolderRenameHandler : ProjectTreeActionHandlerBase { - /// - /// Responsible for setting the AppDesignerFolder property for when the AppDesigner - /// folder ("Properties" in C# and "My Project" in Visual Basic) is renamed. - /// - [Order(ManagedPriorityOrder.Default)] - [Export(typeof(IProjectTreeActionHandler))] - [AppliesTo(ProjectCapability.AppDesigner)] - internal class AppDesignerFolderRenameHandler : ProjectTreeActionHandlerBase + private readonly IActiveConfiguredValue _properties; + + [ImportingConstructor] + public AppDesignerFolderRenameHandler(IActiveConfiguredValue properties) { - private readonly IActiveConfiguredValue _properties; + _properties = properties; + } - [ImportingConstructor] - public AppDesignerFolderRenameHandler(IActiveConfiguredValue properties) - { - _properties = properties; - } + public override async Task RenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) + { + await base.RenameAsync(context, node, value); - public override async Task RenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) + if (node.Flags.Contains(ProjectTreeFlags.AppDesignerFolder)) { - await base.RenameAsync(context, node, value); - - if (node.Flags.Contains(ProjectTreeFlags.AppDesignerFolder)) - { - AppDesigner appDesigner = await _properties.Value.GetAppDesignerPropertiesAsync(); + AppDesigner appDesigner = await _properties.Value.GetAppDesignerPropertiesAsync(); - await appDesigner.FolderName.SetUnevaluatedValueAsync(value); - } + await appDesigner.FolderName.SetUnevaluatedValueAsync(value); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependenciesChanges.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependenciesChanges.cs index bf301ec94c..aba979fa19 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependenciesChanges.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependenciesChanges.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies; + +/// +/// Models changes to a project's dependencies. +/// +/// +/// +/// Used by legacy dependency providers that export . +/// +/// +/// All dependencies modelled via this interface are unconfigured (do not apply to a specific project +/// configuration). +/// +/// +public interface IDependenciesChanges { /// - /// Models changes to a project's dependencies. + /// Gets models for the set of added and updated dependencies. + /// + IImmutableList AddedNodes { get; } + + /// + /// Gets models for the set of removed dependencies. /// /// - /// - /// Used by legacy dependency providers that export . - /// - /// - /// All dependencies modelled via this interface are unconfigured (do not apply to a specific project - /// configuration). - /// + /// Consumers must only use the and + /// properties of returned items. /// - public interface IDependenciesChanges - { - /// - /// Gets models for the set of added and updated dependencies. - /// - IImmutableList AddedNodes { get; } - - /// - /// Gets models for the set of removed dependencies. - /// - /// - /// Consumers must only use the and - /// properties of returned items. - /// - IImmutableList RemovedNodes { get; } - } + IImmutableList RemovedNodes { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependencyModel.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependencyModel.cs index e27f643c71..86484f41f3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependencyModel.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IDependencyModel.cs @@ -2,143 +2,142 @@ using Microsoft.VisualStudio.Imaging.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies; + +/// +/// A public model used to update dependencies in the snapshot. +/// +public interface IDependencyModel { /// - /// A public model used to update dependencies in the snapshot. - /// - public interface IDependencyModel - { - /// - /// Uniquely identifies the dependency within its project (for a given configuration). - /// - /// - /// For dependencies obtained via MSBuild, this equals . - /// - string Id { get; } - - /// - /// Dependency type, a formal name of the provider type that knows how to create a node - /// for given dependency. - /// - string ProviderType { get; } - - /// - /// Name of the dependency - /// - [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] - string Name { get; } - - /// - /// ItemSpec by which dependency could be found in msbuild Project. - /// - If dependency is "Resolved" then resolved path will be in Path property, - /// and unresolved in OriginalItemSpec. - /// - if dependency is "Unresolved" then Path and OriginalItemSpec are the same. - /// - if dependency is "custom", i.e. does not have item in the msbuild project or - /// item is not represented by xaml rule, then OriginalItemSpec will be ignored - /// and should be empty. - /// - string? OriginalItemSpec { get; } - - /// - /// When is , this contains the resolved path - /// of the dependency, otherwise it is equal to . - /// - string? Path { get; } - - /// - /// Friendly name of the dependency, should be used for UI (captions etc) - /// - string Caption { get; } - - /// - /// Used to determine the browse object rule for this dependency. - /// - string? SchemaName { get; } - - /// - /// Used to determine the browse object rule for this dependency. - /// - string? SchemaItemType { get; } - - /// - /// Version of the dependency - /// - [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] - string Version { get; } - - /// - /// Specifies if dependency is resolved or not - /// - bool Resolved { get; } - - /// - /// Specifies if dependency is an explicit project dependency or not - /// - [Obsolete( - "IDependencyModel may only represent a top-level dependency. " + - "Implementations should only instantiate IDependencyModel objects for which this value would be true, and should return true from this property. " + - "For more information see https://github.com/dotnet/project-system/blob/main/docs/repo/dependencies-node-roadmap.md#transitive-dependencies")] - bool TopLevel { get; } - - /// - /// Specifies if dependency was brought by default and can not be removed/modified by user. - /// - bool Implicit { get; } - - /// - /// In some cases dependency should be present in snapshot, but not displayed in the Tree. - /// - [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] - bool Visible { get; } - - /// - /// Gets the icon to use when the dependency is resolved and collapsed. - /// - ImageMoniker Icon { get; } - - /// - /// Gets the icon to use when the dependency is resolved and expanded. - /// - [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] - ImageMoniker ExpandedIcon { get; } - - /// - /// Gets the icon to use when the dependency is unresolved and collapsed. - /// - ImageMoniker UnresolvedIcon { get; } - - /// - /// Gets the icon to use when the dependency is unresolved and expanded. - /// - [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] - ImageMoniker UnresolvedExpandedIcon { get; } - - /// - /// Unused. - /// - [Obsolete("IDependencyModel is only used for top-level dependencies, and this property only existed to support transitive references.")] - int Priority { get; } - - ProjectTreeFlags Flags { get; } - - /// - /// A list of properties that might be displayed in property pages - /// (in BrowsableObject context). - /// - IImmutableDictionary Properties { get; } - - /// - /// Gets the set of child dependency IDs. May be empty, but never . - /// - /// - /// Each ID is of the form provided by the dependency model. - /// For dependencies obtained via MSBuild, these will be values. - /// - [Obsolete( - "IDependencyModel cannot model children. Transitive (non-top-level) dependencies are handled via a different set of APIs for performance reasons. " + - "Implementation of this property may throw, as it should never be called. " + - "For more information see https://github.com/dotnet/project-system/blob/main/docs/repo/dependencies-node-roadmap.md#transitive-dependencies")] - IImmutableList DependencyIDs { get; } - } + /// Uniquely identifies the dependency within its project (for a given configuration). + /// + /// + /// For dependencies obtained via MSBuild, this equals . + /// + string Id { get; } + + /// + /// Dependency type, a formal name of the provider type that knows how to create a node + /// for given dependency. + /// + string ProviderType { get; } + + /// + /// Name of the dependency + /// + [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] + string Name { get; } + + /// + /// ItemSpec by which dependency could be found in msbuild Project. + /// - If dependency is "Resolved" then resolved path will be in Path property, + /// and unresolved in OriginalItemSpec. + /// - if dependency is "Unresolved" then Path and OriginalItemSpec are the same. + /// - if dependency is "custom", i.e. does not have item in the msbuild project or + /// item is not represented by xaml rule, then OriginalItemSpec will be ignored + /// and should be empty. + /// + string? OriginalItemSpec { get; } + + /// + /// When is , this contains the resolved path + /// of the dependency, otherwise it is equal to . + /// + string? Path { get; } + + /// + /// Friendly name of the dependency, should be used for UI (captions etc) + /// + string Caption { get; } + + /// + /// Used to determine the browse object rule for this dependency. + /// + string? SchemaName { get; } + + /// + /// Used to determine the browse object rule for this dependency. + /// + string? SchemaItemType { get; } + + /// + /// Version of the dependency + /// + [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] + string Version { get; } + + /// + /// Specifies if dependency is resolved or not + /// + bool Resolved { get; } + + /// + /// Specifies if dependency is an explicit project dependency or not + /// + [Obsolete( + "IDependencyModel may only represent a top-level dependency. " + + "Implementations should only instantiate IDependencyModel objects for which this value would be true, and should return true from this property. " + + "For more information see https://github.com/dotnet/project-system/blob/main/docs/repo/dependencies-node-roadmap.md#transitive-dependencies")] + bool TopLevel { get; } + + /// + /// Specifies if dependency was brought by default and can not be removed/modified by user. + /// + bool Implicit { get; } + + /// + /// In some cases dependency should be present in snapshot, but not displayed in the Tree. + /// + [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] + bool Visible { get; } + + /// + /// Gets the icon to use when the dependency is resolved and collapsed. + /// + ImageMoniker Icon { get; } + + /// + /// Gets the icon to use when the dependency is resolved and expanded. + /// + [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] + ImageMoniker ExpandedIcon { get; } + + /// + /// Gets the icon to use when the dependency is unresolved and collapsed. + /// + ImageMoniker UnresolvedIcon { get; } + + /// + /// Gets the icon to use when the dependency is unresolved and expanded. + /// + [Obsolete("Property is unused. Implementation of this property may throw, as it should never be called.")] + ImageMoniker UnresolvedExpandedIcon { get; } + + /// + /// Unused. + /// + [Obsolete("IDependencyModel is only used for top-level dependencies, and this property only existed to support transitive references.")] + int Priority { get; } + + ProjectTreeFlags Flags { get; } + + /// + /// A list of properties that might be displayed in property pages + /// (in BrowsableObject context). + /// + IImmutableDictionary Properties { get; } + + /// + /// Gets the set of child dependency IDs. May be empty, but never . + /// + /// + /// Each ID is of the form provided by the dependency model. + /// For dependencies obtained via MSBuild, these will be values. + /// + [Obsolete( + "IDependencyModel cannot model children. Transitive (non-top-level) dependencies are handled via a different set of APIs for performance reasons. " + + "Implementation of this property may throw, as it should never be called. " + + "For more information see https://github.com/dotnet/project-system/blob/main/docs/repo/dependencies-node-roadmap.md#transitive-dependencies")] + IImmutableList DependencyIDs { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IProjectDependenciesSubTreeProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IProjectDependenciesSubTreeProvider.cs index 11f1978da3..ce44818001 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IProjectDependenciesSubTreeProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Legacy/IProjectDependenciesSubTreeProvider.cs @@ -2,87 +2,86 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies; + +/// +/// Contract responsible for providing data about project dependencies of a specific type, +/// for example assemblies, projects, packages etc. +/// +[ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] +public interface IProjectDependenciesSubTreeProvider { /// - /// Contract responsible for providing data about project dependencies of a specific type, - /// for example assemblies, projects, packages etc. + /// Gets a string that uniquely identifies the type of dependency nodes emitted by this provider. /// - [ProjectSystemContract(ProjectSystemContractScope.UnconfiguredProject, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ZeroOrMore)] - public interface IProjectDependenciesSubTreeProvider - { - /// - /// Gets a string that uniquely identifies the type of dependency nodes emitted by this provider. - /// - /// - /// This string will be associated with provider's nodes (via project tree flags). - /// - string ProviderType { get; } + /// + /// This string will be associated with provider's nodes (via project tree flags). + /// + string ProviderType { get; } - /// - /// Returns the root (group) node for this provider's dependency nodes. - /// - /// - /// Despite the method's name, implementations may return the same instance for repeated - /// calls, so long as the returned value is immutable. - /// - IDependencyModel CreateRootDependencyNode(); + /// + /// Returns the root (group) node for this provider's dependency nodes. + /// + /// + /// Despite the method's name, implementations may return the same instance for repeated + /// calls, so long as the returned value is immutable. + /// + IDependencyModel CreateRootDependencyNode(); - /// - /// Raised when this provider's dependencies changed. - /// - event EventHandler DependenciesChanged; - } + /// + /// Raised when this provider's dependencies changed. + /// + event EventHandler DependenciesChanged; +} - public sealed class DependenciesChangedEventArgs +public sealed class DependenciesChangedEventArgs +{ + [Obsolete("Constructor includes unused properties. Use the non-obsolete overload instead.")] + public DependenciesChangedEventArgs( + IProjectDependenciesSubTreeProvider provider, + string? targetShortOrFullName, + IDependenciesChanges changes, + IProjectCatalogSnapshot? catalogs, + IImmutableDictionary? dataSourceVersions) + : this(provider, changes, CancellationToken.None) { - [Obsolete("Constructor includes unused properties. Use the non-obsolete overload instead.")] - public DependenciesChangedEventArgs( - IProjectDependenciesSubTreeProvider provider, - string? targetShortOrFullName, - IDependenciesChanges changes, - IProjectCatalogSnapshot? catalogs, - IImmutableDictionary? dataSourceVersions) - : this(provider, changes, CancellationToken.None) - { - } + } - [Obsolete("Constructor includes unused properties. Use the non-obsolete overload instead.")] - public DependenciesChangedEventArgs( - IProjectDependenciesSubTreeProvider provider, - string? targetShortOrFullName, - IDependenciesChanges changes, - CancellationToken token) - : this(provider, changes, CancellationToken.None) - { - Provider = provider; - Changes = changes; - Token = token; - } + [Obsolete("Constructor includes unused properties. Use the non-obsolete overload instead.")] + public DependenciesChangedEventArgs( + IProjectDependenciesSubTreeProvider provider, + string? targetShortOrFullName, + IDependenciesChanges changes, + CancellationToken token) + : this(provider, changes, CancellationToken.None) + { + Provider = provider; + Changes = changes; + Token = token; + } - /// - /// Initializes a new instance of . - /// - /// The subtree provider whose dependencies changed. - /// The collection of dependency changes. - /// A cancellation token that allows cancelling the update. - public DependenciesChangedEventArgs( - IProjectDependenciesSubTreeProvider provider, - IDependenciesChanges changes, - CancellationToken token) - { - Provider = provider; - Changes = changes; - Token = token; - } + /// + /// Initializes a new instance of . + /// + /// The subtree provider whose dependencies changed. + /// The collection of dependency changes. + /// A cancellation token that allows cancelling the update. + public DependenciesChangedEventArgs( + IProjectDependenciesSubTreeProvider provider, + IDependenciesChanges changes, + CancellationToken token) + { + Provider = provider; + Changes = changes; + Token = token; + } - public IProjectDependenciesSubTreeProvider Provider { get; } + public IProjectDependenciesSubTreeProvider Provider { get; } - [Obsolete("It is not possible to model configured dependencies via IProjectDependenciesSubTreeProvider.")] - public string? TargetShortOrFullName => null; + [Obsolete("It is not possible to model configured dependencies via IProjectDependenciesSubTreeProvider.")] + public string? TargetShortOrFullName => null; - public IDependenciesChanges Changes { get; } + public IDependenciesChanges Changes { get; } - public CancellationToken Token { get; } - } + public CancellationToken Token { get; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshot.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshot.cs index 75b64e6d4b..7b23edd747 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshot.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshot.cs @@ -1,176 +1,175 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot +namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot; + +/// +/// Immutable snapshot of a project's dependencies, both configured and unconfigured. +/// +/// +/// Only models top-level (direct) dependencies. No transitive dependencies are included. +/// +internal sealed class DependenciesSnapshot { + public DependenciesSnapshot( + ProjectConfigurationSlice primarySlice, + ImmutableDictionary dependenciesBySlice, + ImmutableDictionary> unconfiguredDependenciesByType) + { + Requires.Argument(dependenciesBySlice.ContainsKey(primarySlice), nameof(dependenciesBySlice), "Must contain the primary slice."); + + PrimarySlice = primarySlice; + DependenciesBySlice = dependenciesBySlice; + UnconfiguredDependenciesByType = unconfiguredDependenciesByType; + } + /// - /// Immutable snapshot of a project's dependencies, both configured and unconfigured. + /// Gets the slice representing the project's primary configuration. /// /// - /// Only models top-level (direct) dependencies. No transitive dependencies are included. + /// When a project has multiple slices, the primary one is the first in the list. + /// This information is used when creating the tree, where we only want one slice's + /// dependencies to be made available via DTE/hierarchy APIs. We use the primary slice + /// for consistency with other similar APIs. /// - internal sealed class DependenciesSnapshot - { - public DependenciesSnapshot( - ProjectConfigurationSlice primarySlice, - ImmutableDictionary dependenciesBySlice, - ImmutableDictionary> unconfiguredDependenciesByType) - { - Requires.Argument(dependenciesBySlice.ContainsKey(primarySlice), nameof(dependenciesBySlice), "Must contain the primary slice."); + public ProjectConfigurationSlice PrimarySlice { get; } - PrimarySlice = primarySlice; - DependenciesBySlice = dependenciesBySlice; - UnconfiguredDependenciesByType = unconfiguredDependenciesByType; - } + /// + /// Gets per-slice dependency snapshots. + /// + public ImmutableDictionary DependenciesBySlice { get; } - /// - /// Gets the slice representing the project's primary configuration. - /// - /// - /// When a project has multiple slices, the primary one is the first in the list. - /// This information is used when creating the tree, where we only want one slice's - /// dependencies to be made available via DTE/hierarchy APIs. We use the primary slice - /// for consistency with other similar APIs. - /// - public ProjectConfigurationSlice PrimarySlice { get; } - - /// - /// Gets per-slice dependency snapshots. - /// - public ImmutableDictionary DependenciesBySlice { get; } - - /// - /// Gets unconfigured dependencies, by their dependency type. - /// - public ImmutableDictionary> UnconfiguredDependenciesByType { get; } - - /// - /// Gets the maximum diagnostic level across all dependencies within the snapshot. - /// - /// - /// This value determines which overlay, if any, to display on the root "Dependencies" node in the - /// Solution Explorer, so that warnings and errors can be discovered even when the node is collapsed. - /// - public DiagnosticLevel MaximumDiagnosticLevel + /// + /// Gets unconfigured dependencies, by their dependency type. + /// + public ImmutableDictionary> UnconfiguredDependenciesByType { get; } + + /// + /// Gets the maximum diagnostic level across all dependencies within the snapshot. + /// + /// + /// This value determines which overlay, if any, to display on the root "Dependencies" node in the + /// Solution Explorer, so that warnings and errors can be discovered even when the node is collapsed. + /// + public DiagnosticLevel MaximumDiagnosticLevel + { + get { - get - { - DiagnosticLevel max = UnconfiguredDependenciesByType.GetMaximumDiagnosticLevel(); + DiagnosticLevel max = UnconfiguredDependenciesByType.GetMaximumDiagnosticLevel(); - foreach ((_, DependenciesSnapshotSlice snapshotSlice) in DependenciesBySlice) + foreach ((_, DependenciesSnapshotSlice snapshotSlice) in DependenciesBySlice) + { + if (snapshotSlice.MaximumDiagnosticLevel > max) { - if (snapshotSlice.MaximumDiagnosticLevel > max) - { - max = snapshotSlice.MaximumDiagnosticLevel; - } + max = snapshotSlice.MaximumDiagnosticLevel; } - - return max; } + + return max; } + } - public override string ToString() => $"{DependenciesBySlice.Count} slice{(DependenciesBySlice.Count == 1 ? "" : "s")}"; + public override string ToString() => $"{DependenciesBySlice.Count} slice{(DependenciesBySlice.Count == 1 ? "" : "s")}"; - internal DependenciesSnapshot Update( - ProjectConfigurationSlice primarySlice, - ImmutableDictionary configuredDependencies, - ImmutableDictionary> unconfiguredDependencies) + internal DependenciesSnapshot Update( + ProjectConfigurationSlice primarySlice, + ImmutableDictionary configuredDependencies, + ImmutableDictionary> unconfiguredDependencies) + { + if (ReferenceEquals(primarySlice, PrimarySlice) && + ReferenceEquals(configuredDependencies, DependenciesBySlice) && + ReferenceEquals(unconfiguredDependencies, UnconfiguredDependenciesByType)) { - if (ReferenceEquals(primarySlice, PrimarySlice) && - ReferenceEquals(configuredDependencies, DependenciesBySlice) && - ReferenceEquals(unconfiguredDependencies, UnconfiguredDependenciesByType)) - { - return this; - } - - return new(primarySlice, configuredDependencies, unconfiguredDependencies); + return this; } + return new(primarySlice, configuredDependencies, unconfiguredDependencies); + } + #if DEBUG - public override bool Equals(object? obj) - { - if (ReferenceEquals(this, obj)) - return true; + public override bool Equals(object? obj) + { + if (ReferenceEquals(this, obj)) + return true; - if (obj is not DependenciesSnapshot other) - return false; + if (obj is not DependenciesSnapshot other) + return false; - if (!Equals(PrimarySlice, other.PrimarySlice)) - return false; + if (!Equals(PrimarySlice, other.PrimarySlice)) + return false; - if (!CheckBySliceByGroup(DependenciesBySlice, other.DependenciesBySlice)) - return false; + if (!CheckBySliceByGroup(DependenciesBySlice, other.DependenciesBySlice)) + return false; - if (!CheckByGroup(UnconfiguredDependenciesByType, other.UnconfiguredDependenciesByType)) - return false; + if (!CheckByGroup(UnconfiguredDependenciesByType, other.UnconfiguredDependenciesByType)) + return false; - return true; + return true; + + static bool CheckBySliceByGroup(ImmutableDictionary a, ImmutableDictionary b) + { + if (a.Count != b.Count) + return false; + if (!a.Keys.ToHashSet().SetEquals(b.Keys)) + return false; - static bool CheckBySliceByGroup(ImmutableDictionary a, ImmutableDictionary b) + foreach ((ProjectConfigurationSlice slice, DependenciesSnapshotSlice x) in a) { - if (a.Count != b.Count) + DependenciesSnapshotSlice y = b[slice]; + + if (!Equals(x.Slice, y.Slice)) + return false; + if (!Equals(x.ConfiguredProject, y.ConfiguredProject)) return false; - if (!a.Keys.ToHashSet().SetEquals(b.Keys)) + if (!Equals(x.Catalogs, y.Catalogs)) return false; - foreach ((ProjectConfigurationSlice slice, DependenciesSnapshotSlice x) in a) - { - DependenciesSnapshotSlice y = b[slice]; + if (!CheckByGroup(x.DependenciesByType, y.DependenciesByType)) + return false; + } - if (!Equals(x.Slice, y.Slice)) - return false; - if (!Equals(x.ConfiguredProject, y.ConfiguredProject)) - return false; - if (!Equals(x.Catalogs, y.Catalogs)) - return false; + return true; + } - if (!CheckByGroup(x.DependenciesByType, y.DependenciesByType)) - return false; - } + static bool CheckByGroup(ImmutableDictionary> a, ImmutableDictionary> b) + { + if (a.Count != b.Count) + return false; + if (!a.Keys.ToHashSet().SetEquals(b.Keys)) + return false; - return true; + foreach ((DependencyGroupType groupType, ImmutableArray x) in a) + { + ImmutableArray y = b[groupType]; + + if (!CheckDependencies(x, y)) + return false; } - static bool CheckByGroup(ImmutableDictionary> a, ImmutableDictionary> b) + return true; + + static bool CheckDependencies(ImmutableArray a, ImmutableArray b) { - if (a.Count != b.Count) - return false; - if (!a.Keys.ToHashSet().SetEquals(b.Keys)) + if (a.Length != b.Length) return false; - foreach ((DependencyGroupType groupType, ImmutableArray x) in a) + for (int i = 0; i < a.Length; i++) { - ImmutableArray y = b[groupType]; + IDependency x = a[i]; + IDependency y = b[i]; - if (!CheckDependencies(x, y)) + if (!Equals(x, y)) return false; } return true; - - static bool CheckDependencies(ImmutableArray a, ImmutableArray b) - { - if (a.Length != b.Length) - return false; - - for (int i = 0; i < a.Length; i++) - { - IDependency x = a[i]; - IDependency y = b[i]; - - if (!Equals(x, y)) - return false; - } - - return true; - } } } + } - public override int GetHashCode() - { - // Suppress warning, only in debug configuration. - return base.GetHashCode(); - } -#endif + public override int GetHashCode() + { + // Suppress warning, only in debug configuration. + return base.GetHashCode(); } +#endif } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/MSBuildDependencies/PackageDependencyFactory.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/MSBuildDependencies/PackageDependencyFactory.cs index 1d221a5c06..98056316c1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/MSBuildDependencies/PackageDependencyFactory.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/MSBuildDependencies/PackageDependencyFactory.cs @@ -2,96 +2,95 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Subscriptions.MSBuildDependencies +namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Subscriptions.MSBuildDependencies; + +[Export(typeof(IMSBuildDependencyFactory))] +[AppliesTo(AppliesTo)] +internal sealed class PackageDependencyFactory : MSBuildDependencyFactoryBase { - [Export(typeof(IMSBuildDependencyFactory))] - [AppliesTo(AppliesTo)] - internal sealed class PackageDependencyFactory : MSBuildDependencyFactoryBase - { - public const string AppliesTo = ProjectCapability.DependenciesTree + " & " + ProjectCapabilities.PackageReferences; + public const string AppliesTo = ProjectCapability.DependenciesTree + " & " + ProjectCapabilities.PackageReferences; - private static readonly DependencyFlagCache s_flagCache = new( - resolved: DependencyTreeFlags.PackageDependency + DependencyTreeFlags.SupportsFolderBrowse, - unresolved: DependencyTreeFlags.PackageDependency); + private static readonly DependencyFlagCache s_flagCache = new( + resolved: DependencyTreeFlags.PackageDependency + DependencyTreeFlags.SupportsFolderBrowse, + unresolved: DependencyTreeFlags.PackageDependency); - // Resolved package reference items (via the _PackageDependenciesDesignTime target) have a - // composite item spec of form name/version, such as "MyPackage/1.2.3". - // - // The original (unresolved) name is available via the "Name" metadata. - // - // MyPackage/1.2.3 - // Resolved = True - // IsImplicitlyDefined = False - // Version = 1.2.3 - // Name = MyPackage - // Path = C:\Users\drnoakes\.nuget\packages\mypackage\1.2.3 - // - // Prior to 16.7 (SDK 3.1.4xx) an additional "Type" metadatum was present. + // Resolved package reference items (via the _PackageDependenciesDesignTime target) have a + // composite item spec of form name/version, such as "MyPackage/1.2.3". + // + // The original (unresolved) name is available via the "Name" metadata. + // + // MyPackage/1.2.3 + // Resolved = True + // IsImplicitlyDefined = False + // Version = 1.2.3 + // Name = MyPackage + // Path = C:\Users\drnoakes\.nuget\packages\mypackage\1.2.3 + // + // Prior to 16.7 (SDK 3.1.4xx) an additional "Type" metadatum was present. - public override DependencyGroupType DependencyGroupType => DependencyGroupTypes.Packages; + public override DependencyGroupType DependencyGroupType => DependencyGroupTypes.Packages; - public override string UnresolvedRuleName => PackageReference.SchemaName; - public override string ResolvedRuleName => ResolvedPackageReference.SchemaName; + public override string UnresolvedRuleName => PackageReference.SchemaName; + public override string ResolvedRuleName => ResolvedPackageReference.SchemaName; - public override string SchemaItemType => PackageReference.PrimaryDataSourceItemType; + public override string SchemaItemType => PackageReference.PrimaryDataSourceItemType; - public override ProjectImageMoniker Icon => KnownProjectImageMonikers.NuGetNoColor; - public override ProjectImageMoniker IconWarning => KnownProjectImageMonikers.NuGetNoColorWarning; - public override ProjectImageMoniker IconError => KnownProjectImageMonikers.NuGetNoColorError; - public override ProjectImageMoniker IconImplicit => KnownProjectImageMonikers.NuGetNoColorPrivate; + public override ProjectImageMoniker Icon => KnownProjectImageMonikers.NuGetNoColor; + public override ProjectImageMoniker IconWarning => KnownProjectImageMonikers.NuGetNoColorWarning; + public override ProjectImageMoniker IconError => KnownProjectImageMonikers.NuGetNoColorError; + public override ProjectImageMoniker IconImplicit => KnownProjectImageMonikers.NuGetNoColorPrivate; - public override DependencyFlagCache FlagCache => s_flagCache; + public override DependencyFlagCache FlagCache => s_flagCache; - protected internal override string GetUnresolvedCaption(string itemSpec, IImmutableDictionary unresolvedProperties) - { - string? version = unresolvedProperties.GetStringProperty(ProjectItemMetadata.Version); + protected internal override string GetUnresolvedCaption(string itemSpec, IImmutableDictionary unresolvedProperties) + { + string? version = unresolvedProperties.GetStringProperty(ProjectItemMetadata.Version); - return string.IsNullOrWhiteSpace(version) ? itemSpec : $"{itemSpec} ({version})"; - } + return string.IsNullOrWhiteSpace(version) ? itemSpec : $"{itemSpec} ({version})"; + } - protected internal override string GetResolvedCaption(string itemSpec, string? originalItemSpec, IImmutableDictionary resolvedProperties) - { - string? version = resolvedProperties.GetStringProperty(ProjectItemMetadata.Version); + protected internal override string GetResolvedCaption(string itemSpec, string? originalItemSpec, IImmutableDictionary resolvedProperties) + { + string? version = resolvedProperties.GetStringProperty(ProjectItemMetadata.Version); - return string.IsNullOrWhiteSpace(version) ? originalItemSpec ?? itemSpec : $"{originalItemSpec} ({version})"; - } + return string.IsNullOrWhiteSpace(version) ? originalItemSpec ?? itemSpec : $"{originalItemSpec} ({version})"; + } - protected internal override string? GetOriginalItemSpec(string resolvedItemSpec, IImmutableDictionary resolvedProperties) + protected internal override string? GetOriginalItemSpec(string resolvedItemSpec, IImmutableDictionary resolvedProperties) + { + // We have design-time build data + string? dependencyType = resolvedProperties.GetStringProperty(ProjectItemMetadata.Type); + + if (dependencyType is not null) { - // We have design-time build data - string? dependencyType = resolvedProperties.GetStringProperty(ProjectItemMetadata.Type); - - if (dependencyType is not null) - { - // In 16.7 (SDK 3.1.4xx) the format of ResolvedPackageReference items was changed in task PreprocessPackageDependenciesDesignTime. - // - // If we observe "Type" metadata then we are running with an older SDK, which is out of support. - // - // Legacy behavior had a few differences: - // - // - Both direct and transitive dependencies were included. - // - Packages for all targets were returned, even though we have a build per-target. - // - The package's ItemSpec included its target (for example: ".NETFramework,Version=v4.8/MyPackage/1.2.3"). - // - // From 16.7 to 17.5 we would attempt to parse the results of the outdated SDK. From 17.6, if we detect data - // of this format, we are no longer able to parse it and exclude these items. The 3.1 SDK went out of support before - // 17.5 was released. - - // Return null such that this item can not be considered for further processing, as it cannot - // be matched with an evaluated item without the original item spec. - return null; - } - - // Resolved package references have the version in the item spec (i.e. "MyPackage/1.2.3"), - // so we dig into the "Name" metadata to obtain the original item spec. + // In 16.7 (SDK 3.1.4xx) the format of ResolvedPackageReference items was changed in task PreprocessPackageDependenciesDesignTime. // - // This should always be present, as Name is required in PreprocessPackageDependenciesDesignTime from 16.7. - return resolvedProperties.GetStringProperty(ProjectItemMetadata.Name); - } + // If we observe "Type" metadata then we are running with an older SDK, which is out of support. + // + // Legacy behavior had a few differences: + // + // - Both direct and transitive dependencies were included. + // - Packages for all targets were returned, even though we have a build per-target. + // - The package's ItemSpec included its target (for example: ".NETFramework,Version=v4.8/MyPackage/1.2.3"). + // + // From 16.7 to 17.5 we would attempt to parse the results of the outdated SDK. From 17.6, if we detect data + // of this format, we are no longer able to parse it and exclude these items. The 3.1 SDK went out of support before + // 17.5 was released. - protected internal override ProjectTreeFlags UpdateTreeFlags(string id, ProjectTreeFlags flags) - { - return flags.Add($"$ID:{id}"); + // Return null such that this item can not be considered for further processing, as it cannot + // be matched with an evaluated item without the original item spec. + return null; } + + // Resolved package references have the version in the item spec (i.e. "MyPackage/1.2.3"), + // so we dig into the "Name" metadata to obtain the original item spec. + // + // This should always be present, as Name is required in PreprocessPackageDependenciesDesignTime from 16.7. + return resolvedProperties.GetStringProperty(ProjectItemMetadata.Name); + } + + protected internal override ProjectTreeFlags UpdateTreeFlags(string id, ProjectTreeFlags flags) + { + return flags.Add($"$ID:{id}"); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/ProjectItemMetadata.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/ProjectItemMetadata.cs index ad912707b8..7c906667f6 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/ProjectItemMetadata.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/ProjectItemMetadata.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Subscriptions +namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Subscriptions; + +/// +/// Names of metadata found on project items. +/// +internal static class ProjectItemMetadata { - /// - /// Names of metadata found on project items. - /// - internal static class ProjectItemMetadata - { - public const string Name = "Name"; - public const string Type = "Type"; - public const string Version = "Version"; - public const string IsImplicitlyDefined = "IsImplicitlyDefined"; - public const string DefiningProjectFullPath = "DefiningProjectFullPath"; - public const string OriginalItemSpec = "OriginalItemSpec"; - public const string Visible = "Visible"; - public const string DiagnosticLevel = "DiagnosticLevel"; - } + public const string Name = "Name"; + public const string Type = "Type"; + public const string Version = "Version"; + public const string IsImplicitlyDefined = "IsImplicitlyDefined"; + public const string DefiningProjectFullPath = "DefiningProjectFullPath"; + public const string OriginalItemSpec = "OriginalItemSpec"; + public const string Visible = "Visible"; + public const string DiagnosticLevel = "DiagnosticLevel"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/FileIconProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/FileIconProvider.cs index 6788b2b5fb..261da72b44 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/FileIconProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/FileIconProvider.cs @@ -4,41 +4,40 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.ProjectSystem.Tree; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree; + +[Export(typeof(IFileIconProvider))] +internal sealed class FileIconProvider : IFileIconProvider { - [Export(typeof(IFileIconProvider))] - internal sealed class FileIconProvider : IFileIconProvider - { - private readonly IUIImageService _imageService; + private readonly IUIImageService _imageService; - private ImmutableDictionary _imageMonikerByExtensions = ImmutableDictionary.Create(StringComparers.Paths); + private ImmutableDictionary _imageMonikerByExtensions = ImmutableDictionary.Create(StringComparers.Paths); - [ImportingConstructor] - public FileIconProvider(IUIImageService imageService) - { - _imageService = imageService; - } - - public ImageMoniker GetFileExtensionImageMoniker(string path) - { - Requires.NotNull(path); + [ImportingConstructor] + public FileIconProvider(IUIImageService imageService) + { + _imageService = imageService; + } - string extension = Path.GetExtension(path); + public ImageMoniker GetFileExtensionImageMoniker(string path) + { + Requires.NotNull(path); - return ImmutableInterlocked.GetOrAdd(ref _imageMonikerByExtensions, extension, GetImageMoniker, path); + string extension = Path.GetExtension(path); - ImageMoniker GetImageMoniker(string _, string p) - { - ImageMoniker imageMoniker = _imageService.GetImageMonikerForFile(p); + return ImmutableInterlocked.GetOrAdd(ref _imageMonikerByExtensions, extension, GetImageMoniker, path); - if (imageMoniker.Id == -1) - { - // No specific icon exists for this extension - imageMoniker = KnownMonikers.Document; - } + ImageMoniker GetImageMoniker(string _, string p) + { + ImageMoniker imageMoniker = _imageService.GetImageMonikerForFile(p); - return imageMoniker; + if (imageMoniker.Id == -1) + { + // No specific icon exists for this extension + imageMoniker = KnownMonikers.Document; } + + return imageMoniker; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/IFileIconProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/IFileIconProvider.cs index 0f39d77a91..64d1d654b1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/IFileIconProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/IFileIconProvider.cs @@ -3,20 +3,19 @@ using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree +namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree; + +/// +/// Provides icons for files based on their file names. +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne, Provider = ProjectSystemContractProvider.Private)] +public interface IFileIconProvider { /// - /// Provides icons for files based on their file names. + /// Gets the icon associated with 's extension. /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne, Provider = ProjectSystemContractProvider.Private)] - public interface IFileIconProvider - { - /// - /// Gets the icon associated with 's extension. - /// - /// - /// If no specific icon could be determined, is returned. - /// - ImageMoniker GetFileExtensionImageMoniker(string path); - } + /// + /// If no specific icon could be determined, is returned. + /// + ImageMoniker GetFileExtensionImageMoniker(string path); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/PackageContentProjectTreePropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/PackageContentProjectTreePropertiesProvider.cs index 103c1374cb..8d05cbeae1 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/PackageContentProjectTreePropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/PackageContentProjectTreePropertiesProvider.cs @@ -1,24 +1,23 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Tree +namespace Microsoft.VisualStudio.ProjectSystem.Tree; + +/// +/// Marks content items that come from packages as "user read-only". +/// +[Export(typeof(IProjectTreePropertiesProvider))] +[AppliesTo(ProjectCapability.PackageReferences)] +internal class PackageContentProjectTreePropertiesProvider : IProjectTreePropertiesProvider { - /// - /// Marks content items that come from packages as "user read-only". - /// - [Export(typeof(IProjectTreePropertiesProvider))] - [AppliesTo(ProjectCapability.PackageReferences)] - internal class PackageContentProjectTreePropertiesProvider : IProjectTreePropertiesProvider + public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) { - public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + // Package content items always come in as linked items, so to reduce + // the number of items we look at, we limit ourselves to them + if (propertyValues.Flags.Contains(ProjectTreeFlags.LinkedItem) && + propertyContext.Metadata is not null && + propertyContext.Metadata.TryGetValue(None.NuGetPackageIdProperty, out string packageId) && packageId.Length > 0) { - // Package content items always come in as linked items, so to reduce - // the number of items we look at, we limit ourselves to them - if (propertyValues.Flags.Contains(ProjectTreeFlags.LinkedItem) && - propertyContext.Metadata is not null && - propertyContext.Metadata.TryGetValue(None.NuGetPackageIdProperty, out string packageId) && packageId.Length > 0) - { - propertyValues.Flags |= ProjectTreeFlags.UserReadOnly; - } + propertyValues.Flags |= ProjectTreeFlags.UserReadOnly; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs index 323ca14409..e0867e82a2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectImports/ImportTreeProvider.cs @@ -7,368 +7,367 @@ using Microsoft.VisualStudio.ProjectSystem.Utilities; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.Tree.ProjectImports +namespace Microsoft.VisualStudio.ProjectSystem.Tree.ProjectImports; + +/// +/// Provides a top-level project sub-tree showing the tree of MSBuild project imports, +/// including props/targets from the SDK, targets, etc. +/// +/// +/// +/// To reduce clutter and memory usage, this tree is only visible (and populated) when +/// the "Show all Files" feature of Solution Explorer is enabled. +/// +/// +/// This feature can be enabled and disabled via the ProjectImportsTree project capability. +/// +/// +[Export(ExportContractNames.ProjectTreeProviders.PhysicalViewRootGraft, typeof(IProjectTreeProvider))] +[AppliesTo(ProjectCapability.ProjectImportsTree)] +internal sealed class ImportTreeProvider : ProjectTreeProviderBase, IProjectTreeProvider, IShowAllFilesProjectTreeProvider { - /// - /// Provides a top-level project sub-tree showing the tree of MSBuild project imports, - /// including props/targets from the SDK, targets, etc. - /// - /// - /// - /// To reduce clutter and memory usage, this tree is only visible (and populated) when - /// the "Show all Files" feature of Solution Explorer is enabled. - /// - /// - /// This feature can be enabled and disabled via the ProjectImportsTree project capability. - /// - /// - [Export(ExportContractNames.ProjectTreeProviders.PhysicalViewRootGraft, typeof(IProjectTreeProvider))] - [AppliesTo(ProjectCapability.ProjectImportsTree)] - internal sealed class ImportTreeProvider : ProjectTreeProviderBase, IProjectTreeProvider, IShowAllFilesProjectTreeProvider + private static readonly ProjectImageMoniker s_rootIcon = KnownMonikers.ProjectImports.ToProjectSystemType(); + private static readonly ProjectImageMoniker s_nodeIcon = KnownMonikers.TargetFile.ToProjectSystemType(); + private static readonly ProjectImageMoniker s_nodeImplicitIcon = KnownMonikers.TargetFilePrivate.ToProjectSystemType(); + + public static ProjectTreeFlags ProjectImport { get; } = ProjectTreeFlags.Create("ProjectImport"); + public static ProjectTreeFlags ProjectImportImplicit { get; } = ProjectTreeFlags.Create("ProjectImportImplicit"); + + private static readonly ProjectTreeFlags s_projectImportsTreeRootFlags = ProjectTreeFlags.Create( + ProjectTreeFlags.Common.BubbleUp | // sort to top of tree, not alphabetically + ProjectTreeFlags.Common.VirtualFolder | + ProjectTreeFlags.Common.DisableAddItemFolder); + + private static readonly ProjectTreeFlags s_projectImportFlags = ProjectImport | ProjectTreeFlags.FileOnDisk | ProjectTreeFlags.FileSystemEntity; + private static readonly ProjectTreeFlags s_projectImportImplicitFlags = s_projectImportFlags + ProjectImportImplicit; + + private readonly ProjectFileClassifier _projectFileClassifier = new(); + private readonly UnconfiguredProject _project; + private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; + private readonly IUnconfiguredProjectTasksService _unconfiguredProjectTasksService; + + private DisposableBag? _subscriptions; + private bool _showAllFiles; + + [ImportingConstructor] + internal ImportTreeProvider( + IProjectThreadingService threadingService, + UnconfiguredProject project, + IActiveConfiguredProjectSubscriptionService projectSubscriptionService, + IUnconfiguredProjectTasksService unconfiguredProjectTasksService) + : base(threadingService, project, useDisplayOrdering: true) { - private static readonly ProjectImageMoniker s_rootIcon = KnownMonikers.ProjectImports.ToProjectSystemType(); - private static readonly ProjectImageMoniker s_nodeIcon = KnownMonikers.TargetFile.ToProjectSystemType(); - private static readonly ProjectImageMoniker s_nodeImplicitIcon = KnownMonikers.TargetFilePrivate.ToProjectSystemType(); - - public static ProjectTreeFlags ProjectImport { get; } = ProjectTreeFlags.Create("ProjectImport"); - public static ProjectTreeFlags ProjectImportImplicit { get; } = ProjectTreeFlags.Create("ProjectImportImplicit"); - - private static readonly ProjectTreeFlags s_projectImportsTreeRootFlags = ProjectTreeFlags.Create( - ProjectTreeFlags.Common.BubbleUp | // sort to top of tree, not alphabetically - ProjectTreeFlags.Common.VirtualFolder | - ProjectTreeFlags.Common.DisableAddItemFolder); - - private static readonly ProjectTreeFlags s_projectImportFlags = ProjectImport | ProjectTreeFlags.FileOnDisk | ProjectTreeFlags.FileSystemEntity; - private static readonly ProjectTreeFlags s_projectImportImplicitFlags = s_projectImportFlags + ProjectImportImplicit; - - private readonly ProjectFileClassifier _projectFileClassifier = new(); - private readonly UnconfiguredProject _project; - private readonly IActiveConfiguredProjectSubscriptionService _projectSubscriptionService; - private readonly IUnconfiguredProjectTasksService _unconfiguredProjectTasksService; - - private DisposableBag? _subscriptions; - private bool _showAllFiles; + _project = project; + _projectSubscriptionService = projectSubscriptionService; + _unconfiguredProjectTasksService = unconfiguredProjectTasksService; + } - [ImportingConstructor] - internal ImportTreeProvider( - IProjectThreadingService threadingService, - UnconfiguredProject project, - IActiveConfiguredProjectSubscriptionService projectSubscriptionService, - IUnconfiguredProjectTasksService unconfiguredProjectTasksService) - : base(threadingService, project, useDisplayOrdering: true) + public override string? GetPath(IProjectTree node) + { + // Only process nodes belonging to our tree. + // This test excludes the root which is fine as it doesn't have a file path. + if (node.Flags.Contains(ProjectImport)) { - _project = project; - _projectSubscriptionService = projectSubscriptionService; - _unconfiguredProjectTasksService = unconfiguredProjectTasksService; + return node.FilePath; } - public override string? GetPath(IProjectTree node) + return null; + } + + public bool ShowAllFiles + { + get => _showAllFiles; + set { - // Only process nodes belonging to our tree. - // This test excludes the root which is fine as it doesn't have a file path. - if (node.Flags.Contains(ProjectImport)) + // Ensure we no-op if the value didn't change, avoiding race conditions + lock (SyncObject) { - return node.FilePath; + if (_showAllFiles == value) + { + return; + } + + _showAllFiles = value; } - return null; - } + ToggleTree(); - public bool ShowAllFiles - { - get => _showAllFiles; - set + return; + + void ToggleTree() { - // Ensure we no-op if the value didn't change, avoiding race conditions - lock (SyncObject) - { - if (_showAllFiles == value) + // Queue up an operation that will toggle the import tree + _ = _unconfiguredProjectTasksService.LoadedProjectAsync( + async () => { - return; - } - - _showAllFiles = value; - } + await TaskScheduler.Default.SwitchTo(alwaysYield: true); - ToggleTree(); + _unconfiguredProjectTasksService.UnloadCancellationToken.ThrowIfCancellationRequested(); - return; - - void ToggleTree() - { - // Queue up an operation that will toggle the import tree - _ = _unconfiguredProjectTasksService.LoadedProjectAsync( - async () => + lock (SyncObject) { - await TaskScheduler.Default.SwitchTo(alwaysYield: true); + Verify.NotDisposed(this); - _unconfiguredProjectTasksService.UnloadCancellationToken.ThrowIfCancellationRequested(); - - lock (SyncObject) + // Use the presence or absence of a subscription to indicate which operation we are performing here. + // This avoids a race condition between the (locked) changing of _showAllFiles and the (locked) changing + // of _subscriptions. Even if there is a race, the right number of toggles will occur and the end result + // will be correct. + if (_subscriptions is null) { - Verify.NotDisposed(this); - - // Use the presence or absence of a subscription to indicate which operation we are performing here. - // This avoids a race condition between the (locked) changing of _showAllFiles and the (locked) changing - // of _subscriptions. Even if there is a race, the right number of toggles will occur and the end result - // will be correct. - if (_subscriptions is null) - { - SetUpTree(); - } - else - { - TearDownTree(); - } + SetUpTree(); } - }); + else + { + TearDownTree(); + } + } + }); - return; + return; - void SetUpTree() - { - Assumes.Null(_subscriptions); + void SetUpTree() + { + Assumes.Null(_subscriptions); - // Set a visible root - _ = SubmitTreeUpdateAsync( - (currentTree, configuredProjectExports, token) => - { - // Update (make visible) or create a new tree if no prior one exists - IProjectTree tree = currentTree is null - ? NewTree(Resources.ImportsTreeNodeName, icon: s_rootIcon, flags: s_projectImportsTreeRootFlags) - : currentTree.Value.Tree.SetVisible(true); + // Set a visible root + _ = SubmitTreeUpdateAsync( + (currentTree, configuredProjectExports, token) => + { + // Update (make visible) or create a new tree if no prior one exists + IProjectTree tree = currentTree is null + ? NewTree(Resources.ImportsTreeNodeName, icon: s_rootIcon, flags: s_projectImportsTreeRootFlags) + : currentTree.Value.Tree.SetVisible(true); - return Task.FromResult(new TreeUpdateResult(tree)); - }); + return Task.FromResult(new TreeUpdateResult(tree)); + }); - // Subscribe to data to populate the tree and keep it updated with changes - IReceivableSourceBlock> importTreeSource - = _projectSubscriptionService.ImportTreeSource.SourceBlock; + // Subscribe to data to populate the tree and keep it updated with changes + IReceivableSourceBlock> importTreeSource + = _projectSubscriptionService.ImportTreeSource.SourceBlock; - IReceivableSourceBlock> projectRuleSource - = _projectSubscriptionService.ProjectRuleSource.SourceBlock; + IReceivableSourceBlock> projectRuleSource + = _projectSubscriptionService.ProjectRuleSource.SourceBlock; - IPropagatorBlock, IProjectVersionedValue> intermediateBlock - = DataflowBlockSlim.CreateSimpleBufferBlock>("Import Tree Intermediate: {1}"); + IPropagatorBlock, IProjectVersionedValue> intermediateBlock + = DataflowBlockSlim.CreateSimpleBufferBlock>("Import Tree Intermediate: {1}"); - _subscriptions ??= new DisposableBag(); + _subscriptions ??= new DisposableBag(); - _subscriptions.Add( - projectRuleSource.LinkTo( - intermediateBlock, - ruleNames: ConfigurationGeneral.SchemaName, - suppressVersionOnlyUpdates: false, - linkOptions: DataflowOption.PropagateCompletion)); + _subscriptions.Add( + projectRuleSource.LinkTo( + intermediateBlock, + ruleNames: ConfigurationGeneral.SchemaName, + suppressVersionOnlyUpdates: false, + linkOptions: DataflowOption.PropagateCompletion)); - ITargetBlock>> actionBlock = - DataflowBlockFactory.CreateActionBlock>>( - SyncTree, - _project, - skipIntermediateInputData: true, // We can skip versions without breaking the tree - nameFormat: "Import Tree Action: {1}"); + ITargetBlock>> actionBlock = + DataflowBlockFactory.CreateActionBlock>>( + SyncTree, + _project, + skipIntermediateInputData: true, // We can skip versions without breaking the tree + nameFormat: "Import Tree Action: {1}"); - _subscriptions.Add( - ProjectDataSources.SyncLinkTo( - importTreeSource.SyncLinkOptions(), - intermediateBlock.SyncLinkOptions(), - actionBlock, - linkOptions: DataflowOption.PropagateCompletion)); + _subscriptions.Add( + ProjectDataSources.SyncLinkTo( + importTreeSource.SyncLinkOptions(), + intermediateBlock.SyncLinkOptions(), + actionBlock, + linkOptions: DataflowOption.PropagateCompletion)); - JoinUpstreamDataSources(_projectSubscriptionService.ImportTreeSource, _projectSubscriptionService.ProjectRuleSource); + JoinUpstreamDataSources(_projectSubscriptionService.ImportTreeSource, _projectSubscriptionService.ProjectRuleSource); - return; + return; - void SyncTree(IProjectVersionedValue> e) + void SyncTree(IProjectVersionedValue> e) + { + if (e.Value.Item2.CurrentState.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectRuleSnapshot snapshot)) { - if (e.Value.Item2.CurrentState.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectRuleSnapshot snapshot)) + if (snapshot.Properties.TryGetStringProperty(ConfigurationGeneral.MSBuildProjectExtensionsPathProperty, out string? projectExtensionsPath)) { - if (snapshot.Properties.TryGetStringProperty(ConfigurationGeneral.MSBuildProjectExtensionsPathProperty, out string? projectExtensionsPath)) - { - _projectFileClassifier.ProjectExtensionsPath = projectExtensionsPath; - } + _projectFileClassifier.ProjectExtensionsPath = projectExtensionsPath; + } - if (snapshot.Properties.TryGetStringProperty(ConfigurationGeneral.NuGetPackageFoldersProperty, out string? nuGetPackageFolders)) - { - _projectFileClassifier.NuGetPackageFolders = nuGetPackageFolders; - } + if (snapshot.Properties.TryGetStringProperty(ConfigurationGeneral.NuGetPackageFoldersProperty, out string? nuGetPackageFolders)) + { + _projectFileClassifier.NuGetPackageFolders = nuGetPackageFolders; } + } - _ = SubmitTreeUpdateAsync( - (currentTree, configuredProjectExports, token) => - { - Assumes.NotNull(currentTree); + _ = SubmitTreeUpdateAsync( + (currentTree, configuredProjectExports, token) => + { + Assumes.NotNull(currentTree); + + IProjectTree updatedTree = SyncNode( + imports: e.Value.Item1.Value, + node: (IProjectTree2)currentTree.Value.Tree); - IProjectTree updatedTree = SyncNode( - imports: e.Value.Item1.Value, - node: (IProjectTree2)currentTree.Value.Tree); + return Task.FromResult(new TreeUpdateResult(updatedTree, e.DataSourceVersions)); + }); - return Task.FromResult(new TreeUpdateResult(updatedTree, e.DataSourceVersions)); - }); + return; - return; + IProjectTree2 SyncNode(IReadOnlyList imports, IProjectTree2 node) + { + Dictionary captionByProjectPath = GetCaptionByProjectPath(); - IProjectTree2 SyncNode(IReadOnlyList imports, IProjectTree2 node) + foreach (IProjectTree existingNode in node.Children) { - Dictionary captionByProjectPath = GetCaptionByProjectPath(); + Assumes.NotNullOrEmpty(existingNode.FilePath); - foreach (IProjectTree existingNode in node.Children) + if (!imports.Any(import => StringComparers.Paths.Equals(import.ProjectPath, existingNode.FilePath))) { - Assumes.NotNullOrEmpty(existingNode.FilePath); - - if (!imports.Any(import => StringComparers.Paths.Equals(import.ProjectPath, existingNode.FilePath))) + // Remove child that's no longer present + if (node.TryFind(existingNode.Identity, out IProjectTree? child)) { - // Remove child that's no longer present - if (node.TryFind(existingNode.Identity, out IProjectTree? child)) - { - node = (IProjectTree2)child.Remove(); - } + node = (IProjectTree2)child.Remove(); } } + } - for (int displayOrder = 0; displayOrder < imports.Count; displayOrder++) - { - IProjectImportSnapshot import = imports[displayOrder]; + for (int displayOrder = 0; displayOrder < imports.Count; displayOrder++) + { + IProjectImportSnapshot import = imports[displayOrder]; - // Attempt to find the existing child in the tree, based on file path - IProjectTree2? child = (IProjectTree2?)node.Children.FirstOrDefault(c => StringComparers.Paths.Equals(c.FilePath, import.ProjectPath)); + // Attempt to find the existing child in the tree, based on file path + IProjectTree2? child = (IProjectTree2?)node.Children.FirstOrDefault(c => StringComparers.Paths.Equals(c.FilePath, import.ProjectPath)); - if (child is null) - { - // No child exists for this import, so add it - bool isImplicit = _projectFileClassifier.IsNonUserEditable(import.ProjectPath); - ProjectTreeFlags flags = isImplicit ? s_projectImportImplicitFlags : s_projectImportFlags; - ProjectImageMoniker icon = isImplicit ? s_nodeImplicitIcon : s_nodeIcon; - string caption = captionByProjectPath[import.ProjectPath]; - - IProjectTree2 newChild = NewTree( - caption, - filePath: import.ProjectPath, - flags: flags, - icon: icon, - displayOrder: displayOrder); - - // Recur down the tree - newChild = SyncNode(import.Imports, newChild); - - node = AddChild(newChild); - } - else if (child.DisplayOrder != displayOrder) - { - // Child exists but with the wrong display order - node = (IProjectTree2)child.SetDisplayOrder(displayOrder).Parent!; - } - else - { - // Child exists with correct display order, so continue walking tree - IProjectTree2 newChild = SyncNode(import.Imports, child); + if (child is null) + { + // No child exists for this import, so add it + bool isImplicit = _projectFileClassifier.IsNonUserEditable(import.ProjectPath); + ProjectTreeFlags flags = isImplicit ? s_projectImportImplicitFlags : s_projectImportFlags; + ProjectImageMoniker icon = isImplicit ? s_nodeImplicitIcon : s_nodeIcon; + string caption = captionByProjectPath[import.ProjectPath]; + + IProjectTree2 newChild = NewTree( + caption, + filePath: import.ProjectPath, + flags: flags, + icon: icon, + displayOrder: displayOrder); + + // Recur down the tree + newChild = SyncNode(import.Imports, newChild); + + node = AddChild(newChild); + } + else if (child.DisplayOrder != displayOrder) + { + // Child exists but with the wrong display order + node = (IProjectTree2)child.SetDisplayOrder(displayOrder).Parent!; + } + else + { + // Child exists with correct display order, so continue walking tree + IProjectTree2 newChild = SyncNode(import.Imports, child); - if (!ReferenceEquals(child, newChild)) - { - node = ReplaceChild(child, newChild); - } + if (!ReferenceEquals(child, newChild)) + { + node = ReplaceChild(child, newChild); } } + } - return node; + return node; - IProjectTree2 AddChild(IProjectTree2 child) => (IProjectTree2)node.Add(child).Parent!; - IProjectTree2 ReplaceChild(IProjectTree2 oldChild, IProjectTree2 newChild) => (IProjectTree2)node.Remove(oldChild).Add(newChild).Parent!; + IProjectTree2 AddChild(IProjectTree2 child) => (IProjectTree2)node.Add(child).Parent!; + IProjectTree2 ReplaceChild(IProjectTree2 oldChild, IProjectTree2 newChild) => (IProjectTree2)node.Remove(oldChild).Add(newChild).Parent!; - Dictionary GetCaptionByProjectPath() - { - // Creates a map from each import's project path to the filename of that project path. - // Although LINQ's Enumerable.ToDictionary extension method can be used to create such - // a map, that implementation fails when there are duplicate entries in the imports. - // Therefore, an extension method that allows duplicate keys to be ignored - // is used to create the map. - var result = imports.ToDictionary(i => i.ProjectPath, i => Path.GetFileName(i.ProjectPath), StringComparers.Paths, ignoreDuplicateKeys: true); + Dictionary GetCaptionByProjectPath() + { + // Creates a map from each import's project path to the filename of that project path. + // Although LINQ's Enumerable.ToDictionary extension method can be used to create such + // a map, that implementation fails when there are duplicate entries in the imports. + // Therefore, an extension method that allows duplicate keys to be ignored + // is used to create the map. + var result = imports.ToDictionary(i => i.ProjectPath, i => Path.GetFileName(i.ProjectPath), StringComparers.Paths, ignoreDuplicateKeys: true); - var isDuplicateByFileName = new Dictionary(StringComparers.Paths); + var isDuplicateByFileName = new Dictionary(StringComparers.Paths); - foreach ((string _, string fileName) in result) - { - isDuplicateByFileName[fileName] = isDuplicateByFileName.ContainsKey(fileName); - } + foreach ((string _, string fileName) in result) + { + isDuplicateByFileName[fileName] = isDuplicateByFileName.ContainsKey(fileName); + } - foreach (IProjectImportSnapshot import in imports) + foreach (IProjectImportSnapshot import in imports) + { + string fileName = result[import.ProjectPath]; + + if (isDuplicateByFileName[fileName]) { - string fileName = result[import.ProjectPath]; - - if (isDuplicateByFileName[fileName]) - { - result[import.ProjectPath] = $"{fileName} ({Path.GetDirectoryName(import.ProjectPath)})"; - } + result[import.ProjectPath] = $"{fileName} ({Path.GetDirectoryName(import.ProjectPath)})"; } - - return result; } + + return result; } } } + } - void TearDownTree() - { - Assumes.NotNull(_subscriptions); - _subscriptions.Dispose(); - _subscriptions = null; + void TearDownTree() + { + Assumes.NotNull(_subscriptions); + _subscriptions.Dispose(); + _subscriptions = null; - _ = SubmitTreeUpdateAsync( - (currentTree, configuredProjectExports, token) => - { - Assumes.NotNull(currentTree); + _ = SubmitTreeUpdateAsync( + (currentTree, configuredProjectExports, token) => + { + Assumes.NotNull(currentTree); - IProjectTree tree = currentTree.Value.Tree; + IProjectTree tree = currentTree.Value.Tree; - // Set invisible - tree = tree.SetVisible(false); + // Set invisible + tree = tree.SetVisible(false); - // Remove all children - while (tree.Children.Count != 0) - { - tree = tree.Children[0].Remove(); - } + // Remove all children + while (tree.Children.Count != 0) + { + tree = tree.Children[0].Remove(); + } - return Task.FromResult(new TreeUpdateResult(tree)); - }); - } + return Task.FromResult(new TreeUpdateResult(tree)); + }); } } } + } - // CPS's physical tree provider currently only uses the IShowAllFilesProjectTreeProvider.ShowAllFiles property - // for graft root providers. These other interface methods have safe no-op implementations in case that changes - // one day. + // CPS's physical tree provider currently only uses the IShowAllFilesProjectTreeProvider.ShowAllFiles property + // for graft root providers. These other interface methods have safe no-op implementations in case that changes + // one day. - bool IShowAllFilesProjectTreeProvider.CanIncludeItems(IImmutableSet nodes) => true; - bool IShowAllFilesProjectTreeProvider.CanExcludeItems(IImmutableSet nodes) => true; - Task IShowAllFilesProjectTreeProvider.IncludeItemsAsync(IImmutableSet nodes) => Task.CompletedTask; - Task IShowAllFilesProjectTreeProvider.ExcludeItemsAsync(IImmutableSet nodes) => Task.CompletedTask; + bool IShowAllFilesProjectTreeProvider.CanIncludeItems(IImmutableSet nodes) => true; + bool IShowAllFilesProjectTreeProvider.CanExcludeItems(IImmutableSet nodes) => true; + Task IShowAllFilesProjectTreeProvider.IncludeItemsAsync(IImmutableSet nodes) => Task.CompletedTask; + Task IShowAllFilesProjectTreeProvider.ExcludeItemsAsync(IImmutableSet nodes) => Task.CompletedTask; - protected override ConfiguredProjectExports GetActiveConfiguredProjectExports(ConfiguredProject newActiveConfiguredProject) - { - Requires.NotNull(newActiveConfiguredProject); + protected override ConfiguredProjectExports GetActiveConfiguredProjectExports(ConfiguredProject newActiveConfiguredProject) + { + Requires.NotNull(newActiveConfiguredProject); - return GetActiveConfiguredProjectExports(newActiveConfiguredProject); - } + return GetActiveConfiguredProjectExports(newActiveConfiguredProject); + } - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) + { + if (disposing) { - if (disposing) - { - _subscriptions?.Dispose(); - } - - base.Dispose(disposing); + _subscriptions?.Dispose(); } - [Export] - private sealed class MyConfiguredProjectExports : ConfiguredProjectExports + base.Dispose(disposing); + } + + [Export] + private sealed class MyConfiguredProjectExports : ConfiguredProjectExports + { + [ImportingConstructor] + public MyConfiguredProjectExports(ConfiguredProject configuredProject) + : base(configuredProject) { - [ImportingConstructor] - public MyConfiguredProjectExports(ConfiguredProject configuredProject) - : base(configuredProject) - { - } } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectRootImageProjectTreePropertiesProvider.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectRootImageProjectTreePropertiesProvider.cs index 6cdbb45e88..5b1b4a52c5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectRootImageProjectTreePropertiesProvider.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/ProjectRootImageProjectTreePropertiesProvider.cs @@ -3,54 +3,53 @@ using Microsoft.VisualStudio.ProjectSystem.Imaging; using ManagedPriorityOrder = Microsoft.VisualStudio.ProjectSystem.Order; -namespace Microsoft.VisualStudio.ProjectSystem.Tree +namespace Microsoft.VisualStudio.ProjectSystem.Tree; + +/// +/// Modifies the Solution Explorer tree image for the project root. +/// +[Export(typeof(IProjectTreePropertiesProvider))] +[AppliesTo(ProjectCapability.DotNet)] +[Order(ManagedPriorityOrder.Default)] +internal class ProjectRootImageProjectTreePropertiesProvider : IProjectTreePropertiesProvider { - /// - /// Modifies the Solution Explorer tree image for the project root. - /// - [Export(typeof(IProjectTreePropertiesProvider))] - [AppliesTo(ProjectCapability.DotNet)] - [Order(ManagedPriorityOrder.Default)] - internal class ProjectRootImageProjectTreePropertiesProvider : IProjectTreePropertiesProvider + private readonly IProjectCapabilitiesService _capabilities; + private readonly IProjectImageProvider _imageProvider; + + [ImportingConstructor] + public ProjectRootImageProjectTreePropertiesProvider(IProjectCapabilitiesService capabilities, [Import(typeof(ProjectImageProviderAggregator))]IProjectImageProvider imageProvider) { - private readonly IProjectCapabilitiesService _capabilities; - private readonly IProjectImageProvider _imageProvider; + _capabilities = capabilities; + _imageProvider = imageProvider; + } - [ImportingConstructor] - public ProjectRootImageProjectTreePropertiesProvider(IProjectCapabilitiesService capabilities, [Import(typeof(ProjectImageProviderAggregator))]IProjectImageProvider imageProvider) - { - _capabilities = capabilities; - _imageProvider = imageProvider; - } + private bool IsSharedProject + { + get { return _capabilities.Contains(ProjectCapabilities.SharedAssetsProject); } + } - private bool IsSharedProject + public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + { + Requires.NotNull(propertyContext); + Requires.NotNull(propertyValues); + + if (propertyValues.Flags.Contains(ProjectTreeFlags.Common.ProjectRoot)) { - get { return _capabilities.Contains(ProjectCapabilities.SharedAssetsProject); } + SetImage(propertyValues, IsSharedProject ? ProjectImageKey.SharedProjectRoot : ProjectImageKey.ProjectRoot); } - - public void CalculatePropertyValues(IProjectTreeCustomizablePropertyContext propertyContext, IProjectTreeCustomizablePropertyValues propertyValues) + else if (propertyValues.Flags.Contains(ProjectTreeFlags.Common.SharedItemsImportFile)) { - Requires.NotNull(propertyContext); - Requires.NotNull(propertyValues); - - if (propertyValues.Flags.Contains(ProjectTreeFlags.Common.ProjectRoot)) - { - SetImage(propertyValues, IsSharedProject ? ProjectImageKey.SharedProjectRoot : ProjectImageKey.ProjectRoot); - } - else if (propertyValues.Flags.Contains(ProjectTreeFlags.Common.SharedItemsImportFile)) - { - SetImage(propertyValues, ProjectImageKey.SharedItemsImportFile); - } + SetImage(propertyValues, ProjectImageKey.SharedItemsImportFile); } + } - private void SetImage(IProjectTreeCustomizablePropertyValues propertyValues, string imageKey) - { - ProjectImageMoniker? icon = _imageProvider.GetProjectImage(imageKey); + private void SetImage(IProjectTreeCustomizablePropertyValues propertyValues, string imageKey) + { + ProjectImageMoniker? icon = _imageProvider.GetProjectImage(imageKey); - if (icon is not null) - { - propertyValues.Icon = icon; - } + if (icon is not null) + { + propertyValues.Icon = icon; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectCommonServices.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectCommonServices.cs index 44c3059d9c..60e7ac78cb 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectCommonServices.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectCommonServices.cs @@ -1,54 +1,53 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +/// +/// Provides a default implementation of . +/// +[Export(typeof(IUnconfiguredProjectCommonServices))] +internal class UnconfiguredProjectCommonServices : IUnconfiguredProjectCommonServices { - /// - /// Provides a default implementation of . - /// - [Export(typeof(IUnconfiguredProjectCommonServices))] - internal class UnconfiguredProjectCommonServices : IUnconfiguredProjectCommonServices + private readonly UnconfiguredProject _project; + private readonly Lazy _threadingService; + private readonly Lazy _projectAccessor; + private readonly IActiveConfiguredValue _activeConfiguredProject; + private readonly IActiveConfiguredValue _activeConfiguredProjectProperties; + + [ImportingConstructor] + public UnconfiguredProjectCommonServices(UnconfiguredProject project, Lazy threadingService, + IActiveConfiguredValue activeConfiguredProject, IActiveConfiguredValue activeConfiguredProjectProperties, + Lazy projectAccessor) + { + _project = project; + _threadingService = threadingService; + _activeConfiguredProject = activeConfiguredProject; + _activeConfiguredProjectProperties = activeConfiguredProjectProperties; + _projectAccessor = projectAccessor; + } + + public IProjectThreadingService ThreadingService + { + get { return _threadingService.Value; } + } + + public UnconfiguredProject Project + { + get { return _project; } + } + + public ConfiguredProject ActiveConfiguredProject + { + get { return _activeConfiguredProject.Value; } + } + + public ProjectProperties ActiveConfiguredProjectProperties + { + get { return _activeConfiguredProjectProperties.Value; } + } + + public IProjectAccessor ProjectAccessor { - private readonly UnconfiguredProject _project; - private readonly Lazy _threadingService; - private readonly Lazy _projectAccessor; - private readonly IActiveConfiguredValue _activeConfiguredProject; - private readonly IActiveConfiguredValue _activeConfiguredProjectProperties; - - [ImportingConstructor] - public UnconfiguredProjectCommonServices(UnconfiguredProject project, Lazy threadingService, - IActiveConfiguredValue activeConfiguredProject, IActiveConfiguredValue activeConfiguredProjectProperties, - Lazy projectAccessor) - { - _project = project; - _threadingService = threadingService; - _activeConfiguredProject = activeConfiguredProject; - _activeConfiguredProjectProperties = activeConfiguredProjectProperties; - _projectAccessor = projectAccessor; - } - - public IProjectThreadingService ThreadingService - { - get { return _threadingService.Value; } - } - - public UnconfiguredProject Project - { - get { return _project; } - } - - public ConfiguredProject ActiveConfiguredProject - { - get { return _activeConfiguredProject.Value; } - } - - public ProjectProperties ActiveConfiguredProjectProperties - { - get { return _activeConfiguredProjectProperties.Value; } - } - - public IProjectAccessor ProjectAccessor - { - get { return _projectAccessor.Value; } - } + get { return _projectAccessor.Value; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectTasksService.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectTasksService.cs index d64d5c6bcc..74006643a8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectTasksService.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/UnconfiguredProjectTasksService.cs @@ -2,126 +2,125 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Export] +[Export(typeof(IUnconfiguredProjectTasksService))] +[AppliesTo(ProjectCapability.DotNet)] +internal class UnconfiguredProjectTasksService : IUnconfiguredProjectTasksService { - [Export] - [Export(typeof(IUnconfiguredProjectTasksService))] + private readonly IProjectAsynchronousTasksService _tasksService; + private readonly IProjectThreadingService _threadingService; + private readonly ILoadedInHostListener? _loadedInHostListener; + private readonly ISolutionService? _solutionService; + private readonly TaskCompletionSource _projectLoadedInHost = new(); + private readonly TaskCompletionSource _prioritizedProjectLoadedInHost = new(); + private readonly JoinableTaskCollection _prioritizedTasks; + + [ImportingConstructor] + public UnconfiguredProjectTasksService( + [Import(ExportContractNames.Scopes.UnconfiguredProject)]IProjectAsynchronousTasksService tasksService, + IProjectThreadingService threadingService, + [Import(AllowDefault = true)] ILoadedInHostListener? loadedInHostListener, + [Import(AllowDefault = true)] ISolutionService? solutionService) + { + _prioritizedTasks = threadingService.JoinableTaskContext.CreateCollection(); + _prioritizedTasks.DisplayName = "PrioritizedProjectLoadedInHostTasks"; + _tasksService = tasksService; + _threadingService = threadingService; + _loadedInHostListener = loadedInHostListener; + _solutionService = solutionService; + } + + [ProjectAutoLoad(completeBy: ProjectLoadCheckpoint.ProjectFactoryCompleted)] [AppliesTo(ProjectCapability.DotNet)] - internal class UnconfiguredProjectTasksService : IUnconfiguredProjectTasksService + public Task OnProjectFactoryCompleted() { - private readonly IProjectAsynchronousTasksService _tasksService; - private readonly IProjectThreadingService _threadingService; - private readonly ILoadedInHostListener? _loadedInHostListener; - private readonly ISolutionService? _solutionService; - private readonly TaskCompletionSource _projectLoadedInHost = new(); - private readonly TaskCompletionSource _prioritizedProjectLoadedInHost = new(); - private readonly JoinableTaskCollection _prioritizedTasks; - - [ImportingConstructor] - public UnconfiguredProjectTasksService( - [Import(ExportContractNames.Scopes.UnconfiguredProject)]IProjectAsynchronousTasksService tasksService, - IProjectThreadingService threadingService, - [Import(AllowDefault = true)] ILoadedInHostListener? loadedInHostListener, - [Import(AllowDefault = true)] ISolutionService? solutionService) + if (_loadedInHostListener is not null) { - _prioritizedTasks = threadingService.JoinableTaskContext.CreateCollection(); - _prioritizedTasks.DisplayName = "PrioritizedProjectLoadedInHostTasks"; - _tasksService = tasksService; - _threadingService = threadingService; - _loadedInHostListener = loadedInHostListener; - _solutionService = solutionService; + return _loadedInHostListener.StartListeningAsync(); } - - [ProjectAutoLoad(completeBy: ProjectLoadCheckpoint.ProjectFactoryCompleted)] - [AppliesTo(ProjectCapability.DotNet)] - public Task OnProjectFactoryCompleted() + else { - if (_loadedInHostListener is not null) - { - return _loadedInHostListener.StartListeningAsync(); - } - else - { - _projectLoadedInHost.TrySetResult(); - return Task.CompletedTask; - } + _projectLoadedInHost.TrySetResult(); + return Task.CompletedTask; } + } - public Task ProjectLoadedInHost - { - get { return _projectLoadedInHost.Task.WithCancellation(_tasksService.UnloadCancellationToken); } - } + public Task ProjectLoadedInHost + { + get { return _projectLoadedInHost.Task.WithCancellation(_tasksService.UnloadCancellationToken); } + } - public Task PrioritizedProjectLoadedInHost - { - get { return _prioritizedProjectLoadedInHost.Task.WithCancellation(_tasksService.UnloadCancellationToken); } - } + public Task PrioritizedProjectLoadedInHost + { + get { return _prioritizedProjectLoadedInHost.Task.WithCancellation(_tasksService.UnloadCancellationToken); } + } - public Task SolutionLoadedInHost - { + public Task SolutionLoadedInHost + { #pragma warning disable VSTHRD110 // Observe result of async calls (https://github.com/microsoft/vs-threading/issues/881) - get { return _solutionService?.LoadedInHost.WithCancellation(_tasksService.UnloadCancellationToken) ?? throw new NotSupportedException(); } + get { return _solutionService?.LoadedInHost.WithCancellation(_tasksService.UnloadCancellationToken) ?? throw new NotSupportedException(); } #pragma warning restore VSTHRD110 // Observe result of async calls - } + } - public CancellationToken UnloadCancellationToken - { - get { return _tasksService.UnloadCancellationToken; } - } + public CancellationToken UnloadCancellationToken + { + get { return _tasksService.UnloadCancellationToken; } + } #pragma warning disable RS0030 // Do not use LoadedProjectAsync (this is the replacement) - public Task LoadedProjectAsync(Func action) - { - JoinableTask joinable = _tasksService.LoadedProjectAsync(action); + public Task LoadedProjectAsync(Func action) + { + JoinableTask joinable = _tasksService.LoadedProjectAsync(action); - return joinable.Task; - } + return joinable.Task; + } - public Task LoadedProjectAsync(Func> action) - { - JoinableTask joinable = _tasksService.LoadedProjectAsync(action); + public Task LoadedProjectAsync(Func> action) + { + JoinableTask joinable = _tasksService.LoadedProjectAsync(action); - return joinable.Task; - } + return joinable.Task; + } #pragma warning restore RS0030 // Do not use LoadedProjectAsync - public Task PrioritizedProjectLoadedInHostAsync(Func> action) - { - Requires.NotNull(action); + public Task PrioritizedProjectLoadedInHostAsync(Func> action) + { + Requires.NotNull(action); - _tasksService.UnloadCancellationToken.ThrowIfCancellationRequested(); + _tasksService.UnloadCancellationToken.ThrowIfCancellationRequested(); - JoinableTask task = _threadingService.JoinableTaskFactory.RunAsync(action); + JoinableTask task = _threadingService.JoinableTaskFactory.RunAsync(action); - _prioritizedTasks.Add(task); + _prioritizedTasks.Add(task); - return task.Task; - } + return task.Task; + } - public Task PrioritizedProjectLoadedInHostAsync(Func action) - { - Requires.NotNull(action); + public Task PrioritizedProjectLoadedInHostAsync(Func action) + { + Requires.NotNull(action); - _tasksService.UnloadCancellationToken.ThrowIfCancellationRequested(); + _tasksService.UnloadCancellationToken.ThrowIfCancellationRequested(); - JoinableTask task = _threadingService.JoinableTaskFactory.RunAsync(action); + JoinableTask task = _threadingService.JoinableTaskFactory.RunAsync(action); - _prioritizedTasks.Add(task); + _prioritizedTasks.Add(task); - return task.Task; - } + return task.Task; + } - public void OnProjectLoadedInHost() - { - _projectLoadedInHost.SetResult(); - } + public void OnProjectLoadedInHost() + { + _projectLoadedInHost.SetResult(); + } - public void OnPrioritizedProjectLoadedInHost() - { - _prioritizedProjectLoadedInHost.SetResult(); + public void OnPrioritizedProjectLoadedInHost() + { + _prioritizedProjectLoadedInHost.SetResult(); - _threadingService.ExecuteSynchronously(_prioritizedTasks.JoinTillEmptyAsync); - } + _threadingService.ExecuteSynchronously(_prioritizedTasks.JoinTillEmptyAsync); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/AsyncDisposable.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/AsyncDisposable.cs index 353017759b..a480e07c68 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/AsyncDisposable.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/AsyncDisposable.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal sealed class AsyncDisposable : IAsyncDisposable { - internal sealed class AsyncDisposable : IAsyncDisposable - { - private readonly Func _callback; + private readonly Func _callback; - private int _isDisposed; + private int _isDisposed; - public AsyncDisposable(Func callback) => _callback = callback; + public AsyncDisposable(Func callback) => _callback = callback; - public async ValueTask DisposeAsync() + public async ValueTask DisposeAsync() + { + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) { - if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) - { - await _callback(); - } + await _callback(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableBag.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableBag.cs index 62c63ae034..b07310fb11 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableBag.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableBag.cs @@ -2,72 +2,71 @@ using System.Collections; -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +/// +/// Tracks a bag of distinct disposable objects which will be disposed when the bag itself is disposed. +/// +internal sealed class DisposableBag : IDisposable, IEnumerable { /// - /// Tracks a bag of distinct disposable objects which will be disposed when the bag itself is disposed. + /// The set of disposable blocks. If , then this disposable bag has been disposed. + /// + private ImmutableHashSet? _disposables = ImmutableHashSet.Create(); + + /// + /// Disposes of all contained disposable items. /// - internal sealed class DisposableBag : IDisposable, IEnumerable + public void Dispose() { - /// - /// The set of disposable blocks. If , then this disposable bag has been disposed. - /// - private ImmutableHashSet? _disposables = ImmutableHashSet.Create(); + ImmutableHashSet? disposables = Interlocked.Exchange(ref _disposables, null); - /// - /// Disposes of all contained disposable items. - /// - public void Dispose() + if (disposables is not null) { - ImmutableHashSet? disposables = Interlocked.Exchange(ref _disposables, null); - - if (disposables is not null) + foreach (IDisposable? item in disposables) { - foreach (IDisposable? item in disposables) - { - item?.Dispose(); - } + item?.Dispose(); } } + } - /// - /// Adds an object to this bag, to be disposed when the bag itself is disposed. - /// - /// - /// If this disposable bag has already been disposed, will be disposed immediately. - /// - /// The value to be included in this disposable bag. - public void Add(IDisposable? disposable) + /// + /// Adds an object to this bag, to be disposed when the bag itself is disposed. + /// + /// + /// If this disposable bag has already been disposed, will be disposed immediately. + /// + /// The value to be included in this disposable bag. + public void Add(IDisposable? disposable) + { + if (disposable is null) { - if (disposable is null) - { - return; - } + return; + } - bool shouldDisposeArgument = false; - ImmutableInterlocked.Update( - ref _disposables, - (set, item) => + bool shouldDisposeArgument = false; + ImmutableInterlocked.Update( + ref _disposables, + (set, item) => + { + if (set is null) { - if (set is null) - { - shouldDisposeArgument = true; - return null!; - } + shouldDisposeArgument = true; + return null!; + } - return set.Add(item); - }, - disposable); + return set.Add(item); + }, + disposable); - if (shouldDisposeArgument) - { - disposable.Dispose(); - } + if (shouldDisposeArgument) + { + disposable.Dispose(); } - - /// - /// Implemented only to allow collection initialization of this type. - /// - IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); } + + /// + /// Implemented only to allow collection initialization of this type. + /// + IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableDelegate.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableDelegate.cs index be7ca61381..91da8faaa9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableDelegate.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/DisposableDelegate.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +/// +/// Wraps an delegate as an , ensuring it is called +/// only once, the first time is called. +/// +internal sealed class DisposableDelegate : IDisposable { - /// - /// Wraps an delegate as an , ensuring it is called - /// only once, the first time is called. - /// - internal sealed class DisposableDelegate : IDisposable - { - private Action? _onDispose; + private Action? _onDispose; - public DisposableDelegate(Action onDispose) - { - Requires.NotNull(onDispose); + public DisposableDelegate(Action onDispose) + { + Requires.NotNull(onDispose); - _onDispose = onDispose; - } + _onDispose = onDispose; + } - public void Dispose() - { - // Prevent double-dispose, and null field out on dispose - Action? action = Interlocked.Exchange(ref _onDispose, null); + public void Dispose() + { + // Prevent double-dispose, and null field out on dispose + Action? action = Interlocked.Exchange(ref _onDispose, null); - action?.Invoke(); - } + action?.Invoke(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EmptyDisposable.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EmptyDisposable.cs index 24ec09aa83..9be745dea9 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EmptyDisposable.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EmptyDisposable.cs @@ -1,11 +1,10 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +internal sealed class EmptyDisposable : IDisposable { - internal sealed class EmptyDisposable : IDisposable - { - public static IDisposable Instance { get; } = new EmptyDisposable(); + public static IDisposable Instance { get; } = new EmptyDisposable(); - public void Dispose() { } - } + public void Dispose() { } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EnvironmentHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EnvironmentHelper.cs index 67d8d69684..0dd3048d4b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EnvironmentHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/EnvironmentHelper.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +/// +/// Wrapper over System.Environment abstraction for unit testing +/// +[Export(typeof(IEnvironmentHelper))] +internal class EnvironmentHelper : IEnvironmentHelper { - /// - /// Wrapper over System.Environment abstraction for unit testing - /// - [Export(typeof(IEnvironmentHelper))] - internal class EnvironmentHelper : IEnvironmentHelper + public string? GetEnvironmentVariable(string name) { - public string? GetEnvironmentVariable(string name) - { - return Environment.GetEnvironmentVariable(name); - } + return Environment.GetEnvironmentVariable(name); + } - public string ExpandEnvironmentVariables(string name) + public string ExpandEnvironmentVariables(string name) + { + if (name.IndexOf('%') == -1) { - if (name.IndexOf('%') == -1) - { - // There cannot be any environment variables in this string. - // Avoid several allocations in the .NET Framework's implementation - // of Environment.ExpandEnvironmentVariables. - return name; - } - - return Environment.ExpandEnvironmentVariables(name); + // There cannot be any environment variables in this string. + // Avoid several allocations in the .NET Framework's implementation + // of Environment.ExpandEnvironmentVariables. + return name; } + + return Environment.ExpandEnvironmentVariables(name); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/IEnvironmentHelper.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/IEnvironmentHelper.cs index d1323158a7..06fab6ce26 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/IEnvironmentHelper.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/IEnvironmentHelper.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +/// +/// Abstraction for System.Environment for unit testing +/// +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IEnvironmentHelper { - /// - /// Abstraction for System.Environment for unit testing - /// - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IEnvironmentHelper - { - string? GetEnvironmentVariable(string name); + string? GetEnvironmentVariable(string name); - string ExpandEnvironmentVariables(string name); - } + string ExpandEnvironmentVariables(string name); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/TraceUtilities.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/TraceUtilities.cs index fda4f9edfb..117a7f1c52 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/TraceUtilities.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Utilities/TraceUtilities.cs @@ -5,142 +5,141 @@ // Let inspection tools detect that CPS uses a SourceSwitch for tracing messages. [assembly: Switch("RoslynProjectSystem", typeof(SourceSwitch))] -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +/// +/// This class contains methods that are useful for logging. +/// +internal static class TraceUtilities { + private const int CriticalTraceBufferSize = 32; + + /// + /// The CPS trace source. + /// + internal static readonly TraceSource Source = new("CPS"); + + /// + /// Buffer to preserve latest set of error messages to help diagnosing Watson bugs. + /// + private static readonly string[] s_criticalTraceBuffer = new string[CriticalTraceBufferSize]; + private static volatile int s_currentTraceIndex; + /// - /// This class contains methods that are useful for logging. + /// Gives the current Travel Level setting for the CPS tracing /// - internal static class TraceUtilities + internal static SourceLevels CurrentLevel { - private const int CriticalTraceBufferSize = 32; - - /// - /// The CPS trace source. - /// - internal static readonly TraceSource Source = new("CPS"); - - /// - /// Buffer to preserve latest set of error messages to help diagnosing Watson bugs. - /// - private static readonly string[] s_criticalTraceBuffer = new string[CriticalTraceBufferSize]; - private static volatile int s_currentTraceIndex; - - /// - /// Gives the current Travel Level setting for the CPS tracing - /// - internal static SourceLevels CurrentLevel - { - get { return Source.Switch.Level; } - } + get { return Source.Switch.Level; } + } - #region Tracing - Verbose + #region Tracing - Verbose - /// - /// Requests a verbose trace message to be written out to the listeners. - /// - /// The message to be traced. - internal static void TraceVerbose(string formattedMessage) - { - Source.TraceEvent(TraceEventType.Verbose, 0, formattedMessage); - } + /// + /// Requests a verbose trace message to be written out to the listeners. + /// + /// The message to be traced. + internal static void TraceVerbose(string formattedMessage) + { + Source.TraceEvent(TraceEventType.Verbose, 0, formattedMessage); + } - /// - /// Requests a verbose trace message to be written out to the listeners. - /// - /// The unformatted message to be traced. - /// The arguments to be formatted into the message - internal static void TraceVerbose(string unformattedMessage, params object[] args) - { - Source.TraceEvent(TraceEventType.Verbose, 0, unformattedMessage, args); - } + /// + /// Requests a verbose trace message to be written out to the listeners. + /// + /// The unformatted message to be traced. + /// The arguments to be formatted into the message + internal static void TraceVerbose(string unformattedMessage, params object[] args) + { + Source.TraceEvent(TraceEventType.Verbose, 0, unformattedMessage, args); + } - #endregion + #endregion - #region Tracing - Warning + #region Tracing - Warning - /// - /// Requests a warning trace message to be written out to the listeners. - /// - /// The message to be traced. - internal static void TraceWarning(string formattedMessage) - { - RecordCriticalMessage(formattedMessage); - Source.TraceEvent(TraceEventType.Warning, 0, formattedMessage); - } + /// + /// Requests a warning trace message to be written out to the listeners. + /// + /// The message to be traced. + internal static void TraceWarning(string formattedMessage) + { + RecordCriticalMessage(formattedMessage); + Source.TraceEvent(TraceEventType.Warning, 0, formattedMessage); + } - /// - /// Requests a warning trace message to be written out to the listeners. - /// - /// The unformatted message to be traced. - /// The arguments to be formatted into the message - internal static void TraceWarning(string unformattedMessage, params object[] args) - { - RecordCriticalMessage(unformattedMessage); - Source.TraceEvent(TraceEventType.Warning, 0, unformattedMessage, args); - } + /// + /// Requests a warning trace message to be written out to the listeners. + /// + /// The unformatted message to be traced. + /// The arguments to be formatted into the message + internal static void TraceWarning(string unformattedMessage, params object[] args) + { + RecordCriticalMessage(unformattedMessage); + Source.TraceEvent(TraceEventType.Warning, 0, unformattedMessage, args); + } - #endregion + #endregion - #region Tracing - Error + #region Tracing - Error - /// - /// Requests an error trace message to be written out to the listeners. - /// - /// The message to be traced. - internal static void TraceError(string formattedMessage) + /// + /// Requests an error trace message to be written out to the listeners. + /// + /// The message to be traced. + internal static void TraceError(string formattedMessage) + { + RecordCriticalMessage(formattedMessage); + Source.TraceEvent(TraceEventType.Error, 0, formattedMessage); + } + + /// + /// Requests an error trace message to be written out to the listeners. + /// + /// The unformatted message to be traced. + /// The arguments to be formatted into the message + internal static void TraceError(string unformattedMessage, params object[] args) + { + RecordCriticalMessage(unformattedMessage); + Source.TraceEvent(TraceEventType.Error, 0, unformattedMessage, args); + } + + /// + /// Requests an error trace message to be written out to the listeners + /// + internal static void TraceException(string formattedMessage, Exception e) + { + string message = e.ToString(); + + if (e is AggregateException aggregateException) { - RecordCriticalMessage(formattedMessage); - Source.TraceEvent(TraceEventType.Error, 0, formattedMessage); + message = aggregateException.Flatten().ToString(); } - /// - /// Requests an error trace message to be written out to the listeners. - /// - /// The unformatted message to be traced. - /// The arguments to be formatted into the message - internal static void TraceError(string unformattedMessage, params object[] args) + if (!string.IsNullOrEmpty(formattedMessage)) { - RecordCriticalMessage(unformattedMessage); - Source.TraceEvent(TraceEventType.Error, 0, unformattedMessage, args); + TraceError(formattedMessage + ":" + message); } - - /// - /// Requests an error trace message to be written out to the listeners - /// - internal static void TraceException(string formattedMessage, Exception e) + else { - string message = e.ToString(); - - if (e is AggregateException aggregateException) - { - message = aggregateException.Flatten().ToString(); - } - - if (!string.IsNullOrEmpty(formattedMessage)) - { - TraceError(formattedMessage + ":" + message); - } - else - { - TraceError("Traced Exception:" + message); - } + TraceError("Traced Exception:" + message); } - #endregion + } + #endregion - private static void RecordCriticalMessage(string message) + private static void RecordCriticalMessage(string message) + { + int currentValue; + + // Allocate the next index. We use CompareExchange here to prevent the race condition between two threads. + do { - int currentValue; - - // Allocate the next index. We use CompareExchange here to prevent the race condition between two threads. - do - { - currentValue = s_currentTraceIndex; - } - while (Interlocked.CompareExchange(ref s_currentTraceIndex, currentValue, (currentValue + 1) % CriticalTraceBufferSize) != currentValue); - - // possible to override, if the buffer is written heavily - // but this is just to help us to gather information, so performance is more important here. - s_criticalTraceBuffer[currentValue] = message; + currentValue = s_currentTraceIndex; } + while (Interlocked.CompareExchange(ref s_currentTraceIndex, currentValue, (currentValue + 1) % CriticalTraceBufferSize) != currentValue); + + // possible to override, if the buffer is written heavily + // but this is just to help us to gather information, so performance is more important here. + s_criticalTraceBuffer[currentValue] = message; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitContext.cs index 9094f7bbb1..9a8b012e02 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitContext.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Waiting +namespace Microsoft.VisualStudio.ProjectSystem.Waiting; + +internal interface IWaitContext : IDisposable { - internal interface IWaitContext : IDisposable - { - /// - /// Gets a cancellation token that represents the user's request for cancellation. - /// - /// - /// If the wait operation does not support cancellation, this will be . - /// - CancellationToken CancellationToken { get; } + /// + /// Gets a cancellation token that represents the user's request for cancellation. + /// + /// + /// If the wait operation does not support cancellation, this will be . + /// + CancellationToken CancellationToken { get; } - /// - /// Allows the operation being waited on to update the ongoing operation's status for the user. - /// - /// The message to display, or if no change is required. - /// The current step's (one-based) index, or if no change is required. - /// The total number of steps, or if no change is required. - /// A progress message display, or if no change is required. - void Update( - string? message = null, - int? currentStep = null, - int? totalSteps = null, - string? progressText = null); - } + /// + /// Allows the operation being waited on to update the ongoing operation's status for the user. + /// + /// The message to display, or if no change is required. + /// The current step's (one-based) index, or if no change is required. + /// The total number of steps, or if no change is required. + /// A progress message display, or if no change is required. + void Update( + string? message = null, + int? currentStep = null, + int? totalSteps = null, + string? progressText = null); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitIndicator.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitIndicator.cs index 0550e61bd8..e51dd12db0 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitIndicator.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/IWaitIndicator.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Waiting +namespace Microsoft.VisualStudio.ProjectSystem.Waiting; + +[ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] +internal interface IWaitIndicator { - [ProjectSystemContract(ProjectSystemContractScope.Global, ProjectSystemContractProvider.Private, Cardinality = ImportCardinality.ExactlyOne)] - internal interface IWaitIndicator - { - Task RunAsync(string title, string message, bool allowCancel, Func asyncMethod, int totalSteps = 0); + Task RunAsync(string title, string message, bool allowCancel, Func asyncMethod, int totalSteps = 0); - Task> RunAsync(string title, string message, bool allowCancel, Func> asyncMethod, int totalSteps = 0); - } + Task> RunAsync(string title, string message, bool allowCancel, Func> asyncMethod, int totalSteps = 0); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/WaitIndicatorResult.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/WaitIndicatorResult.cs index 641560c114..c1bbf87df5 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/WaitIndicatorResult.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Waiting/WaitIndicatorResult.cs @@ -1,67 +1,66 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Waiting -{ - /// - /// Represents the result of . - /// - internal readonly struct WaitIndicatorResult - { - public static readonly WaitIndicatorResult Cancelled = new(isCancelled: true); - public static readonly WaitIndicatorResult Completed = new(isCancelled: false); +namespace Microsoft.VisualStudio.ProjectSystem.Waiting; - private readonly bool _isCancelled; +/// +/// Represents the result of . +/// +internal readonly struct WaitIndicatorResult +{ + public static readonly WaitIndicatorResult Cancelled = new(isCancelled: true); + public static readonly WaitIndicatorResult Completed = new(isCancelled: false); - private WaitIndicatorResult(bool isCancelled) - { - _isCancelled = isCancelled; - } + private readonly bool _isCancelled; - /// - /// Gets a value indicating whether the operation was cancelled, either - /// by the user or the operation itself. - /// - public bool IsCancelled => _isCancelled; + private WaitIndicatorResult(bool isCancelled) + { + _isCancelled = isCancelled; } /// - /// Represents the result of . + /// Gets a value indicating whether the operation was cancelled, either + /// by the user or the operation itself. /// - internal readonly struct WaitIndicatorResult - { - public static readonly WaitIndicatorResult Cancelled = new(isCancelled: true, result: default!); + public bool IsCancelled => _isCancelled; +} - private readonly bool _isCancelled; - private readonly T _result; +/// +/// Represents the result of . +/// +internal readonly struct WaitIndicatorResult +{ + public static readonly WaitIndicatorResult Cancelled = new(isCancelled: true, result: default!); - private WaitIndicatorResult(bool isCancelled, T result) - { - _isCancelled = isCancelled; - _result = result; - } + private readonly bool _isCancelled; + private readonly T _result; + + private WaitIndicatorResult(bool isCancelled, T result) + { + _isCancelled = isCancelled; + _result = result; + } - /// - /// Gets a value indicating whether the operation was cancelled, either - /// by the user or the operation itself. - /// - public bool IsCancelled => _isCancelled; + /// + /// Gets a value indicating whether the operation was cancelled, either + /// by the user or the operation itself. + /// + public bool IsCancelled => _isCancelled; - /// - /// Gets the result of the operation. - /// - /// - /// is . - /// - public T Result + /// + /// Gets the result of the operation. + /// + /// + /// is . + /// + public T Result + { + get { - get - { - Verify.Operation(!_isCancelled, "Cannot get the result of a cancelled operation."); + Verify.Operation(!_isCancelled, "Cannot get the result of a cancelled operation."); - return _result; - } + return _result; } - - public static WaitIndicatorResult FromResult(T result) => new(isCancelled: false, result: result); } + + public static WaitIndicatorResult FromResult(T result) => new(isCancelled: false, result: result); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringComparers.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringComparers.cs index c5a089baaa..47cf86b829 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringComparers.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringComparers.cs @@ -1,76 +1,75 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +/// +/// Contains commonly used instances. +/// +/// +/// Mirrors values in . +/// +internal static class StringComparers { - /// - /// Contains commonly used instances. - /// - /// - /// Mirrors values in . - /// - internal static class StringComparers - { - public static StringComparer WorkspaceProjectContextIds => StringComparer.Ordinal; - public static StringComparer Paths => StringComparer.OrdinalIgnoreCase; - public static StringComparer PropertyNames => StringComparer.OrdinalIgnoreCase; - public static StringComparer PropertyLiteralValues => StringComparer.OrdinalIgnoreCase; - public static StringComparer PropertyValues => StringComparer.Ordinal; - public static StringComparer RuleNames => StringComparer.OrdinalIgnoreCase; - public static StringComparer CategoryNames => StringComparer.OrdinalIgnoreCase; - public static StringComparer ConfigurationDimensionNames => StringComparer.Ordinal; - public static StringComparer ConfigurationDimensionValues => StringComparer.Ordinal; - public static StringComparer DependencyIds => StringComparer.OrdinalIgnoreCase; - public static StringComparer ItemNames => StringComparer.OrdinalIgnoreCase; - public static StringComparer ItemTypes => StringComparer.OrdinalIgnoreCase; - public static StringComparer TargetNames => StringComparer.OrdinalIgnoreCase; - public static StringComparer FrameworkIdentifiers => StringComparer.OrdinalIgnoreCase; - public static StringComparer EnvironmentVariableNames => StringComparer.OrdinalIgnoreCase; - public static StringComparer TelemetryEventNames => StringComparer.Ordinal; - public static StringComparer NamedExports => StringComparer.OrdinalIgnoreCase; - public static StringComparer LaunchSettingsPropertyNames => StringComparer.Ordinal; - public static StringComparer LaunchProfileNames => StringComparer.Ordinal; - public static StringComparer LaunchProfileProperties => StringComparer.Ordinal; - public static StringComparer LaunchProfileCommandNames => StringComparer.Ordinal; - public static StringComparer UserEnteredSearchTermIgnoreCase => StringComparer.CurrentCultureIgnoreCase; - public static StringComparer ProjectTreeCaptionIgnoreCase => StringComparer.OrdinalIgnoreCase; - public static StringComparer LanguageIdentifiers => StringComparer.Ordinal; - public static StringComparer LanguageIdentifiersIgnoreCase => StringComparer.OrdinalIgnoreCase; - public static StringComparer VisualStudioSetupComponentIds => StringComparer.OrdinalIgnoreCase; - } + public static StringComparer WorkspaceProjectContextIds => StringComparer.Ordinal; + public static StringComparer Paths => StringComparer.OrdinalIgnoreCase; + public static StringComparer PropertyNames => StringComparer.OrdinalIgnoreCase; + public static StringComparer PropertyLiteralValues => StringComparer.OrdinalIgnoreCase; + public static StringComparer PropertyValues => StringComparer.Ordinal; + public static StringComparer RuleNames => StringComparer.OrdinalIgnoreCase; + public static StringComparer CategoryNames => StringComparer.OrdinalIgnoreCase; + public static StringComparer ConfigurationDimensionNames => StringComparer.Ordinal; + public static StringComparer ConfigurationDimensionValues => StringComparer.Ordinal; + public static StringComparer DependencyIds => StringComparer.OrdinalIgnoreCase; + public static StringComparer ItemNames => StringComparer.OrdinalIgnoreCase; + public static StringComparer ItemTypes => StringComparer.OrdinalIgnoreCase; + public static StringComparer TargetNames => StringComparer.OrdinalIgnoreCase; + public static StringComparer FrameworkIdentifiers => StringComparer.OrdinalIgnoreCase; + public static StringComparer EnvironmentVariableNames => StringComparer.OrdinalIgnoreCase; + public static StringComparer TelemetryEventNames => StringComparer.Ordinal; + public static StringComparer NamedExports => StringComparer.OrdinalIgnoreCase; + public static StringComparer LaunchSettingsPropertyNames => StringComparer.Ordinal; + public static StringComparer LaunchProfileNames => StringComparer.Ordinal; + public static StringComparer LaunchProfileProperties => StringComparer.Ordinal; + public static StringComparer LaunchProfileCommandNames => StringComparer.Ordinal; + public static StringComparer UserEnteredSearchTermIgnoreCase => StringComparer.CurrentCultureIgnoreCase; + public static StringComparer ProjectTreeCaptionIgnoreCase => StringComparer.OrdinalIgnoreCase; + public static StringComparer LanguageIdentifiers => StringComparer.Ordinal; + public static StringComparer LanguageIdentifiersIgnoreCase => StringComparer.OrdinalIgnoreCase; + public static StringComparer VisualStudioSetupComponentIds => StringComparer.OrdinalIgnoreCase; +} - /// - /// Contains commonly used instances. - /// - /// - /// Mirrors values in . - /// - internal static class StringComparisons - { - public static StringComparison WorkspaceProjectContextIds => StringComparison.Ordinal; - public static StringComparison Paths => StringComparison.OrdinalIgnoreCase; - public static StringComparison PropertyNames => StringComparison.OrdinalIgnoreCase; - public static StringComparison PropertyLiteralValues => StringComparison.OrdinalIgnoreCase; - public static StringComparison PropertyValues => StringComparison.Ordinal; - public static StringComparison RuleNames => StringComparison.OrdinalIgnoreCase; - public static StringComparison CategoryNames => StringComparison.OrdinalIgnoreCase; - public static StringComparison ConfigurationDimensionNames => StringComparison.Ordinal; - public static StringComparison ConfigurationDimensionValues => StringComparison.Ordinal; - public static StringComparison DependencyIds => StringComparison.OrdinalIgnoreCase; - public static StringComparison ItemNames => StringComparison.OrdinalIgnoreCase; - public static StringComparison ItemTypes => StringComparison.OrdinalIgnoreCase; - public static StringComparison TargetNames => StringComparison.OrdinalIgnoreCase; - public static StringComparison FrameworkIdentifiers => StringComparison.OrdinalIgnoreCase; - public static StringComparison EnvironmentVariableNames => StringComparison.OrdinalIgnoreCase; - public static StringComparison TelemetryEventNames => StringComparison.Ordinal; - public static StringComparison NamedExports => StringComparison.OrdinalIgnoreCase; - public static StringComparison LaunchSettingsPropertyNames => StringComparison.Ordinal; - public static StringComparison LaunchProfileNames => StringComparison.Ordinal; - public static StringComparison LaunchProfileProperties => StringComparison.Ordinal; - public static StringComparison LaunchProfileCommandNames => StringComparison.Ordinal; - public static StringComparison UserEnteredSearchTermIgnoreCase => StringComparison.CurrentCultureIgnoreCase; - public static StringComparison ProjectTreeCaptionIgnoreCase => StringComparison.OrdinalIgnoreCase; - public static StringComparison LanguageIdentifiers => StringComparison.Ordinal; - public static StringComparison LanguageIdentifiersIgnoreCase => StringComparison.OrdinalIgnoreCase; - public static StringComparison VisualStudioSetupComponentIds => StringComparison.OrdinalIgnoreCase; - } +/// +/// Contains commonly used instances. +/// +/// +/// Mirrors values in . +/// +internal static class StringComparisons +{ + public static StringComparison WorkspaceProjectContextIds => StringComparison.Ordinal; + public static StringComparison Paths => StringComparison.OrdinalIgnoreCase; + public static StringComparison PropertyNames => StringComparison.OrdinalIgnoreCase; + public static StringComparison PropertyLiteralValues => StringComparison.OrdinalIgnoreCase; + public static StringComparison PropertyValues => StringComparison.Ordinal; + public static StringComparison RuleNames => StringComparison.OrdinalIgnoreCase; + public static StringComparison CategoryNames => StringComparison.OrdinalIgnoreCase; + public static StringComparison ConfigurationDimensionNames => StringComparison.Ordinal; + public static StringComparison ConfigurationDimensionValues => StringComparison.Ordinal; + public static StringComparison DependencyIds => StringComparison.OrdinalIgnoreCase; + public static StringComparison ItemNames => StringComparison.OrdinalIgnoreCase; + public static StringComparison ItemTypes => StringComparison.OrdinalIgnoreCase; + public static StringComparison TargetNames => StringComparison.OrdinalIgnoreCase; + public static StringComparison FrameworkIdentifiers => StringComparison.OrdinalIgnoreCase; + public static StringComparison EnvironmentVariableNames => StringComparison.OrdinalIgnoreCase; + public static StringComparison TelemetryEventNames => StringComparison.Ordinal; + public static StringComparison NamedExports => StringComparison.OrdinalIgnoreCase; + public static StringComparison LaunchSettingsPropertyNames => StringComparison.Ordinal; + public static StringComparison LaunchProfileNames => StringComparison.Ordinal; + public static StringComparison LaunchProfileProperties => StringComparison.Ordinal; + public static StringComparison LaunchProfileCommandNames => StringComparison.Ordinal; + public static StringComparison UserEnteredSearchTermIgnoreCase => StringComparison.CurrentCultureIgnoreCase; + public static StringComparison ProjectTreeCaptionIgnoreCase => StringComparison.OrdinalIgnoreCase; + public static StringComparison LanguageIdentifiers => StringComparison.Ordinal; + public static StringComparison LanguageIdentifiersIgnoreCase => StringComparison.OrdinalIgnoreCase; + public static StringComparison VisualStudioSetupComponentIds => StringComparison.OrdinalIgnoreCase; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringExtensions.cs index 4ea25be81f..ced50c2006 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/StringExtensions.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class StringExtensions { - internal static class StringExtensions + public static string[] SplitReturningEmptyIfEmpty(this string value, params char[] separator) { - public static string[] SplitReturningEmptyIfEmpty(this string value, params char[] separator) - { - string[] values = value.Split(separator); + string[] values = value.Split(separator); - if (values.Length == 1 && string.IsNullOrEmpty(values[0])) - return Array.Empty(); + if (values.Length == 1 && string.IsNullOrEmpty(values[0])) + return Array.Empty(); - return values; - } + return values; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/ComplexPropertyValue.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/ComplexPropertyValue.cs index b732b962e9..be25b06888 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/ComplexPropertyValue.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/ComplexPropertyValue.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Telemetry +namespace Microsoft.VisualStudio.Telemetry; + +/// +/// Wrapper for complex (non-scalar) property values being reported +/// via . +/// +internal readonly struct ComplexPropertyValue { - /// - /// Wrapper for complex (non-scalar) property values being reported - /// via . - /// - internal readonly struct ComplexPropertyValue - { - public object Data { get; } + public object Data { get; } - public ComplexPropertyValue(object data) - { - Data = data; - } + public ComplexPropertyValue(object data) + { + Data = data; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryEventName.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryEventName.cs index 26b3d415e6..11a8da85ed 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryEventName.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryEventName.cs @@ -1,80 +1,79 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Telemetry +namespace Microsoft.VisualStudio.Telemetry; + +/// +/// Provides telemetry event names used throughout this project. +/// +internal static class TelemetryEventName { + // NOTE we don't extract the prefix (vs/projectsystem/managed) into a variable here, to make + // it easier to search for the full event name across repositories and find this code as + // a match. + /// - /// Provides telemetry event names used throughout this project. + /// Indicates the a fault in the managed project system. /// - internal static class TelemetryEventName - { - // NOTE we don't extract the prefix (vs/projectsystem/managed) into a variable here, to make - // it easier to search for the full event name across repositories and find this code as - // a match. + public const string Fault = "vs/projectsystem/managed/fault"; - /// - /// Indicates the a fault in the managed project system. - /// - public const string Fault = "vs/projectsystem/managed/fault"; - - /// - /// Indicates that a project's last build is considered up-to-date. - /// - public const string UpToDateCheckSuccess = "vs/projectsystem/managed/uptodatecheck/success"; + /// + /// Indicates that a project's last build is considered up-to-date. + /// + public const string UpToDateCheckSuccess = "vs/projectsystem/managed/uptodatecheck/success"; - /// - /// Indicates that a project's last build is considered out-of-date. - /// - public const string UpToDateCheckFail = "vs/projectsystem/managed/uptodatecheck/fail"; + /// + /// Indicates that a project's last build is considered out-of-date. + /// + public const string UpToDateCheckFail = "vs/projectsystem/managed/uptodatecheck/fail"; - /// - /// Indicates that a design-time build has completed. - /// - public const string DesignTimeBuildComplete = "vs/projectsystem/managed/designtimebuildcomplete"; + /// + /// Indicates that a design-time build has completed. + /// + public const string DesignTimeBuildComplete = "vs/projectsystem/managed/designtimebuildcomplete"; - /// - /// Indicates the .NET Core SDK version. - /// - public const string SDKVersion = "vs/projectsystem/managed/sdkversion"; + /// + /// Indicates the .NET Core SDK version. + /// + public const string SDKVersion = "vs/projectsystem/managed/sdkversion"; - /// - /// Indicates that the TempPE compilation queue has been processed. - /// - public const string TempPEProcessQueue = "vs/projectsystem/managed/temppe/processcompilequeue"; + /// + /// Indicates that the TempPE compilation queue has been processed. + /// + public const string TempPEProcessQueue = "vs/projectsystem/managed/temppe/processcompilequeue"; - /// - /// Indicates that the TempPE compilation has occurred on demand from a designer. - /// - public const string TempPECompileOnDemand = "vs/projectsystem/managed/temppe/compileondemand"; + /// + /// Indicates that the TempPE compilation has occurred on demand from a designer. + /// + public const string TempPECompileOnDemand = "vs/projectsystem/managed/temppe/compileondemand"; - /// - /// Indicates that project was not up-to-date after build, meaning that incremental build is not - /// working correctly for the project. - /// - /// - /// In some cases, we run the up-to-date check after a build completes, to determine whether - /// the project's incremental build is working correctly. When a build completes, it should be up-to-date. - /// - public const string IncrementalBuildValidationFailure = "vs/projectsystem/managed/incrementalbuild/validationfailure"; + /// + /// Indicates that project was not up-to-date after build, meaning that incremental build is not + /// working correctly for the project. + /// + /// + /// In some cases, we run the up-to-date check after a build completes, to determine whether + /// the project's incremental build is working correctly. When a build completes, it should be up-to-date. + /// + public const string IncrementalBuildValidationFailure = "vs/projectsystem/managed/incrementalbuild/validationfailure"; - /// - /// Indicates that the user was notified of the suspected incremental build failure. - /// - public const string IncrementalBuildValidationFailureDisplayed = "vs/projectsystem/managed/incrementalbuild/validationfailure/displayed"; + /// + /// Indicates that the user was notified of the suspected incremental build failure. + /// + public const string IncrementalBuildValidationFailureDisplayed = "vs/projectsystem/managed/incrementalbuild/validationfailure/displayed"; - /// - /// Contains a summary of the solution build, from the perspective of the .NET Project System. - /// - public const string SolutionBuildSummary = "vs/projectsystem/managed/incrementalbuild/solutionbuildsummary"; + /// + /// Contains a summary of the solution build, from the perspective of the .NET Project System. + /// + public const string SolutionBuildSummary = "vs/projectsystem/managed/incrementalbuild/solutionbuildsummary"; - /// - /// Indicates that the NuGet restore detected a cycle. - /// - public const string NuGetRestoreCycleDetected = "vs/projectsystem/managed/nugetrestore/cycledetected"; + /// + /// Indicates that the NuGet restore detected a cycle. + /// + public const string NuGetRestoreCycleDetected = "vs/projectsystem/managed/nugetrestore/cycledetected"; - /// - /// Notifies that the legacy CodeModel was requested for a given project. - /// Only fires once per unconfigured project. - /// - public const string CodeModelRequested = "vs/projectsystem/managed/codemodel/requested"; - } + /// + /// Notifies that the legacy CodeModel was requested for a given project. + /// Only fires once per unconfigured project. + /// + public const string CodeModelRequested = "vs/projectsystem/managed/codemodel/requested"; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryPropertyName.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryPropertyName.cs index 5ae6d62343..7cba78dc52 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryPropertyName.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Telemetry/TelemetryPropertyName.cs @@ -1,274 +1,273 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Telemetry +namespace Microsoft.VisualStudio.Telemetry; + +/// +/// Provides telemetry property names used throughout this project. +/// +internal static class TelemetryPropertyName { - /// - /// Provides telemetry property names used throughout this project. - /// - internal static class TelemetryPropertyName + // NOTE we don't extract the prefix (vs.projectsystem.managed) into a variable here, to make + // it easier to search for the full property name across repositories and find this code as + // a match. + + public static class UpToDateCheck + { + /// + /// Indicates the reason that a project's last build is considered out-of-date. + /// + public const string FailReason = "vs.projectsystem.managed.uptodatecheck.fail.reason2"; + + /// + /// Indicates the duration of the up-to-date check, in milliseconds. Includes wait time and execution time. + /// + public const string DurationMillis = "vs.projectsystem.managed.uptodatecheck.durationmillis"; + + /// + /// Indicates the duration of time between when the check was requested, and when we actually + /// start execution. + /// + /// + /// During this time we await the latest project data, which can take quite some time. + /// We also acquire a lock, and query the host for the status of the up-to-date check. + /// We report this wait time separately via telemetry in order to properly attribute the source + /// of delays in the up-to-date check. This time is also included in + /// for historical reasons. Ideally they would not overlap, but changing that now would + /// make analysis of historical data difficult. + /// + public const string WaitDurationMillis = "vs.projectsystem.managed.uptodatecheck.waitdurationmillis"; + + /// + /// Indicates the number of file system timestamps that were queried during the up-to-date check. + /// + public const string FileCount = "vs.projectsystem.managed.uptodatecheck.filecount"; + + /// + /// Indicates the number of (implicitly active) configurations that were included in the the up-to-date check. + /// + /// + /// The up-to-date check runs for the active configuration only, but will consider state from all + /// implicitly active configurations. Generally, for a single targeting project this will equal one, + /// and for multi-targeting projects this will equal the number of target frameworks being targeted. + /// + public const string ConfigurationCount = "vs.projectsystem.managed.uptodatecheck.configurationcount"; + + /// + /// Indicates the user's chosen logging level. Values from the enum. + /// + public const string LogLevel = "vs.projectsystem.managed.uptodatecheck.loglevel"; + + /// + /// Indicates any ignore kinds provided to the fast up-to-date check. + /// Used to skip analyzers during indirect builds (for debug or unit tests). + /// + public const string IgnoreKinds = "vs.projectsystem.managed.uptodatecheck.ignorekinds"; + + /// + /// Identifies the project to which data in the telemetry event applies. + /// + public const string Project = "vs.projectsystem.managed.uptodatecheck.projectid"; + + /// + /// Indicates the number of checks performed for this project so far in the current session, starting at one. + /// This number resets when the project is reloaded. + /// + public const string CheckNumber = "vs.projectsystem.managed.uptodatecheck.checknumber"; + + /// + /// The outcome of the FUTDC's Build Acceleration evaluation. + /// + public const string AccelerationResult = "vs.projectsystem.managed.uptodatecheck.accelerationresult"; + + /// + /// The number of files copied as part of Build Acceleration. Zero if disabled or no files were copied. + /// See to understand why the value may be zero. + /// + public const string AcceleratedCopyCount = "vs.projectsystem.managed.uptodatecheck.acceleratedcopycount"; + } + + public static class SolutionBuildSummary + { + /// + /// Indicates the duration of the solution build, in milliseconds. + /// + public const string DurationMillis = "vs.projectsystem.managed.solutionbuildsummary.durationmillis"; + + /// + /// Indicates the total number of projects that participated in the solution build. + /// + public const string ProjectCount = "vs.projectsystem.managed.solutionbuildsummary.projectcount"; + + /// + /// Indicates the total number of configured projects that participated in the solution build. + /// This can help understand the impact of multi-targeting projects. + /// + public const string ConfiguredProjectCount = "vs.projectsystem.managed.solutionbuildsummary.configuredprojectcount"; + + /// + /// Indicates the number of projects backed by the .NET Project System that were considered up-to-date and therefore skipped. + /// Excludes accelerated projects and those found out-of-date. + /// + public const string UpToDateProjectCount = "vs.projectsystem.managed.solutionbuildsummary.uptodateprojectcount"; + + /// + /// Indicates the number of projects backed by the .NET Project System that were considered out-to-date and therefore built. + /// Excludes accelerated projects and those found up-to-date. + /// + public const string OutOfDateProjectCount = "vs.projectsystem.managed.solutionbuildsummary.outofdateprojectcount"; + + /// + /// Indicates the number of projects backed by the .NET Project System that were accelerated rather than built, + /// meaning that VS performed the build operation rather than MSBuild. + /// + public const string AcceleratedProjectCount = "vs.projectsystem.managed.solutionbuildsummary.acceleratedprojectcount"; + + /// + /// Indicates the number of projects in the solution build that were identified as candidates for acceleration. + /// + public const string AccelerationCandidateProjectCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationcandidateprojectcount"; + + /// + /// Indicates the number of files that were copied as part of Build Acceleration. + /// If no projects were accelerated, this value will be zero. + /// + public const string AccelerationFileCopyCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationfilecopycount"; + + /// + /// Indicates the number of project for which build acceleration was explicitly enabled. + /// + public const string AccelerationEnabledCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationenabledcount"; + + /// + /// Indicates the number of project for which build acceleration was explicitly disabled. + /// + public const string AccelerationDisabledCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationdisabledcount"; + + /// + /// Indicates the number of project for which build acceleration was neither explicitly enabled or disabled. + /// + public const string AccelerationUnspecifiedCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationunspecifiedcount"; + + /// + /// Indicates the number of file timestamps inspected by the fast up-to-date checks, across all projects in the solution build. + /// + public const string FilesCheckedCount = "vs.projectsystem.managed.solutionbuildsummary.filescheckedcount"; + + /// + /// Indicates the amount of time the fast up-to-date check spent waiting for project data to be available via asynchronous dataflow. + /// + public const string WaitTimeMillis = "vs.projectsystem.managed.solutionbuildsummary.waittimemillis"; + + /// + /// Indicates the total amount of time spent within the fast up-to-date check across all projects in the solution build. + /// Note that this is the sum of the time spent for each project, however those times may have overlapped during the + /// solution build, and have executed concurrently. + /// + public const string CheckTimeMillis = "vs.projectsystem.managed.solutionbuildsummary.checktimemillis"; + + /// + /// Indicates the fast up-to-date check log level selected for the solution build. + /// + public const string LogLevel = "vs.projectsystem.managed.solutionbuildsummary.loglevel"; + } + + public static class DesignTimeBuildComplete + { + /// + /// Indicates whether a design-time build has completed without errors. + /// + public const string Succeeded = "vs.projectsystem.managed.designtimebuildcomplete.succeeded"; + + /// + /// A complex value, with a map from target name to duration (in milliseconds). + /// + /// + /// + /// If the task is not known to be shipped by Microsoft (i.e. in the VS or .NET SDK folders), + /// the target name is hashed as potential PII. + /// + /// + /// This is version 2 of the property. Version 1 was a single string, was always hashed, + /// and used a longer form of hash value. + /// + /// + public const string Targets = "vs.projectsystem.managed.designtimebuildcomplete.targets2"; + + /// + /// The number of errors reported during the design-time build. + /// + public const string ErrorCount = "vs.projectsystem.managed.designtimebuildcomplete.errorcount"; + + /// + /// The names of targets that failed during the design-time build. + /// + public const string ErrorTargets = "vs.projectsystem.managed.designtimebuildcomplete.errortargets"; + } + + public static class SDKVersion + { + /// + /// Indicates the project that contains the SDK version. + /// + public const string Project = "vs.projectsystem.managed.sdkversion.project"; + + /// + /// Indicates the actual underlying version of .NET Core SDK. + /// + public const string NETCoreSDKVersion = "vs.projectsystem.managed.sdkversion.netcoresdkversion"; + } + + public static class TempPE + { + /// + /// Indicates the number of TempPE DLLs compiled + /// + public const string CompileCount = "vs.projectsystem.managed.temppe.processcompilequeue.compilecount"; + + /// + /// Indicates the starting length of the TempPE compilation queue + /// + public const string InitialQueueLength = "vs.projectsystem.managed.temppe.processcompilequeue.queuelength"; + + /// + /// Indicates whether the TempPE compilation was cancelled + /// + public const string CompileWasCancelled = "vs.projectsystem.managed.temppe.processcompilequeue.cancelled"; + + /// + /// Indicates the duration of the TempPE compilation + /// + public const string CompileDuration = "vs.projectsystem.managed.temppe.processcompilequeue.duration"; + } + + public static class IncrementalBuildValidation + { + /// + /// Indicates the reason the project was not up-to-date immediately after build. + /// + public const string FailureReason = "vs.projectsystem.managed.incrementalbuild.validationfailure.reason"; + + /// + /// Indicates the duration of the up-to-date check performed immediately after build to find incremental build breaks. + /// + public const string DurationMillis = "vs.projectsystem.managed.incrementalbuild.validationfailure.durationmillis"; + } + + public static class NuGetRestoreCycleDetected { - // NOTE we don't extract the prefix (vs.projectsystem.managed) into a variable here, to make - // it easier to search for the full property name across repositories and find this code as - // a match. - - public static class UpToDateCheck - { - /// - /// Indicates the reason that a project's last build is considered out-of-date. - /// - public const string FailReason = "vs.projectsystem.managed.uptodatecheck.fail.reason2"; - - /// - /// Indicates the duration of the up-to-date check, in milliseconds. Includes wait time and execution time. - /// - public const string DurationMillis = "vs.projectsystem.managed.uptodatecheck.durationmillis"; - - /// - /// Indicates the duration of time between when the check was requested, and when we actually - /// start execution. - /// - /// - /// During this time we await the latest project data, which can take quite some time. - /// We also acquire a lock, and query the host for the status of the up-to-date check. - /// We report this wait time separately via telemetry in order to properly attribute the source - /// of delays in the up-to-date check. This time is also included in - /// for historical reasons. Ideally they would not overlap, but changing that now would - /// make analysis of historical data difficult. - /// - public const string WaitDurationMillis = "vs.projectsystem.managed.uptodatecheck.waitdurationmillis"; - - /// - /// Indicates the number of file system timestamps that were queried during the up-to-date check. - /// - public const string FileCount = "vs.projectsystem.managed.uptodatecheck.filecount"; - - /// - /// Indicates the number of (implicitly active) configurations that were included in the the up-to-date check. - /// - /// - /// The up-to-date check runs for the active configuration only, but will consider state from all - /// implicitly active configurations. Generally, for a single targeting project this will equal one, - /// and for multi-targeting projects this will equal the number of target frameworks being targeted. - /// - public const string ConfigurationCount = "vs.projectsystem.managed.uptodatecheck.configurationcount"; - - /// - /// Indicates the user's chosen logging level. Values from the enum. - /// - public const string LogLevel = "vs.projectsystem.managed.uptodatecheck.loglevel"; - - /// - /// Indicates any ignore kinds provided to the fast up-to-date check. - /// Used to skip analyzers during indirect builds (for debug or unit tests). - /// - public const string IgnoreKinds = "vs.projectsystem.managed.uptodatecheck.ignorekinds"; - - /// - /// Identifies the project to which data in the telemetry event applies. - /// - public const string Project = "vs.projectsystem.managed.uptodatecheck.projectid"; - - /// - /// Indicates the number of checks performed for this project so far in the current session, starting at one. - /// This number resets when the project is reloaded. - /// - public const string CheckNumber = "vs.projectsystem.managed.uptodatecheck.checknumber"; - - /// - /// The outcome of the FUTDC's Build Acceleration evaluation. - /// - public const string AccelerationResult = "vs.projectsystem.managed.uptodatecheck.accelerationresult"; - - /// - /// The number of files copied as part of Build Acceleration. Zero if disabled or no files were copied. - /// See to understand why the value may be zero. - /// - public const string AcceleratedCopyCount = "vs.projectsystem.managed.uptodatecheck.acceleratedcopycount"; - } - - public static class SolutionBuildSummary - { - /// - /// Indicates the duration of the solution build, in milliseconds. - /// - public const string DurationMillis = "vs.projectsystem.managed.solutionbuildsummary.durationmillis"; - - /// - /// Indicates the total number of projects that participated in the solution build. - /// - public const string ProjectCount = "vs.projectsystem.managed.solutionbuildsummary.projectcount"; - - /// - /// Indicates the total number of configured projects that participated in the solution build. - /// This can help understand the impact of multi-targeting projects. - /// - public const string ConfiguredProjectCount = "vs.projectsystem.managed.solutionbuildsummary.configuredprojectcount"; - - /// - /// Indicates the number of projects backed by the .NET Project System that were considered up-to-date and therefore skipped. - /// Excludes accelerated projects and those found out-of-date. - /// - public const string UpToDateProjectCount = "vs.projectsystem.managed.solutionbuildsummary.uptodateprojectcount"; - - /// - /// Indicates the number of projects backed by the .NET Project System that were considered out-to-date and therefore built. - /// Excludes accelerated projects and those found up-to-date. - /// - public const string OutOfDateProjectCount = "vs.projectsystem.managed.solutionbuildsummary.outofdateprojectcount"; - - /// - /// Indicates the number of projects backed by the .NET Project System that were accelerated rather than built, - /// meaning that VS performed the build operation rather than MSBuild. - /// - public const string AcceleratedProjectCount = "vs.projectsystem.managed.solutionbuildsummary.acceleratedprojectcount"; - - /// - /// Indicates the number of projects in the solution build that were identified as candidates for acceleration. - /// - public const string AccelerationCandidateProjectCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationcandidateprojectcount"; - - /// - /// Indicates the number of files that were copied as part of Build Acceleration. - /// If no projects were accelerated, this value will be zero. - /// - public const string AccelerationFileCopyCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationfilecopycount"; - - /// - /// Indicates the number of project for which build acceleration was explicitly enabled. - /// - public const string AccelerationEnabledCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationenabledcount"; - - /// - /// Indicates the number of project for which build acceleration was explicitly disabled. - /// - public const string AccelerationDisabledCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationdisabledcount"; - - /// - /// Indicates the number of project for which build acceleration was neither explicitly enabled or disabled. - /// - public const string AccelerationUnspecifiedCount = "vs.projectsystem.managed.solutionbuildsummary.accelerationunspecifiedcount"; - - /// - /// Indicates the number of file timestamps inspected by the fast up-to-date checks, across all projects in the solution build. - /// - public const string FilesCheckedCount = "vs.projectsystem.managed.solutionbuildsummary.filescheckedcount"; - - /// - /// Indicates the amount of time the fast up-to-date check spent waiting for project data to be available via asynchronous dataflow. - /// - public const string WaitTimeMillis = "vs.projectsystem.managed.solutionbuildsummary.waittimemillis"; - - /// - /// Indicates the total amount of time spent within the fast up-to-date check across all projects in the solution build. - /// Note that this is the sum of the time spent for each project, however those times may have overlapped during the - /// solution build, and have executed concurrently. - /// - public const string CheckTimeMillis = "vs.projectsystem.managed.solutionbuildsummary.checktimemillis"; - - /// - /// Indicates the fast up-to-date check log level selected for the solution build. - /// - public const string LogLevel = "vs.projectsystem.managed.solutionbuildsummary.loglevel"; - } - - public static class DesignTimeBuildComplete - { - /// - /// Indicates whether a design-time build has completed without errors. - /// - public const string Succeeded = "vs.projectsystem.managed.designtimebuildcomplete.succeeded"; - - /// - /// A complex value, with a map from target name to duration (in milliseconds). - /// - /// - /// - /// If the task is not known to be shipped by Microsoft (i.e. in the VS or .NET SDK folders), - /// the target name is hashed as potential PII. - /// - /// - /// This is version 2 of the property. Version 1 was a single string, was always hashed, - /// and used a longer form of hash value. - /// - /// - public const string Targets = "vs.projectsystem.managed.designtimebuildcomplete.targets2"; - - /// - /// The number of errors reported during the design-time build. - /// - public const string ErrorCount = "vs.projectsystem.managed.designtimebuildcomplete.errorcount"; - - /// - /// The names of targets that failed during the design-time build. - /// - public const string ErrorTargets = "vs.projectsystem.managed.designtimebuildcomplete.errortargets"; - } - - public static class SDKVersion - { - /// - /// Indicates the project that contains the SDK version. - /// - public const string Project = "vs.projectsystem.managed.sdkversion.project"; - - /// - /// Indicates the actual underlying version of .NET Core SDK. - /// - public const string NETCoreSDKVersion = "vs.projectsystem.managed.sdkversion.netcoresdkversion"; - } - - public static class TempPE - { - /// - /// Indicates the number of TempPE DLLs compiled - /// - public const string CompileCount = "vs.projectsystem.managed.temppe.processcompilequeue.compilecount"; - - /// - /// Indicates the starting length of the TempPE compilation queue - /// - public const string InitialQueueLength = "vs.projectsystem.managed.temppe.processcompilequeue.queuelength"; - - /// - /// Indicates whether the TempPE compilation was cancelled - /// - public const string CompileWasCancelled = "vs.projectsystem.managed.temppe.processcompilequeue.cancelled"; - - /// - /// Indicates the duration of the TempPE compilation - /// - public const string CompileDuration = "vs.projectsystem.managed.temppe.processcompilequeue.duration"; - } - - public static class IncrementalBuildValidation - { - /// - /// Indicates the reason the project was not up-to-date immediately after build. - /// - public const string FailureReason = "vs.projectsystem.managed.incrementalbuild.validationfailure.reason"; - - /// - /// Indicates the duration of the up-to-date check performed immediately after build to find incremental build breaks. - /// - public const string DurationMillis = "vs.projectsystem.managed.incrementalbuild.validationfailure.durationmillis"; - } - - public static class NuGetRestoreCycleDetected - { - /// - /// Indicates the duration of the NuGet restore to detect a cycle - /// - public const string RestoreDurationMillis = "vs.projectsystem.managed.nugetrestore.cycledetected.durationmillis"; - - /// - /// Indicates the number of time NuGet restore have succeeded until now. - /// - public const string RestoreSuccesses = "vs.projectsystem.managed.nugetrestore.cycledetected.restoresuccesses"; - - /// - /// Indicates the number of times NuGet restore have detected cycles until now. - /// - public const string RestoreCyclesDetected = "vs.projectsystem.managed.nugetrestore.cycledetected.cyclesdetected"; - } + /// + /// Indicates the duration of the NuGet restore to detect a cycle + /// + public const string RestoreDurationMillis = "vs.projectsystem.managed.nugetrestore.cycledetected.durationmillis"; + + /// + /// Indicates the number of time NuGet restore have succeeded until now. + /// + public const string RestoreSuccesses = "vs.projectsystem.managed.nugetrestore.cycledetected.restoresuccesses"; + + /// + /// Indicates the number of times NuGet restore have detected cycles until now. + /// + public const string RestoreCyclesDetected = "vs.projectsystem.managed.nugetrestore.cycledetected.cyclesdetected"; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/IncrementalHasher.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/IncrementalHasher.cs index 21b09476d6..9d562c977e 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/IncrementalHasher.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/IncrementalHasher.cs @@ -4,92 +4,91 @@ using System.Security.Cryptography; using System.Text; -namespace Microsoft.VisualStudio.Text +namespace Microsoft.VisualStudio.Text; + +/// +/// Provides support for computing a hash for instances +/// incrementally across several segments. +/// +internal class IncrementalHasher : IDisposable { - /// - /// Provides support for computing a hash for instances - /// incrementally across several segments. - /// - internal class IncrementalHasher : IDisposable + private const int BufferCharacterSize = 631; // Largest amount of UTF-8 characters that can roughly fit in 2048 bytes + private static readonly int s_bufferByteSize = Encoding.UTF8.GetMaxByteCount(BufferCharacterSize); + private readonly IncrementalHash _hasher; + private readonly byte[] _buffer; + + public IncrementalHasher() + { + _hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); + _buffer = ArrayPool.Shared.Rent(s_bufferByteSize); + } + + public void Append(string value) { - private const int BufferCharacterSize = 631; // Largest amount of UTF-8 characters that can roughly fit in 2048 bytes - private static readonly int s_bufferByteSize = Encoding.UTF8.GetMaxByteCount(BufferCharacterSize); - private readonly IncrementalHash _hasher; - private readonly byte[] _buffer; + Requires.NotNull(value); - public IncrementalHasher() + int charIndex = 0; + while (charIndex < value.Length) { - _hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); - _buffer = ArrayPool.Shared.Rent(s_bufferByteSize); + int charCount = Math.Min(BufferCharacterSize, value.Length - charIndex); + + int bytesCount = Encoding.UTF8.GetBytes(value, charIndex, charCount, _buffer, 0); + charIndex += charCount; + + _hasher.AppendData(_buffer, 0, bytesCount); } + } - public void Append(string value) - { - Requires.NotNull(value); + public Hash GetHashAndReset() + { + return new(_hasher.GetHashAndReset()); + } - int charIndex = 0; - while (charIndex < value.Length) - { - int charCount = Math.Min(BufferCharacterSize, value.Length - charIndex); + public void Dispose() + { + _hasher.Dispose(); + ArrayPool.Shared.Return(_buffer); + } +} - int bytesCount = Encoding.UTF8.GetBytes(value, charIndex, charCount, _buffer, 0); - charIndex += charCount; +internal readonly struct Hash(byte[] bytes) : IEquatable +{ + private readonly byte[] _bytes = bytes; - _hasher.AppendData(_buffer, 0, bytesCount); - } - } + public override bool Equals(object obj) => obj is Hash hash && Equals(hash); + + public bool Equals(Hash other) + { + byte[] thisBytes = _bytes; + byte[] thatBytes = other._bytes; - public Hash GetHashAndReset() + if (ReferenceEquals(thisBytes, thatBytes)) { - return new(_hasher.GetHashAndReset()); + return true; } - public void Dispose() + if (thisBytes is null || thatBytes is null) { - _hasher.Dispose(); - ArrayPool.Shared.Return(_buffer); + return false; } + + return thisBytes.AsSpan().SequenceEqual(thatBytes.AsSpan()); } - internal readonly struct Hash(byte[] bytes) : IEquatable + public override int GetHashCode() { - private readonly byte[] _bytes = bytes; + const int prime = 0x1000193; - public override bool Equals(object obj) => obj is Hash hash && Equals(hash); - - public bool Equals(Hash other) + unchecked { - byte[] thisBytes = _bytes; - byte[] thatBytes = other._bytes; + int hash = (int)0x811C9DC5; - if (ReferenceEquals(thisBytes, thatBytes)) + for (int i = 0; i < _bytes.Length; i++) { - return true; + hash = (hash ^ _bytes[i]) * prime; } - if (thisBytes is null || thatBytes is null) - { - return false; - } - - return thisBytes.AsSpan().SequenceEqual(thatBytes.AsSpan()); - } - - public override int GetHashCode() - { - const int prime = 0x1000193; - - unchecked - { - int hash = (int)0x811C9DC5; - - for (int i = 0; i < _bytes.Length; i++) - { - hash = (hash ^ _bytes[i]) * prime; - } - - return hash; - } + return hash; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/LazyStringSplit.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/LazyStringSplit.cs index d89ee54495..ca0a8baa73 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/LazyStringSplit.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/LazyStringSplit.cs @@ -2,129 +2,128 @@ using System.Collections; -namespace Microsoft.VisualStudio.Text +namespace Microsoft.VisualStudio.Text; + +/// +/// Splits a string by a delimiter, producing substrings lazily during enumeration. +/// Skips empty items, behaving equivalently to with +/// . +/// +/// +/// Unlike and overloads, +/// does not allocate an array for the return, and allocates strings on demand during +/// enumeration. A custom enumerator type is used so that the only allocations made are +/// the substrings themselves. We also avoid the large internal arrays assigned by the +/// methods on . +/// +internal readonly struct LazyStringSplit : IEnumerable { - /// - /// Splits a string by a delimiter, producing substrings lazily during enumeration. - /// Skips empty items, behaving equivalently to with - /// . - /// - /// - /// Unlike and overloads, - /// does not allocate an array for the return, and allocates strings on demand during - /// enumeration. A custom enumerator type is used so that the only allocations made are - /// the substrings themselves. We also avoid the large internal arrays assigned by the - /// methods on . - /// - internal readonly struct LazyStringSplit : IEnumerable - { - private readonly string _input; - private readonly char _delimiter; + private readonly string _input; + private readonly char _delimiter; - public LazyStringSplit(string input, char delimiter) - { - Requires.NotNull(input); + public LazyStringSplit(string input, char delimiter) + { + Requires.NotNull(input); - _input = input; - _delimiter = delimiter; - } + _input = input; + _delimiter = delimiter; + } - public Enumerator GetEnumerator() => new(this); + public Enumerator GetEnumerator() => new(this); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerable Select(Func func) + public IEnumerable Select(Func func) + { + foreach (string value in this) { - foreach (string value in this) - { - yield return func(value); - } + yield return func(value); } + } - public string First() - { - return FirstOrDefault() ?? throw new InvalidOperationException("Sequence is empty."); - } + public string First() + { + return FirstOrDefault() ?? throw new InvalidOperationException("Sequence is empty."); + } + + public string? FirstOrDefault() + { + var enumerator = new Enumerator(this); + return enumerator.MoveNext() ? enumerator.Current : null; + } + + public struct Enumerator : IEnumerator + { + private readonly string _input; + private readonly char _delimiter; + private int _index; - public string? FirstOrDefault() + internal Enumerator(in LazyStringSplit split) { - var enumerator = new Enumerator(this); - return enumerator.MoveNext() ? enumerator.Current : null; + _index = 0; + _input = split._input; + _delimiter = split._delimiter; + Current = null!; } - public struct Enumerator : IEnumerator - { - private readonly string _input; - private readonly char _delimiter; - private int _index; + public string Current { get; private set; } - internal Enumerator(in LazyStringSplit split) + public bool MoveNext() + { + while (_index != _input.Length) { - _index = 0; - _input = split._input; - _delimiter = split._delimiter; - Current = null!; - } - - public string Current { get; private set; } + int delimiterIndex = _input.IndexOf(_delimiter, _index); - public bool MoveNext() - { - while (_index != _input.Length) + if (delimiterIndex == -1) { - int delimiterIndex = _input.IndexOf(_delimiter, _index); - - if (delimiterIndex == -1) - { - Current = _input.Substring(_index); - _index = _input.Length; - return true; - } - - int length = delimiterIndex - _index; + Current = _input.Substring(_index); + _index = _input.Length; + return true; + } - if (length == 0) - { - _index++; - continue; - } + int length = delimiterIndex - _index; - Current = _input.Substring(_index, length); - _index = delimiterIndex + 1; - return true; + if (length == 0) + { + _index++; + continue; } - return false; + Current = _input.Substring(_index, length); + _index = delimiterIndex + 1; + return true; } - object IEnumerator.Current => Current; + return false; + } - void IEnumerator.Reset() - { - _index = 0; - Current = null!; - } + object IEnumerator.Current => Current; - void IDisposable.Dispose() { } + void IEnumerator.Reset() + { + _index = 0; + Current = null!; } + + void IDisposable.Dispose() { } } +} - internal static class LazyStringSplitExtensions +internal static class LazyStringSplitExtensions +{ + /// + /// This extension method has special knowledge of the type and + /// can compute its result without allocation. + /// + /// + public static string? FirstOrDefault(this LazyStringSplit lazyStringSplit) { - /// - /// This extension method has special knowledge of the type and - /// can compute its result without allocation. - /// - /// - public static string? FirstOrDefault(this LazyStringSplit lazyStringSplit) - { - LazyStringSplit.Enumerator enumerator = lazyStringSplit.GetEnumerator(); + LazyStringSplit.Enumerator enumerator = lazyStringSplit.GetEnumerator(); - return enumerator.MoveNext() - ? enumerator.Current - : null; - } + return enumerator.MoveNext() + ? enumerator.Current + : null; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.SemaphoreDisposer.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.SemaphoreDisposer.cs index 801ce5158f..80553d8e1b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.SemaphoreDisposer.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.SemaphoreDisposer.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading +namespace Microsoft.VisualStudio.Threading; + +internal partial class SemaphoreSlimExtensions { - internal partial class SemaphoreSlimExtensions + internal readonly struct SemaphoreDisposer : IDisposable { - internal readonly struct SemaphoreDisposer : IDisposable - { - private readonly SemaphoreSlim _semaphore; + private readonly SemaphoreSlim _semaphore; - public SemaphoreDisposer(SemaphoreSlim semaphore) - { - _semaphore = semaphore; - } + public SemaphoreDisposer(SemaphoreSlim semaphore) + { + _semaphore = semaphore; + } - public void Dispose() - { - _semaphore.Release(); - } + public void Dispose() + { + _semaphore.Release(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.cs index 5f9872097a..28383cff6c 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/SemaphoreSlimExtensions.cs @@ -1,78 +1,77 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading +namespace Microsoft.VisualStudio.Threading; + +internal static partial class SemaphoreSlimExtensions { - internal static partial class SemaphoreSlimExtensions + public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, JoinableTaskFactory factory, Func> task, CancellationToken cancellationToken = default) { - public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, JoinableTaskFactory factory, Func> task, CancellationToken cancellationToken = default) + // Join the caller to our collection, so that if the lock is already held by another task that needs UI + // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS + // is draining critical tasks and is waiting us. + using (collection.Join()) { - // Join the caller to our collection, so that if the lock is already held by another task that needs UI - // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS - // is draining critical tasks and is waiting us. - using (collection.Join()) - { - await semaphore.WaitAsync(cancellationToken); + await semaphore.WaitAsync(cancellationToken); - using (new SemaphoreDisposer(semaphore)) - { - // We do an inner JoinableTaskFactory.RunAsync here to workaround - // https://github.com/Microsoft/vs-threading/issues/132 - JoinableTask joinableTask = factory.RunAsync(task); + using (new SemaphoreDisposer(semaphore)) + { + // We do an inner JoinableTaskFactory.RunAsync here to workaround + // https://github.com/Microsoft/vs-threading/issues/132 + JoinableTask joinableTask = factory.RunAsync(task); - return await joinableTask.Task; - } + return await joinableTask.Task; } } + } - public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, JoinableTaskFactory factory, Func task, CancellationToken cancellationToken = default) + public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, JoinableTaskFactory factory, Func task, CancellationToken cancellationToken = default) + { + // Join the caller to our collection, so that if the lock is already held by another task that needs UI + // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS + // is draining critical tasks and is waiting us. + using (collection.Join()) { - // Join the caller to our collection, so that if the lock is already held by another task that needs UI - // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS - // is draining critical tasks and is waiting us. - using (collection.Join()) - { - await semaphore.WaitAsync(cancellationToken); + await semaphore.WaitAsync(cancellationToken); - using (new SemaphoreDisposer(semaphore)) - { - // We do an inner JoinableTaskFactory.RunAsync here to workaround - // https://github.com/Microsoft/vs-threading/issues/132 - JoinableTask joinableTask = factory.RunAsync(task); + using (new SemaphoreDisposer(semaphore)) + { + // We do an inner JoinableTaskFactory.RunAsync here to workaround + // https://github.com/Microsoft/vs-threading/issues/132 + JoinableTask joinableTask = factory.RunAsync(task); - await joinableTask.Task; - } + await joinableTask.Task; } } + } - public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, Action action, CancellationToken cancellationToken = default) + public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, Action action, CancellationToken cancellationToken = default) + { + // Join the caller to our collection, so that if the lock is already held by another task that needs UI + // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS + // is draining critical tasks and is waiting us. + using (collection.Join()) { - // Join the caller to our collection, so that if the lock is already held by another task that needs UI - // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS - // is draining critical tasks and is waiting us. - using (collection.Join()) - { - await semaphore.WaitAsync(cancellationToken); + await semaphore.WaitAsync(cancellationToken); - using (new SemaphoreDisposer(semaphore)) - { - action(); - } + using (new SemaphoreDisposer(semaphore)) + { + action(); } } + } - public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, Func func, CancellationToken cancellationToken = default) + public static async Task ExecuteWithinLockAsync(this SemaphoreSlim semaphore, JoinableTaskCollection collection, Func func, CancellationToken cancellationToken = default) + { + // Join the caller to our collection, so that if the lock is already held by another task that needs UI + // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS + // is draining critical tasks and is waiting us. + using (collection.Join()) { - // Join the caller to our collection, so that if the lock is already held by another task that needs UI - // thread access we don't deadlock if we're also being waited on by the UI thread. For example, when CPS - // is draining critical tasks and is waiting us. - using (collection.Join()) - { - await semaphore.WaitAsync(cancellationToken); + await semaphore.WaitAsync(cancellationToken); - using (new SemaphoreDisposer(semaphore)) - { - return func(); - } + using (new SemaphoreDisposer(semaphore)) + { + return func(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/CancellationSeries.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/CancellationSeries.cs index 906a7b492e..ce7b71ca76 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/CancellationSeries.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/CancellationSeries.cs @@ -4,122 +4,121 @@ using System.Diagnostics; #endif -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +/// +/// Produces a series of objects such that requesting a new token +/// causes the previously issued token to be cancelled. +/// +/// +/// Consuming code is responsible for managing overlapping asynchronous operations. +/// This class has a lock-free implementation to minimise latency and contention. +/// +internal sealed class CancellationSeries : IDisposable { + private CancellationTokenSource? _cts = new(); + + private readonly CancellationToken _superToken; + /// - /// Produces a series of objects such that requesting a new token - /// causes the previously issued token to be cancelled. + /// Initializes a new instance of . /// - /// - /// Consuming code is responsible for managing overlapping asynchronous operations. - /// This class has a lock-free implementation to minimise latency and contention. - /// - internal sealed class CancellationSeries : IDisposable + /// An optional cancellation token that, when cancelled, cancels the last + /// issued token and causes any subsequent tokens to be issued in a cancelled state. + public CancellationSeries(CancellationToken token = default) { - private CancellationTokenSource? _cts = new(); - - private readonly CancellationToken _superToken; - - /// - /// Initializes a new instance of . - /// - /// An optional cancellation token that, when cancelled, cancels the last - /// issued token and causes any subsequent tokens to be issued in a cancelled state. - public CancellationSeries(CancellationToken token = default) - { - _superToken = token; + _superToken = token; #if DEBUG - _ctorStack = new StackTrace(); + _ctorStack = new StackTrace(); #endif - } + } #if DEBUG - private readonly StackTrace _ctorStack; + private readonly StackTrace _ctorStack; - ~CancellationSeries() - { - Debug.Assert( - Environment.HasShutdownStarted || _cts is null, - "Instance of CancellationSeries not disposed before being finalized", - "Stack at construction:{0}{1}", - Environment.NewLine, - _ctorStack); - } + ~CancellationSeries() + { + Debug.Assert( + Environment.HasShutdownStarted || _cts is null, + "Instance of CancellationSeries not disposed before being finalized", + "Stack at construction:{0}{1}", + Environment.NewLine, + _ctorStack); + } #endif - /// - /// Creates the next in the series, ensuring the last issued - /// token (if any) is cancelled first. - /// - /// An optional cancellation token that, when cancelled, cancels the - /// returned token. - /// - /// A cancellation token that will be cancelled when either: - /// - /// is called again - /// The token passed to this method (if any) is cancelled - /// The token passed to the constructor (if any) is cancelled - /// is called - /// - /// - /// This object has been disposed. - public CancellationToken CreateNext(CancellationToken token = default) - { + /// + /// Creates the next in the series, ensuring the last issued + /// token (if any) is cancelled first. + /// + /// An optional cancellation token that, when cancelled, cancels the + /// returned token. + /// + /// A cancellation token that will be cancelled when either: + /// + /// is called again + /// The token passed to this method (if any) is cancelled + /// The token passed to the constructor (if any) is cancelled + /// is called + /// + /// + /// This object has been disposed. + public CancellationToken CreateNext(CancellationToken token = default) + { #pragma warning disable RS0030 // Do not used banned APIs (cannot use alternative API here) - var nextSource = CancellationTokenSource.CreateLinkedTokenSource(token, _superToken); + var nextSource = CancellationTokenSource.CreateLinkedTokenSource(token, _superToken); #pragma warning restore RS0030 // Do not used banned APIs - // Obtain the token before exchange, as otherwise the CTS may be cancelled before - // we request the Token, which will result in an ObjectDisposedException. - // This way we would return a cancelled token, which is reasonable. - CancellationToken nextToken = nextSource.Token; - - CancellationTokenSource? priorSource = Interlocked.Exchange(ref _cts, nextSource); + // Obtain the token before exchange, as otherwise the CTS may be cancelled before + // we request the Token, which will result in an ObjectDisposedException. + // This way we would return a cancelled token, which is reasonable. + CancellationToken nextToken = nextSource.Token; - if (priorSource is null) - { - nextSource.Dispose(); + CancellationTokenSource? priorSource = Interlocked.Exchange(ref _cts, nextSource); - throw new ObjectDisposedException(nameof(CancellationSeries)); - } - - try - { - priorSource.Cancel(); - } - finally - { - // A registered action on the token may throw, which would surface here. - // Ensure we always dispose the prior CTS. - priorSource.Dispose(); - } + if (priorSource is null) + { + nextSource.Dispose(); - return nextToken; + throw new ObjectDisposedException(nameof(CancellationSeries)); } - public void Dispose() + try + { + priorSource.Cancel(); + } + finally { + // A registered action on the token may throw, which would surface here. + // Ensure we always dispose the prior CTS. + priorSource.Dispose(); + } + + return nextToken; + } + + public void Dispose() + { #if DEBUG - GC.SuppressFinalize(this); + GC.SuppressFinalize(this); #endif - CancellationTokenSource? source = Interlocked.Exchange(ref _cts, null); - - if (source is null) - { - // Already disposed - return; - } - - try - { - source.Cancel(); - } - finally - { - source.Dispose(); - } + CancellationTokenSource? source = Interlocked.Exchange(ref _cts, null); + + if (source is null) + { + // Already disposed + return; + } + + try + { + source.Cancel(); + } + finally + { + source.Dispose(); } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/ITaskDelayScheduler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/ITaskDelayScheduler.cs index 6efeaf21ca..e0fd88a7a3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/ITaskDelayScheduler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/ITaskDelayScheduler.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +internal interface ITaskDelayScheduler : IDisposable { - internal interface ITaskDelayScheduler : IDisposable - { - /// - /// Schedules an asynchronous operation to be run after some delay, acting as a trailing-edge debouncer. - /// Subsequent scheduled operations will cancel previously scheduled tasks. - /// - /// - /// Operations can overlap, however the passed to an earlier - /// operation is cancelled if and when a later operation is scheduled, which always occurs before that - /// later operation is started. It is up to the caller to ensure proper use of the cancellation token - /// provided when is invoked. - /// - /// - /// The returned Task represents - /// the current scheduled task but not necessarily represents the task that - /// ends up doing the actual work. If another task is scheduled later which causes - /// the cancellation of the current scheduled task, the caller will not know - /// and need to use that latest return task instead. - /// - /// - /// The object has been disposed. - JoinableTask ScheduleAsyncTask(Func operation, CancellationToken token = default); - } + /// + /// Schedules an asynchronous operation to be run after some delay, acting as a trailing-edge debouncer. + /// Subsequent scheduled operations will cancel previously scheduled tasks. + /// + /// + /// Operations can overlap, however the passed to an earlier + /// operation is cancelled if and when a later operation is scheduled, which always occurs before that + /// later operation is started. It is up to the caller to ensure proper use of the cancellation token + /// provided when is invoked. + /// + /// + /// The returned Task represents + /// the current scheduled task but not necessarily represents the task that + /// ends up doing the actual work. If another task is scheduled later which causes + /// the cancellation of the current scheduled task, the caller will not know + /// and need to use that latest return task instead. + /// + /// + /// The object has been disposed. + JoinableTask ScheduleAsyncTask(Func operation, CancellationToken token = default); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/SequentialTaskExecutor.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/SequentialTaskExecutor.cs index f6f139cf2e..c19c224d8d 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/SequentialTaskExecutor.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/SequentialTaskExecutor.cs @@ -1,130 +1,129 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +/// +/// Runs tasks in the sequence they are added. This is done by starting with a completed task, and leveraging ContinueWith\Unwrap to +/// "schedule" subsequent tasks to start after the previous one is completed. The Task containing the callers function is returned so that +/// the caller can await for their specific task to complete. When disposed unprocessed tasks are cancelled. +/// +internal sealed class SequentialTaskExecutor : IDisposable { + private bool _disposed; + private Task _taskAdded = Task.CompletedTask; + private readonly object _syncObject = new(); + private readonly CancellationTokenSource _disposedCancelTokenSource = new(); + private readonly JoinableTaskCollection _joinableCollection; + private readonly JoinableTaskFactory _joinableFactory; + + public SequentialTaskExecutor(JoinableTaskContextNode joinableTaskContext, string displayName) + { + _joinableCollection = joinableTaskContext.CreateCollection(); + _joinableCollection.DisplayName = $"{nameof(SequentialTaskExecutor)}_{displayName}"; + _joinableFactory = joinableTaskContext.CreateFactory(_joinableCollection); + } + /// - /// Runs tasks in the sequence they are added. This is done by starting with a completed task, and leveraging ContinueWith\Unwrap to - /// "schedule" subsequent tasks to start after the previous one is completed. The Task containing the callers function is returned so that - /// the caller can await for their specific task to complete. When disposed unprocessed tasks are cancelled. + /// Deadlocks will occur if a task returned from ExecuteTask, awaits a task which also calls ExecuteTask. The 2nd one will never get started since + /// it will be backed up behind the first one completing. The AsyncLocal is used to detect when a task is being executed, and if a downstream one gets + /// added, it will be executed directly, rather than get queued /// - internal sealed class SequentialTaskExecutor : IDisposable - { - private bool _disposed; - private Task _taskAdded = Task.CompletedTask; - private readonly object _syncObject = new(); - private readonly CancellationTokenSource _disposedCancelTokenSource = new(); - private readonly JoinableTaskCollection _joinableCollection; - private readonly JoinableTaskFactory _joinableFactory; + private readonly System.Threading.AsyncLocal _executingTask = new(); - public SequentialTaskExecutor(JoinableTaskContextNode joinableTaskContext, string displayName) + /// + /// Adds a new task to the continuation chain and returns it so that it can be awaited. + /// + public Task ExecuteTask(Func asyncFunction) + { + if (_disposed) { - _joinableCollection = joinableTaskContext.CreateCollection(); - _joinableCollection.DisplayName = $"{nameof(SequentialTaskExecutor)}_{displayName}"; - _joinableFactory = joinableTaskContext.CreateFactory(_joinableCollection); + throw new ObjectDisposedException(nameof(SequentialTaskExecutor)); } - /// - /// Deadlocks will occur if a task returned from ExecuteTask, awaits a task which also calls ExecuteTask. The 2nd one will never get started since - /// it will be backed up behind the first one completing. The AsyncLocal is used to detect when a task is being executed, and if a downstream one gets - /// added, it will be executed directly, rather than get queued - /// - private readonly System.Threading.AsyncLocal _executingTask = new(); - - /// - /// Adds a new task to the continuation chain and returns it so that it can be awaited. - /// - public Task ExecuteTask(Func asyncFunction) + lock (_syncObject) { - if (_disposed) + // If we are on the same execution chain, run the task directly + if (_executingTask.Value) { - throw new ObjectDisposedException(nameof(SequentialTaskExecutor)); + return asyncFunction(); } - lock (_syncObject) + _taskAdded = _taskAdded.ContinueWith(async t => { - // If we are on the same execution chain, run the task directly - if (_executingTask.Value) + _disposedCancelTokenSource.Token.ThrowIfCancellationRequested(); + try { - return asyncFunction(); + _executingTask.Value = true; + await _joinableFactory.RunAsync(asyncFunction); } - - _taskAdded = _taskAdded.ContinueWith(async t => + finally { - _disposedCancelTokenSource.Token.ThrowIfCancellationRequested(); - try - { - _executingTask.Value = true; - await _joinableFactory.RunAsync(asyncFunction); - } - finally - { - _executingTask.Value = false; - } - }, TaskScheduler.Default).Unwrap(); + _executingTask.Value = false; + } + }, TaskScheduler.Default).Unwrap(); - return JoinTaskAsync(); + return JoinTaskAsync(); - async Task JoinTaskAsync() + async Task JoinTaskAsync() + { + using (_joinableCollection.Join()) { - using (_joinableCollection.Join()) - { - await _taskAdded; - } + await _taskAdded; } } } + } - public Task ExecuteTask(Func> asyncFunction) + public Task ExecuteTask(Func> asyncFunction) + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(SequentialTaskExecutor)); + } + + lock (_syncObject) { - if (_disposed) + // If we are on the same execution chain, run the task directly + if (_executingTask.Value) { - throw new ObjectDisposedException(nameof(SequentialTaskExecutor)); + return asyncFunction(); } - lock (_syncObject) + _taskAdded = _taskAdded.ContinueWith(async t => { - // If we are on the same execution chain, run the task directly - if (_executingTask.Value) + _disposedCancelTokenSource.Token.ThrowIfCancellationRequested(); + try { - return asyncFunction(); + _executingTask.Value = true; + return await _joinableFactory.RunAsync(asyncFunction); } - - _taskAdded = _taskAdded.ContinueWith(async t => + finally { - _disposedCancelTokenSource.Token.ThrowIfCancellationRequested(); - try - { - _executingTask.Value = true; - return await _joinableFactory.RunAsync(asyncFunction); - } - finally - { - _executingTask.Value = false; - } - }, TaskScheduler.Default).Unwrap(); + _executingTask.Value = false; + } + }, TaskScheduler.Default).Unwrap(); - return JoinTaskAsync(); + return JoinTaskAsync(); - async Task JoinTaskAsync() + async Task JoinTaskAsync() + { + using (_joinableCollection.Join()) { - using (_joinableCollection.Join()) - { - return await (Task)_taskAdded; - } + return await (Task)_taskAdded; } } } + } - /// - /// Dispose cancels outstanding tasks - /// - public void Dispose() + /// + /// Dispose cancels outstanding tasks + /// + public void Dispose() + { + if (!_disposed) { - if (!_disposed) - { - _disposedCancelTokenSource.Cancel(); - _disposed = true; - } + _disposedCancelTokenSource.Cancel(); + _disposed = true; } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskCompletionSource.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskCompletionSource.cs index 855980419a..7f8b57ca44 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskCompletionSource.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskCompletionSource.cs @@ -4,56 +4,55 @@ using System.ComponentModel; -namespace System.Threading.Tasks +namespace System.Threading.Tasks; + +/// +/// +/// This provides the non-generic version of a +/// +internal class TaskCompletionSource : TaskCompletionSource { - /// - /// - /// This provides the non-generic version of a - /// - internal class TaskCompletionSource : TaskCompletionSource + /// + public new Task Task + { + get { return base.Task; } + } + + public TaskCompletionSource() + { + } + + public TaskCompletionSource(TaskCreationOptions taskCreationOptions) + : base(taskCreationOptions) + { + } + + /// + public void SetResult() + { + base.SetResult(null); + } + + /// + public bool TrySetResult() + { + return base.TrySetResult(null); + } + + /// + [Obsolete("Use TaskCompletionSource.SetResult()")] + [EditorBrowsable(EditorBrowsableState.Never)] + public new void SetResult(object? result) + { + base.SetResult(result); + } + + /// + [Obsolete("Use TaskCompletionSource.TrySetResult()")] + [EditorBrowsable(EditorBrowsableState.Never)] + public new bool TrySetResult(object? result) { - /// - public new Task Task - { - get { return base.Task; } - } - - public TaskCompletionSource() - { - } - - public TaskCompletionSource(TaskCreationOptions taskCreationOptions) - : base(taskCreationOptions) - { - } - - /// - public void SetResult() - { - base.SetResult(null); - } - - /// - public bool TrySetResult() - { - return base.TrySetResult(null); - } - - /// - [Obsolete("Use TaskCompletionSource.SetResult()")] - [EditorBrowsable(EditorBrowsableState.Never)] - public new void SetResult(object? result) - { - base.SetResult(result); - } - - /// - [Obsolete("Use TaskCompletionSource.TrySetResult()")] - [EditorBrowsable(EditorBrowsableState.Never)] - public new bool TrySetResult(object? result) - { - return base.TrySetResult(result); - } + return base.TrySetResult(result); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskDelayScheduler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskDelayScheduler.cs index 39678a1451..30ff3f4a25 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskDelayScheduler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskDelayScheduler.cs @@ -2,65 +2,64 @@ using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +/// +/// Helper class which allows a task to be scheduled to run after some delay, but if a new task +/// is scheduled before the delay runs out, the previous task is cancelled. +/// +internal sealed class TaskDelayScheduler : ITaskDelayScheduler { + private readonly TimeSpan _taskDelayTime; + private readonly IProjectThreadingService _threadingService; + private readonly CancellationSeries _cancellationSeries; + /// - /// Helper class which allows a task to be scheduled to run after some delay, but if a new task - /// is scheduled before the delay runs out, the previous task is cancelled. + /// Creates an instance of the TaskDelayScheduler. If an originalSourceToken is passed, it will be linked to the PendingUpdateTokenSource so + /// that cancelling that token will also flow through and cancel a pending update. /// - internal sealed class TaskDelayScheduler : ITaskDelayScheduler + public TaskDelayScheduler(TimeSpan taskDelayTime, IProjectThreadingService threadService, CancellationToken originalSourceToken) { - private readonly TimeSpan _taskDelayTime; - private readonly IProjectThreadingService _threadingService; - private readonly CancellationSeries _cancellationSeries; + _taskDelayTime = taskDelayTime; + _threadingService = threadService; + _cancellationSeries = new CancellationSeries(originalSourceToken); + } - /// - /// Creates an instance of the TaskDelayScheduler. If an originalSourceToken is passed, it will be linked to the PendingUpdateTokenSource so - /// that cancelling that token will also flow through and cancel a pending update. - /// - public TaskDelayScheduler(TimeSpan taskDelayTime, IProjectThreadingService threadService, CancellationToken originalSourceToken) - { - _taskDelayTime = taskDelayTime; - _threadingService = threadService; - _cancellationSeries = new CancellationSeries(originalSourceToken); - } + public JoinableTask ScheduleAsyncTask(Func operation, CancellationToken token = default) + { + CancellationToken nextToken = _cancellationSeries.CreateNext(token); - public JoinableTask ScheduleAsyncTask(Func operation, CancellationToken token = default) + // We want to return a joinable task so wrap the function + return _threadingService.JoinableTaskFactory.RunAsync(async () => { - CancellationToken nextToken = _cancellationSeries.CreateNext(token); - - // We want to return a joinable task so wrap the function - return _threadingService.JoinableTaskFactory.RunAsync(async () => + if (nextToken.IsCancellationRequested) { - if (nextToken.IsCancellationRequested) - { - return; - } + return; + } - try - { - await Task.Delay(_taskDelayTime, nextToken); - } - catch (OperationCanceledException) - { - return; - } + try + { + await Task.Delay(_taskDelayTime, nextToken); + } + catch (OperationCanceledException) + { + return; + } - if (nextToken.IsCancellationRequested) - { - return; - } + if (nextToken.IsCancellationRequested) + { + return; + } - await operation(nextToken); - }); - } + await operation(nextToken); + }); + } - /// - /// Cancels any pending tasks and disposes this object. - /// - public void Dispose() - { - _cancellationSeries.Dispose(); - } + /// + /// Cancels any pending tasks and disposes this object. + /// + public void Dispose() + { + _cancellationSeries.Dispose(); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskExtensions.cs index 0ef02a2ee1..f4a828925b 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskExtensions.cs @@ -1,50 +1,49 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +/// +/// Provides extensions for instances. +/// +internal static class TaskExtensions { /// - /// Provides extensions for instances. + /// Creates a task that will complete indicating whether the specified task completed or timed-out. /// - internal static class TaskExtensions + /// + /// The to wait on for completion. + /// + /// + /// The number of milliseconds to wait, or (-1) to wait indefinitely. + /// + /// + /// An instance on which to wait. + /// + /// + /// is a negative number other than -1, which represents an infinite time-out. + /// + /// + /// is cancelled. + /// + public static async Task TryWaitForCompleteOrTimeoutAsync(this Task task, int millisecondsTimeout) { - /// - /// Creates a task that will complete indicating whether the specified task completed or timed-out. - /// - /// - /// The to wait on for completion. - /// - /// - /// The number of milliseconds to wait, or (-1) to wait indefinitely. - /// - /// - /// An instance on which to wait. - /// - /// - /// is a negative number other than -1, which represents an infinite time-out. - /// - /// - /// is cancelled. - /// - public static async Task TryWaitForCompleteOrTimeoutAsync(this Task task, int millisecondsTimeout) + if (millisecondsTimeout == Timeout.Infinite) { - if (millisecondsTimeout == Timeout.Infinite) - { - await task; - return true; - } - - using var cts = new CancellationTokenSource(); + await task; + return true; + } - var delay = Task.Delay(millisecondsTimeout, cts.Token); + using var cts = new CancellationTokenSource(); - if (ReferenceEquals(delay, await Task.WhenAny(task, delay))) - { - return false; - } + var delay = Task.Delay(millisecondsTimeout, cts.Token); - cts.Cancel(); - await task; - return true; + if (ReferenceEquals(delay, await Task.WhenAny(task, delay))) + { + return false; } + + cts.Cancel(); + await task; + return true; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskResult.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskResult.cs index 251d5792a3..2c27f2a739 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskResult.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Threading/Tasks/TaskResult.cs @@ -2,56 +2,55 @@ #pragma warning disable RS0030 // Do not used banned APIs -namespace Microsoft.VisualStudio.Threading +namespace Microsoft.VisualStudio.Threading; + +/// +/// Provides sentinel Tasks that represent commonly returned values. +/// +internal static class TaskResult { /// - /// Provides sentinel Tasks that represent commonly returned values. + /// Represents a that's completed successfully with the result of . + /// + public static Task False => TplExtensions.FalseTask; + + /// + /// Represents a that's completed successfully with the result of . + /// + public static Task True => TplExtensions.TrueTask; + + /// + /// Represents a that's completed successfully with result of the boolean false string. + /// + public static Task FalseString => Task.FromResult(bool.FalseString); + + /// + /// Represents a that's completed successfully with result of the boolean true string. /// - internal static class TaskResult + public static Task TrueString => Task.FromResult(bool.TrueString); + + /// + /// Represents a that's completed successfully with result of the empty string. + /// + public static Task EmptyString => Task.FromResult(""); + + /// + /// Returns a of type that's completed successfully with the result of . + /// + public static Task Null() where T : class => NullTaskResult.Instance; + + private static class NullTaskResult where T : class + { + public static readonly Task Instance = Task.FromResult(null); + } + + /// + /// Returns a whose value is an empty enumerable of type . + /// + public static Task> EmptyEnumerable() => EmptyEnumerableTaskResult.Instance; + + private static class EmptyEnumerableTaskResult { - /// - /// Represents a that's completed successfully with the result of . - /// - public static Task False => TplExtensions.FalseTask; - - /// - /// Represents a that's completed successfully with the result of . - /// - public static Task True => TplExtensions.TrueTask; - - /// - /// Represents a that's completed successfully with result of the boolean false string. - /// - public static Task FalseString => Task.FromResult(bool.FalseString); - - /// - /// Represents a that's completed successfully with result of the boolean true string. - /// - public static Task TrueString => Task.FromResult(bool.TrueString); - - /// - /// Represents a that's completed successfully with result of the empty string. - /// - public static Task EmptyString => Task.FromResult(""); - - /// - /// Returns a of type that's completed successfully with the result of . - /// - public static Task Null() where T : class => NullTaskResult.Instance; - - private static class NullTaskResult where T : class - { - public static readonly Task Instance = Task.FromResult(null); - } - - /// - /// Returns a whose value is an empty enumerable of type . - /// - public static Task> EmptyEnumerable() => EmptyEnumerableTaskResult.Instance; - - private static class EmptyEnumerableTaskResult - { - public static readonly Task> Instance = Task.FromResult(Enumerable.Empty()); - } + public static readonly Task> Instance = Task.FromResult(Enumerable.Empty()); } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/DeconstructionExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/DeconstructionExtensions.cs index 0ffb8d6a14..0de0e1b623 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/DeconstructionExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/DeconstructionExtensions.cs @@ -2,15 +2,14 @@ using System.Diagnostics; -namespace System.Collections.Generic +namespace System.Collections.Generic; + +internal static class DeconstructionExtensions { - internal static class DeconstructionExtensions + [DebuggerStepThrough] + public static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) { - [DebuggerStepThrough] - public static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) - { - key = pair.Key; - value = pair.Value; - } + key = pair.Key; + value = pair.Value; } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/SetDiff.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/SetDiff.cs index cffbd9c403..c693eecb63 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/SetDiff.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/SetDiff.cs @@ -2,97 +2,96 @@ using System.Collections; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal sealed class SetDiff where T : notnull { - internal sealed class SetDiff where T : notnull - { - private const byte FlagBefore = 0; - private const byte FlagAfter = 1; + private const byte FlagBefore = 0; + private const byte FlagAfter = 1; - private readonly Dictionary _dic; + private readonly Dictionary _dic; - public Part Removed => new(_dic, FlagBefore); + public Part Removed => new(_dic, FlagBefore); - public Part Added => new(_dic, FlagAfter); + public Part Added => new(_dic, FlagAfter); - public SetDiff(IEnumerable before, IEnumerable after, IEqualityComparer? equalityComparer = null) - { - Requires.NotNull(before); - Requires.NotNull(after); + public SetDiff(IEnumerable before, IEnumerable after, IEqualityComparer? equalityComparer = null) + { + Requires.NotNull(before); + Requires.NotNull(after); - equalityComparer ??= EqualityComparer.Default; + equalityComparer ??= EqualityComparer.Default; - var dic = new Dictionary(equalityComparer); + var dic = new Dictionary(equalityComparer); - foreach (T item in before) - { - dic[item] = FlagBefore; - } + foreach (T item in before) + { + dic[item] = FlagBefore; + } - foreach (T item in after) + foreach (T item in after) + { + if (!dic.Remove(item)) { - if (!dic.Remove(item)) - { - dic[item] = FlagAfter; - } + dic[item] = FlagAfter; } + } + + _dic = dic; + } + + public readonly struct Part : IEnumerable + { + private readonly Dictionary _dic; + private readonly byte _flag; + public Part(Dictionary dic, byte flag) + { _dic = dic; + _flag = flag; } - public readonly struct Part : IEnumerable + public PartEnumerator GetEnumerator() => new(_dic, _flag); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct PartEnumerator : IEnumerator { - private readonly Dictionary _dic; private readonly byte _flag; - public Part(Dictionary dic, byte flag) + // IMPORTANT cannot be readonly + private Dictionary.Enumerator _enumerator; + + public PartEnumerator(Dictionary dic, byte flag) { - _dic = dic; _flag = flag; + _enumerator = dic.GetEnumerator(); + Current = default!; } - public PartEnumerator GetEnumerator() => new(_dic, _flag); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public struct PartEnumerator : IEnumerator + public bool MoveNext() { - private readonly byte _flag; - - // IMPORTANT cannot be readonly - private Dictionary.Enumerator _enumerator; - - public PartEnumerator(Dictionary dic, byte flag) - { - _flag = flag; - _enumerator = dic.GetEnumerator(); - Current = default!; - } - - public bool MoveNext() + while (_enumerator.MoveNext()) { - while (_enumerator.MoveNext()) + if (_enumerator.Current.Value == _flag) { - if (_enumerator.Current.Value == _flag) - { - Current = _enumerator.Current.Key; - return true; - } + Current = _enumerator.Current.Key; + return true; } - - return false; } - public T Current { get; private set; } + return false; + } + + public T Current { get; private set; } - object IEnumerator.Current => Current!; + object IEnumerator.Current => Current!; - void IEnumerator.Reset() => throw new NotSupportedException(); + void IEnumerator.Reset() => throw new NotSupportedException(); - void IDisposable.Dispose() { } - } + void IDisposable.Dispose() { } } } } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/Strings.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/Strings.cs index dbff5fdab2..d4a8b93b60 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/Strings.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Utilities/Strings.cs @@ -2,14 +2,13 @@ using System.Diagnostics.CodeAnalysis; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class Strings { - internal static class Strings - { - /// - public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] string? s) => string.IsNullOrEmpty(s); - - /// - public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] string? s) => string.IsNullOrWhiteSpace(s); - } + /// + public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] string? s) => string.IsNullOrEmpty(s); + + /// + public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] string? s) => string.IsNullOrWhiteSpace(s); } diff --git a/tests/Common/ThrowingTraceListener.cs b/tests/Common/ThrowingTraceListener.cs index 31c4d3ff99..fa0ef4646c 100644 --- a/tests/Common/ThrowingTraceListener.cs +++ b/tests/Common/ThrowingTraceListener.cs @@ -5,57 +5,56 @@ using System.Runtime.Serialization; #endif -namespace Microsoft.VisualStudio.Diagnostics +namespace Microsoft.VisualStudio.Diagnostics; + +// To enable this for a process, add the following to the app.config for the project: +// +// +// +// +// +// +// +// +// +// +// +public sealed class ThrowingTraceListener : TraceListener { - // To enable this for a process, add the following to the app.config for the project: - // - // - // - // - // - // - // - // - // - // - // - public sealed class ThrowingTraceListener : TraceListener + public override void Fail(string message, string detailMessage) { - public override void Fail(string message, string detailMessage) + throw new DebugAssertFailureException(message + Environment.NewLine + detailMessage); + } + + public override void Write(string message) + { + } + + public override void WriteLine(string message) + { + } + +#if !NET8_0_OR_GREATER + [Serializable] +#endif + public class DebugAssertFailureException : Exception + { + public DebugAssertFailureException() { - throw new DebugAssertFailureException(message + Environment.NewLine + detailMessage); } - public override void Write(string message) + public DebugAssertFailureException(string message) : base(message) { } - public override void WriteLine(string message) + public DebugAssertFailureException(string message, Exception inner) : base(message, inner) { } #if !NET8_0_OR_GREATER - [Serializable] -#endif - public class DebugAssertFailureException : Exception + protected DebugAssertFailureException(SerializationInfo info, StreamingContext context) : base(info, context) { - public DebugAssertFailureException() - { - } - - public DebugAssertFailureException(string message) : base(message) - { - } - - public DebugAssertFailureException(string message, Exception inner) : base(message, inner) - { - } - -#if !NET8_0_OR_GREATER - protected DebugAssertFailureException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -#endif } +#endif } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/GlobalJson.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/GlobalJson.cs index 5ecca53585..beec9a9456 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/GlobalJson.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/GlobalJson.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Defines a global.json file to be created when using . +/// +public sealed class GlobalJson { - /// - /// Defines a global.json file to be created when using . - /// - public sealed class GlobalJson - { - public string SdkVersion { get; } + public string SdkVersion { get; } - public GlobalJson(string sdkVersion) => SdkVersion = sdkVersion; + public GlobalJson(string sdkVersion) => SdkVersion = sdkVersion; - public void Save(string rootPath) - { - File.WriteAllText( - Path.Combine(rootPath, "global.json"), - $"{{ \"sdk\": {{ \"version\": \"{SdkVersion}\" }} }}"); - } + public void Save(string rootPath) + { + File.WriteAllText( + Path.Combine(rootPath, "global.json"), + $"{{ \"sdk\": {{ \"version\": \"{SdkVersion}\" }} }}"); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Project.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Project.cs index 4ba9ac8eae..7552fc36d9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Project.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Project.cs @@ -4,166 +4,165 @@ using System.Xml.Linq; using Microsoft.Test.Apex.VisualStudio.Solution; -namespace Microsoft.VisualStudio.ProjectSystem.VS -{ - /// - /// Defines a .csproj file to be created when using . - /// - public sealed class Project : IEnumerable - { - private static readonly Guid s_sdkProjectTypeGuid = Guid.Parse("9A19103F-16F7-4668-BE54-9A1E7A4F7556"); - - private List? _referencedProjects; - private List? _packageReferences; - private List? _assemblyReferences; - private List? _files; +namespace Microsoft.VisualStudio.ProjectSystem.VS; - public XElement XElement { get; } = new XElement("Project"); - - public string Sdk { get; } - public string TargetFrameworks { get; } - - public string ProjectName { get; } = "Project_" + Guid.NewGuid().ToString("N").Substring(0, 12); - public string ProjectFileName => $"{ProjectName}.csproj"; - public string RelativeProjectFilePath => $"{ProjectName}\\{ProjectName}.csproj"; - - public Guid ProjectGuid { get; } = Guid.NewGuid(); - public object ProjectTypeGuid => s_sdkProjectTypeGuid; +/// +/// Defines a .csproj file to be created when using . +/// +public sealed class Project : IEnumerable +{ + private static readonly Guid s_sdkProjectTypeGuid = Guid.Parse("9A19103F-16F7-4668-BE54-9A1E7A4F7556"); - public ProjectTestExtension? Extension { get; set; } + private List? _referencedProjects; + private List? _packageReferences; + private List? _assemblyReferences; + private List? _files; - public Project(string targetFrameworks, string sdk = "Microsoft.NET.Sdk") - { - TargetFrameworks = targetFrameworks; - Sdk = sdk; - } + public XElement XElement { get; } = new XElement("Project"); - public void Save(string solutionRoot) - { - XElement.Add(new XAttribute("Sdk", Sdk)); + public string Sdk { get; } + public string TargetFrameworks { get; } - XElement.Add(new XElement( - "PropertyGroup", - new XElement("TargetFrameworks", TargetFrameworks))); + public string ProjectName { get; } = "Project_" + Guid.NewGuid().ToString("N").Substring(0, 12); + public string ProjectFileName => $"{ProjectName}.csproj"; + public string RelativeProjectFilePath => $"{ProjectName}\\{ProjectName}.csproj"; - if (_referencedProjects is not null) - { - XElement.Add(new XElement( - "ItemGroup", - _referencedProjects.Select(p => new XElement( - "ProjectReference", - new XAttribute("Include", $"..\\{p.RelativeProjectFilePath}"))))); - } + public Guid ProjectGuid { get; } = Guid.NewGuid(); + public object ProjectTypeGuid => s_sdkProjectTypeGuid; - if (_assemblyReferences is not null) - { - XElement.Add(new XElement( - "ItemGroup", - _assemblyReferences.Select(p => new XElement( - "Reference", - new XAttribute("Include", p.Name))))); - } + public ProjectTestExtension? Extension { get; set; } - if (_packageReferences is not null) - { - XElement.Add(new XElement( - "ItemGroup", - _packageReferences.Select(p => new XElement( - "PackageReference", - new XAttribute("Include", p.PackageId), - new XAttribute("Version", p.Version))))); - } - - var projectRoot = Path.Combine(solutionRoot, ProjectName); - - Directory.CreateDirectory(projectRoot); + public Project(string targetFrameworks, string sdk = "Microsoft.NET.Sdk") + { + TargetFrameworks = targetFrameworks; + Sdk = sdk; + } - XElement.Save(Path.Combine(solutionRoot, RelativeProjectFilePath)); + public void Save(string solutionRoot) + { + XElement.Add(new XAttribute("Sdk", Sdk)); - if (_files is not null) - { - foreach (var file in _files) - { - file.Save(projectRoot); - } - } - } + XElement.Add(new XElement( + "PropertyGroup", + new XElement("TargetFrameworks", TargetFrameworks))); - /// - /// Adds a P2P (project-to-project) reference from this project to . - /// - /// The project to reference. - public void Add(Project referree) + if (_referencedProjects is not null) { - _referencedProjects ??= new List(); - _referencedProjects.Add(referree); + XElement.Add(new XElement( + "ItemGroup", + _referencedProjects.Select(p => new XElement( + "ProjectReference", + new XAttribute("Include", $"..\\{p.RelativeProjectFilePath}"))))); } - public void Add(PackageReference packageReference) + if (_assemblyReferences is not null) { - _packageReferences ??= new List(); - _packageReferences.Add(packageReference); + XElement.Add(new XElement( + "ItemGroup", + _assemblyReferences.Select(p => new XElement( + "Reference", + new XAttribute("Include", p.Name))))); } - public void Add(AssemblyReference assemblyReference) + if (_packageReferences is not null) { - _assemblyReferences ??= new List(); - _assemblyReferences.Add(assemblyReference); + XElement.Add(new XElement( + "ItemGroup", + _packageReferences.Select(p => new XElement( + "PackageReference", + new XAttribute("Include", p.PackageId), + new XAttribute("Version", p.Version))))); } - public void Add(IFile file) + var projectRoot = Path.Combine(solutionRoot, ProjectName); + + Directory.CreateDirectory(projectRoot); + + XElement.Save(Path.Combine(solutionRoot, RelativeProjectFilePath)); + + if (_files is not null) { - _files ??= new List(); - _files.Add(file); + foreach (var file in _files) + { + file.Save(projectRoot); + } } - - /// - /// We only implement to support collection initialiser syntax. - /// - IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); } - public readonly struct PackageReference + /// + /// Adds a P2P (project-to-project) reference from this project to . + /// + /// The project to reference. + public void Add(Project referree) { - public string PackageId { get; } - public string Version { get; } + _referencedProjects ??= new List(); + _referencedProjects.Add(referree); + } - public PackageReference(string packageId, string version) - { - PackageId = packageId; - Version = version; - } + public void Add(PackageReference packageReference) + { + _packageReferences ??= new List(); + _packageReferences.Add(packageReference); } - public readonly struct AssemblyReference + public void Add(AssemblyReference assemblyReference) { - public string Name { get; } + _assemblyReferences ??= new List(); + _assemblyReferences.Add(assemblyReference); + } - public AssemblyReference(string name) - { - Name = name; - } + public void Add(IFile file) + { + _files ??= new List(); + _files.Add(file); } - public interface IFile + /// + /// We only implement to support collection initialiser syntax. + /// + IEnumerator IEnumerable.GetEnumerator() => throw new NotSupportedException(); +} + +public readonly struct PackageReference +{ + public string PackageId { get; } + public string Version { get; } + + public PackageReference(string packageId, string version) { - void Save(string projectRoot); + PackageId = packageId; + Version = version; } +} + +public readonly struct AssemblyReference +{ + public string Name { get; } - public sealed class CSharpClass : IFile + public AssemblyReference(string name) { - public string Name { get; } + Name = name; + } +} - public CSharpClass(string name) - { - Name = name; - } +public interface IFile +{ + void Save(string projectRoot); +} - public void Save(string projectRoot) - { - var content = $@"class {Name} {{ }}"; +public sealed class CSharpClass : IFile +{ + public string Name { get; } - File.WriteAllText(Path.Combine(projectRoot, $"{Name}.cs"), content); - } + public CSharpClass(string name) + { + Name = name; + } + + public void Save(string projectRoot) + { + var content = $@"class {Name} {{ }}"; + + File.WriteAllText(Path.Combine(projectRoot, $"{Name}.cs"), content); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Solution.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Solution.cs index 07189cae7b..9c3d2beb63 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Solution.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Construction/Solution.cs @@ -3,80 +3,79 @@ using System.Collections; using System.Text; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Defines a .sln file to be created when using . +/// +public sealed class Solution : IEnumerable { - /// - /// Defines a .sln file to be created when using . - /// - public sealed class Solution : IEnumerable - { - private readonly List _projects = new(); + private readonly List _projects = new(); - public IEnumerable Projects => _projects; + public IEnumerable Projects => _projects; - public GlobalJson? GlobalJson { get; private set; } + public GlobalJson? GlobalJson { get; private set; } - public void Save(string path) - { - using var stream = File.OpenWrite(path); - using var writer = new StreamWriter(stream, Encoding.ASCII); - - writer.WriteLine(); - writer.WriteLine( - """ - Microsoft Visual Studio Solution File, Format Version 12.00 - # Visual Studio 15 - VisualStudioVersion = 15.0.28010.2019 - MinimumVisualStudioVersion = 10.0.40219.1 - """); + public void Save(string path) + { + using var stream = File.OpenWrite(path); + using var writer = new StreamWriter(stream, Encoding.ASCII); - foreach (var project in _projects) - { - writer.WriteLine( - $""" - Project("{project.ProjectTypeGuid:B}") = "{project.ProjectName}", "{project.RelativeProjectFilePath}", "{project.ProjectGuid:B}" - EndProject - """); - } + writer.WriteLine(); + writer.WriteLine( + """ + Microsoft Visual Studio Solution File, Format Version 12.00 + # Visual Studio 15 + VisualStudioVersion = 15.0.28010.2019 + MinimumVisualStudioVersion = 10.0.40219.1 + """); + foreach (var project in _projects) + { writer.WriteLine( - """ - Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - """); - - foreach (var project in _projects) - { - writer.WriteLine( $""" - {project.ProjectGuid:B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {project.ProjectGuid:B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {project.ProjectGuid:B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {project.ProjectGuid:B}.Release|Any CPU.Build.0 = Release|Any CPU + Project("{project.ProjectTypeGuid:B}") = "{project.ProjectName}", "{project.RelativeProjectFilePath}", "{project.ProjectGuid:B}" + EndProject """); - } + } + + writer.WriteLine( + """ + Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + """); + foreach (var project in _projects) + { writer.WriteLine( - $""" - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {Guid.NewGuid():B} - EndGlobalSection - EndGlobal - """); + $""" + {project.ProjectGuid:B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {project.ProjectGuid:B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {project.ProjectGuid:B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {project.ProjectGuid:B}.Release|Any CPU.Build.0 = Release|Any CPU + """); } - public void Add(Project project) => _projects.Add(project); + writer.WriteLine( + $""" + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {Guid.NewGuid():B} + EndGlobalSection + EndGlobal + """); + } + + public void Add(Project project) => _projects.Add(project); - public void Add(GlobalJson globalJson) => GlobalJson = globalJson; + public void Add(GlobalJson globalJson) => GlobalJson = globalJson; - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - } + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/DependencyNodeIntegrationTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/DependencyNodeIntegrationTests.cs index aa2ae9915b..8d17fbfde7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/DependencyNodeIntegrationTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/DependencyNodeIntegrationTests.cs @@ -3,185 +3,184 @@ using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +// TODO test different SDK version +// TODO review GitHub issues and produce tests for known bugs +// TODO ensure VS shut down after test fixture completes + +[TestClass] +public sealed class DependencyNodeIntegrationTests : ProjectLayoutTestBase { - // TODO test different SDK version - // TODO review GitHub issues and produce tests for known bugs - // TODO ensure VS shut down after test fixture completes + [TestMethod] + public void SingleTarget_NetCoreApp21() + { + CreateProject(new Project("netcoreapp2.1")); - [TestClass] - public sealed class DependencyNodeIntegrationTests : ProjectLayoutTestBase + VerifyDependenciesNode( + new Node("SDK", KnownMonikers.SDK)); + } + + [TestMethod] + public void MultiTarget_NetCoreApp21_Net45() { - [TestMethod] - public void SingleTarget_NetCoreApp21() - { - CreateProject(new Project("netcoreapp2.1")); + CreateProject(new Project("netcoreapp2.1;net45")); - VerifyDependenciesNode( - new Node("SDK", KnownMonikers.SDK)); - } + VerifyDependenciesNode( + new Node("netcoreapp2.1", KnownMonikers.Library) + { + new Node("SDK", KnownMonikers.SDK) + }, + new Node("net45", KnownMonikers.Library) + { + new Node("Assemblies", KnownMonikers.Reference) + }); + } - [TestMethod] - public void MultiTarget_NetCoreApp21_Net45() - { - CreateProject(new Project("netcoreapp2.1;net45")); + [TestMethod] + public void MultiTarget_NetStandard20_Net461() + { + CreateProject(new Project("netstandard2.0;net461")); - VerifyDependenciesNode( - new Node("netcoreapp2.1", KnownMonikers.Library) - { - new Node("SDK", KnownMonikers.SDK) - }, - new Node("net45", KnownMonikers.Library) - { - new Node("Assemblies", KnownMonikers.Reference) - }); - } + VerifyDependenciesNode( + new Node("net461", KnownMonikers.Library) + { + new Node("Assemblies", KnownMonikers.Reference) + }, + new Node("netstandard2.0", KnownMonikers.Library) + { + new Node("SDK", KnownMonikers.SDK) + }); + } - [TestMethod] - public void MultiTarget_NetStandard20_Net461() + [TestMethod] + public void MultiTarget_WithPackageRef() + { + var project = new Project("netstandard2.0;net461") { - CreateProject(new Project("netstandard2.0;net461")); + new PackageReference("MetadataExtractor", "2.1.0") + }; + + CreateProject(project); - VerifyDependenciesNode( - new Node("net461", KnownMonikers.Library) + VerifyDependenciesNode( + new Node("net461", KnownMonikers.Library) + { + new Node("Assemblies", KnownMonikers.Reference), + new Node("Packages", KnownMonikers.NuGetNoColor) { - new Node("Assemblies", KnownMonikers.Reference) - }, - new Node("netstandard2.0", KnownMonikers.Library) + new Node("MetadataExtractor (2.1.0)", KnownMonikers.NuGetNoColor) + } + }, + new Node("netstandard2.0", KnownMonikers.Library) + { + new Node("Packages", KnownMonikers.NuGetNoColor) { - new Node("SDK", KnownMonikers.SDK) - }); - } + new Node("MetadataExtractor (2.1.0)", KnownMonikers.NuGetNoColor) + }, + new Node("SDK", KnownMonikers.SDK) + }); + } - [TestMethod] - public void MultiTarget_WithPackageRef() - { - var project = new Project("netstandard2.0;net461") - { - new PackageReference("MetadataExtractor", "2.1.0") - }; + [TestMethod] + public void ProjectToProjectReferences() + { + var project2 = new Project("netstandard1.6"); - CreateProject(project); + var project1 = new Project("netcoreapp2.1") + { + project2 + }; - VerifyDependenciesNode( - new Node("net461", KnownMonikers.Library) - { - new Node("Assemblies", KnownMonikers.Reference), - new Node("Packages", KnownMonikers.NuGetNoColor) - { - new Node("MetadataExtractor (2.1.0)", KnownMonikers.NuGetNoColor) - } - }, - new Node("netstandard2.0", KnownMonikers.Library) - { - new Node("Packages", KnownMonikers.NuGetNoColor) - { - new Node("MetadataExtractor (2.1.0)", KnownMonikers.NuGetNoColor) - }, - new Node("SDK", KnownMonikers.SDK) - }); - } - - [TestMethod] - public void ProjectToProjectReferences() + var solution = new Solution { - var project2 = new Project("netstandard1.6"); + new GlobalJson(sdkVersion: "2.1.600"), + project1, + project2 + }; - var project1 = new Project("netcoreapp2.1") - { - project2 - }; + CreateSolution(solution); - var solution = new Solution + VerifyDependenciesNode( + project1, + new Node("Projects", KnownMonikers.Application) { - new GlobalJson(sdkVersion: "2.1.600"), - project1, - project2 - }; + new Node(project2.ProjectName, KnownMonikers.Application) + }, + new Node("SDK", KnownMonikers.SDK)); - CreateSolution(solution); + VerifyDependenciesNode( + project2, + new Node("SDK", KnownMonikers.SDK)); + } - VerifyDependenciesNode( - project1, - new Node("Projects", KnownMonikers.Application) - { - new Node(project2.ProjectName, KnownMonikers.Application) - }, - new Node("SDK", KnownMonikers.SDK)); + [TestMethod] + public void AssemblyReferences() + { + var project = new Project("net461") + { + new AssemblyReference("System.Windows.Forms") + }; - VerifyDependenciesNode( - project2, - new Node("SDK", KnownMonikers.SDK)); - } + CreateProject(project); - [TestMethod] - public void AssemblyReferences() - { - var project = new Project("net461") + VerifyDependenciesNode( + project, + new Node("Assemblies", KnownMonikers.Reference) { - new AssemblyReference("System.Windows.Forms") - }; + new Node("System", KnownMonikers.ReferencePrivate), + new Node("System.Core", KnownMonikers.ReferencePrivate), + new Node("System.Data", KnownMonikers.ReferencePrivate), + new Node("System.Drawing", KnownMonikers.ReferencePrivate), + new Node("System.IO.Compression.FileSystem", KnownMonikers.ReferencePrivate), + new Node("System.Numerics", KnownMonikers.ReferencePrivate), + new Node("System.Runtime.Serialization", KnownMonikers.ReferencePrivate), + new Node("System.Windows.Forms", KnownMonikers.Reference), // non private as explicitly added + new Node("System.Xml", KnownMonikers.ReferencePrivate), + new Node("System.Xml.Linq", KnownMonikers.ReferencePrivate) + }); + } - CreateProject(project); + [TestMethod] + [DataRow("net461;netstandard1.3")] + [DataRow("net461")] + public void DteFindsReferences(string targetFrameworks) + { + // NOTE the dependencies node makes only the first TFM visible via DTE. + // For example, netstandard1.3 has 49 references while net461 has the 14 shown here. - VerifyDependenciesNode( - project, - new Node("Assemblies", KnownMonikers.Reference) - { - new Node("System", KnownMonikers.ReferencePrivate), - new Node("System.Core", KnownMonikers.ReferencePrivate), - new Node("System.Data", KnownMonikers.ReferencePrivate), - new Node("System.Drawing", KnownMonikers.ReferencePrivate), - new Node("System.IO.Compression.FileSystem", KnownMonikers.ReferencePrivate), - new Node("System.Numerics", KnownMonikers.ReferencePrivate), - new Node("System.Runtime.Serialization", KnownMonikers.ReferencePrivate), - new Node("System.Windows.Forms", KnownMonikers.Reference), // non private as explicitly added - new Node("System.Xml", KnownMonikers.ReferencePrivate), - new Node("System.Xml.Linq", KnownMonikers.ReferencePrivate) - }); - } - - [TestMethod] - [DataRow("net461;netstandard1.3")] - [DataRow("net461")] - public void DteFindsReferences(string targetFrameworks) + CreateProject(new Project(targetFrameworks) { - // NOTE the dependencies node makes only the first TFM visible via DTE. - // For example, netstandard1.3 has 49 references while net461 has the 14 shown here. - - CreateProject(new Project(targetFrameworks) - { - new PackageReference("MetadataExtractor", "2.1.0"), - new AssemblyReference("System.Windows.Forms"), - new CSharpClass("Class1") - }); + new PackageReference("MetadataExtractor", "2.1.0"), + new AssemblyReference("System.Windows.Forms"), + new CSharpClass("Class1") + }); - var expected = new[] - { - "System.IO.Compression.FileSystem", - "System.Numerics", - "System.Xml.Linq", - "System.Data", - "System.Core", - "System", - "System.Runtime.Serialization", - "System.Drawing", - "System.Xml", - "System.Windows.Forms", - "mscorlib", - "MetadataExtractor", - "Microsoft.CSharp", - "XmpCore" - }; - - var projects = (Array)VisualStudio.Dte.ActiveSolutionProjects; - var vsproject = (VSLangProj.VSProject)projects.Cast().First().Object; - var actual = vsproject.References - .Cast() - .Select(r => r.Name) - .ToList(); - - CollectionAssert.AreEquivalent(expected, actual); - } + var expected = new[] + { + "System.IO.Compression.FileSystem", + "System.Numerics", + "System.Xml.Linq", + "System.Data", + "System.Core", + "System", + "System.Runtime.Serialization", + "System.Drawing", + "System.Xml", + "System.Windows.Forms", + "mscorlib", + "MetadataExtractor", + "Microsoft.CSharp", + "XmpCore" + }; + + var projects = (Array)VisualStudio.Dte.ActiveSolutionProjects; + var vsproject = (VSLangProj.VSProject)projects.Cast().First().Object; + var actual = vsproject.References + .Cast() + .Select(r => r.Name) + .ToList(); + + CollectionAssert.AreEquivalent(expected, actual); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Diagnostics/WarningIgnorerLogger.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Diagnostics/WarningIgnorerLogger.cs index 6ae7a4ccf8..a9369ee313 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Diagnostics/WarningIgnorerLogger.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Diagnostics/WarningIgnorerLogger.cs @@ -6,61 +6,60 @@ using Microsoft.Test.Apex.Services.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio.Diagnostics +namespace Microsoft.VisualStudio.Diagnostics; + +/// +/// Avoids logging "known" warnings that only make investigating real errors harder. +/// +[Export(typeof(ITestLoggerSink))] +[Export(typeof(WarningIgnorerLogger))] +[Export(typeof(TestContextLogger))] +[Export(typeof(IExecutionScopeSink))] +public class WarningIgnorerLogger : TestContextLogger, ITestLoggerSink { - /// - /// Avoids logging "known" warnings that only make investigating real errors harder. - /// - [Export(typeof(ITestLoggerSink))] - [Export(typeof(WarningIgnorerLogger))] - [Export(typeof(TestContextLogger))] - [Export(typeof(IExecutionScopeSink))] - public class WarningIgnorerLogger : TestContextLogger, ITestLoggerSink + // Other than WriteEntry(SinkEntryType, String), everything else is delegated to the base + public WarningIgnorerLogger() { - // Other than WriteEntry(SinkEntryType, String), everything else is delegated to the base - public WarningIgnorerLogger() - { - } - - public new void WriteEntry(SinkEntryType entryType, string message) - { - if (IsKnownLifeTimeActionWarning(entryType, message)) - return; + } - WriteEntry(entryType, LogMessageHelpers.EscapeCurlyBraces(message), string.Empty); - } + public new void WriteEntry(SinkEntryType entryType, string message) + { + if (IsKnownLifeTimeActionWarning(entryType, message)) + return; - internal void SetTestContext(TestContext context) - { - // Set the base TestContext - var property = typeof(TestContextLogger).GetProperty("TestContext", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (property is null) - throw new InvalidOperationException("Unable to find TestContextLogger.TestContext. Has it been renamed?"); + WriteEntry(entryType, LogMessageHelpers.EscapeCurlyBraces(message), string.Empty); + } - property.SetValue(this, context); - } + internal void SetTestContext(TestContext context) + { + // Set the base TestContext + var property = typeof(TestContextLogger).GetProperty("TestContext", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (property is null) + throw new InvalidOperationException("Unable to find TestContextLogger.TestContext. Has it been renamed?"); - private bool IsKnownLifeTimeActionWarning(SinkEntryType entryType, string message) - { - // We have lifetime actions that we have no control over that fail to run due to the lack of - // elevated process, we don't output these warnings to the log as it only confuses investigations. + property.SetValue(this, context); + } - if (entryType == SinkEntryType.Warning) - { - if (message.Contains("Could not find path to the Apex dependency libraries to install 'Microsoft.Test.Apex.ElevateClient'")) - return true; + private bool IsKnownLifeTimeActionWarning(SinkEntryType entryType, string message) + { + // We have lifetime actions that we have no control over that fail to run due to the lack of + // elevated process, we don't output these warnings to the log as it only confuses investigations. - if (message.Contains("CodeMarker Libraries Installation Failure")) - return true; + if (entryType == SinkEntryType.Warning) + { + if (message.Contains("Could not find path to the Apex dependency libraries to install 'Microsoft.Test.Apex.ElevateClient'")) + return true; - if (message.Contains("to 'Microsoft.Internal.Performance.CodeMarkers.dll' to enable CodeMarkers")) - return true; + if (message.Contains("CodeMarker Libraries Installation Failure")) + return true; - if (message.Contains("Unable to locate a file named 'Microsoft.Test.Apex.ElevateClient.exe' within the avaliable probing directories")) - return true; - } + if (message.Contains("to 'Microsoft.Internal.Performance.CodeMarkers.dll' to enable CodeMarkers")) + return true; - return false; + if (message.Contains("Unable to locate a file named 'Microsoft.Test.Apex.ElevateClient.exe' within the avaliable probing directories")) + return true; } + + return false; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/AssemblyLifetime.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/AssemblyLifetime.cs index 4c32e1dbfb..ae2312e76a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/AssemblyLifetime.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/AssemblyLifetime.cs @@ -3,21 +3,20 @@ using Microsoft.VisualStudio.LifetimeActions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +[TestClass] // Just to make sure AssemblyInitialize/Cleanup get called +public static class AssemblyLifetime { - [TestClass] // Just to make sure AssemblyInitialize/Cleanup get called - public static class AssemblyLifetime + [AssemblyInitialize] + public static void OnAssemblyInitialize(TestContext context) { - [AssemblyInitialize] - public static void OnAssemblyInitialize(TestContext context) - { - TestEnvironment.OnAssemblyInitialize(context); - } + TestEnvironment.OnAssemblyInitialize(context); + } - [AssemblyCleanup] - public static void OnAssemblyCleanup() - { - ShutdownVisualStudioAfterLastTestLifetimeAction.OnAssemblyCleanup(); - } + [AssemblyCleanup] + public static void OnAssemblyCleanup() + { + ShutdownVisualStudioAfterLastTestLifetimeAction.OnAssemblyCleanup(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemHostConfiguration.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemHostConfiguration.cs index 9a9f674c78..02731f5893 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemHostConfiguration.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemHostConfiguration.cs @@ -4,30 +4,29 @@ using Microsoft.Test.Apex.Services.Logging.TestLoggerSinks.OmniLog; using Microsoft.Test.Apex.VisualStudio; -namespace Microsoft.VisualStudio -{ - internal sealed class ProjectSystemHostConfiguration : VisualStudioHostConfiguration - { - // This combined with TestBase.IncludeReferencedAssembliesInHostComposition set to false, deliberately limit - // the number of assemblies added to the composition to reduce MEF composition errors in the build log. - internal static readonly string[] CompositionAssemblyPaths = new[] { - typeof(VisualStudioHostConfiguration).Assembly.Location, // Microsoft.Test.Apex.VisualStudio - typeof(HostConfiguration).Assembly.Location, // Microsoft.Test.Apex.Framework - typeof(ProjectSystemHostConfiguration).Assembly.Location, // This assembly - typeof(OmniLogSink).Assembly.Location, // Omni - }; +namespace Microsoft.VisualStudio; - public ProjectSystemHostConfiguration() - { - CommandLineArguments = $"/rootSuffix {TestEnvironment.VisualStudioHive}"; - RestoreUserSettings = false; - InheritProcessEnvironment = true; - AutomaticallyDismissMessageBoxes = true; - DelayInitialVsLicenseValidation = true; - ForceFirstLaunch = false; - BootstrapInjection = BootstrapInjectionMethod.DteFromROT; - } +internal sealed class ProjectSystemHostConfiguration : VisualStudioHostConfiguration +{ + // This combined with TestBase.IncludeReferencedAssembliesInHostComposition set to false, deliberately limit + // the number of assemblies added to the composition to reduce MEF composition errors in the build log. + internal static readonly string[] CompositionAssemblyPaths = new[] { + typeof(VisualStudioHostConfiguration).Assembly.Location, // Microsoft.Test.Apex.VisualStudio + typeof(HostConfiguration).Assembly.Location, // Microsoft.Test.Apex.Framework + typeof(ProjectSystemHostConfiguration).Assembly.Location, // This assembly + typeof(OmniLogSink).Assembly.Location, // Omni + }; - public override IEnumerable CompositionAssemblies => CompositionAssemblyPaths; + public ProjectSystemHostConfiguration() + { + CommandLineArguments = $"/rootSuffix {TestEnvironment.VisualStudioHive}"; + RestoreUserSettings = false; + InheritProcessEnvironment = true; + AutomaticallyDismissMessageBoxes = true; + DelayInitialVsLicenseValidation = true; + ForceFirstLaunch = false; + BootstrapInjection = BootstrapInjectionMethod.DteFromROT; } + + public override IEnumerable CompositionAssemblies => CompositionAssemblyPaths; } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemOperationsConfiguration.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemOperationsConfiguration.cs index 2fddd238b3..7d33de590d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemOperationsConfiguration.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/ProjectSystemOperationsConfiguration.cs @@ -6,44 +6,43 @@ using Microsoft.VisualStudio.Diagnostics; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +// Heavily based on MsTestOperationsConfiguration, which is only needed so that we can control the CompositionAssemblies +// to avoid MEF composition errors being output into the test output and making it harder to understand the build log. +internal sealed class ProjectSystemOperationsConfiguration : OperationsConfiguration { - // Heavily based on MsTestOperationsConfiguration, which is only needed so that we can control the CompositionAssemblies - // to avoid MEF composition errors being output into the test output and making it harder to understand the build log. - internal sealed class ProjectSystemOperationsConfiguration : OperationsConfiguration + internal ProjectSystemOperationsConfiguration(TestContext testContext) { - internal ProjectSystemOperationsConfiguration(TestContext testContext) - { - TestContext = testContext; - } + TestContext = testContext; + } - public override IEnumerable CompositionAssemblies => ProjectSystemHostConfiguration.CompositionAssemblyPaths; + public override IEnumerable CompositionAssemblies => ProjectSystemHostConfiguration.CompositionAssemblyPaths; - public TestContext TestContext { get; } + public TestContext TestContext { get; } - protected override Type Verifier => typeof(IAssertionVerifier); + protected override Type Verifier => typeof(IAssertionVerifier); - protected override Type Logger => typeof(WarningIgnorerLogger); + protected override Type Logger => typeof(WarningIgnorerLogger); - protected override void OnOperationsCreated(Operations operations) - { - base.OnOperationsCreated(operations); + protected override void OnOperationsCreated(Operations operations) + { + base.OnOperationsCreated(operations); - IAssertionVerifier verifier = operations.Get(); - verifier.AssertionDelegate = Assert.Fail; - verifier.FinalFailure += WriteVerificationFailureTree; + IAssertionVerifier verifier = operations.Get(); + verifier.AssertionDelegate = Assert.Fail; + verifier.FinalFailure += WriteVerificationFailureTree; - var logger = operations.Get(); - logger.SetTestContext(TestContext); - } + var logger = operations.Get(); + logger.SetTestContext(TestContext); + } - protected override void OnProbingDirectoriesProviderCreated(IProbingDirectoriesProvider provider) - { - } + protected override void OnProbingDirectoriesProviderCreated(IProbingDirectoriesProvider provider) + { + } - private static void WriteVerificationFailureTree(object sender, FailureEventArgs e) - { - e.Logger.WriteEntry(SinkEntryType.Message, "Full verification failure tree:" + Environment.NewLine + Environment.NewLine + ResultMessageTreeController.Instance.FormatTreeAsText()); - } + private static void WriteVerificationFailureTree(object sender, FailureEventArgs e) + { + e.Logger.WriteEntry(SinkEntryType.Message, "Full verification failure tree:" + Environment.NewLine + Environment.NewLine + ResultMessageTreeController.Instance.FormatTreeAsText()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/TestEnvironment.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/TestEnvironment.cs index 9415efeb86..2d1135998a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/TestEnvironment.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Environment/TestEnvironment.cs @@ -2,41 +2,40 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class TestEnvironment { - internal static class TestEnvironment + /// + /// Gets the Visual Studio hive to run tests under. + /// + public static string? VisualStudioHive { get; private set; } + + public static void OnAssemblyInitialize(TestContext context) { - /// - /// Gets the Visual Studio hive to run tests under. - /// - public static string? VisualStudioHive { get; private set; } + SetTestInstallationDirectoryIfUnset(); - public static void OnAssemblyInitialize(TestContext context) + // Get hive from .runsettings if present (command-line) + string rootSuffix = (string)context.Properties["VsRootSuffix"]; + if (string.IsNullOrEmpty(rootSuffix)) { - SetTestInstallationDirectoryIfUnset(); - - // Get hive from .runsettings if present (command-line) - string rootSuffix = (string)context.Properties["VsRootSuffix"]; - if (string.IsNullOrEmpty(rootSuffix)) - { - // Otherwise, respect the environment, failing that use the default - rootSuffix = Environment.GetEnvironmentVariable("RootSuffix") ?? "Exp"; - } - - VisualStudioHive = rootSuffix; + // Otherwise, respect the environment, failing that use the default + rootSuffix = Environment.GetEnvironmentVariable("RootSuffix") ?? "Exp"; } - private static void SetTestInstallationDirectoryIfUnset() + VisualStudioHive = rootSuffix; + } + + private static void SetTestInstallationDirectoryIfUnset() + { + string installationUnderTest = "VisualStudio.InstallationUnderTest.Path"; + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(installationUnderTest))) { - string installationUnderTest = "VisualStudio.InstallationUnderTest.Path"; - if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(installationUnderTest))) - { - string vsDirectory = Environment.GetEnvironmentVariable("VSAPPIDDIR"); - string devenv = Environment.GetEnvironmentVariable("VSAPPIDNAME"); - if (!string.IsNullOrEmpty(vsDirectory) && !string.IsNullOrEmpty(devenv)) - { // Use the same version we're running inside (Test Explorer) - Environment.SetEnvironmentVariable(installationUnderTest, Path.Combine(vsDirectory, devenv)); - } + string vsDirectory = Environment.GetEnvironmentVariable("VSAPPIDDIR"); + string devenv = Environment.GetEnvironmentVariable("VSAPPIDNAME"); + if (!string.IsNullOrEmpty(vsDirectory) && !string.IsNullOrEmpty(devenv)) + { // Use the same version we're running inside (Test Explorer) + Environment.SetEnvironmentVariable(installationUnderTest, Path.Combine(vsDirectory, devenv)); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/IntegrationTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/IntegrationTestBase.cs index d60a94687f..f35b6cf210 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/IntegrationTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/IntegrationTestBase.cs @@ -3,33 +3,32 @@ using Microsoft.Test.Apex; using Microsoft.Test.Apex.VisualStudio; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public abstract class IntegrationTestBase : VisualStudioHostTest { - public abstract class IntegrationTestBase : VisualStudioHostTest + protected IntegrationTestBase() { - protected IntegrationTestBase() - { - // TestCleanup will fire up another instance of Visual Studio to reset - // the AutoloadExternalChanges if it thinks the default changed even if - // that was just caused by settings to be sync'd. Just turn this feature off. - SuppressReloadPrompt = false; - } + // TestCleanup will fire up another instance of Visual Studio to reset + // the AutoloadExternalChanges if it thinks the default changed even if + // that was just caused by settings to be sync'd. Just turn this feature off. + SuppressReloadPrompt = false; + } - protected override bool IncludeReferencedAssembliesInHostComposition => false; // Do not add things we reference to the MEF Container + protected override bool IncludeReferencedAssembliesInHostComposition => false; // Do not add things we reference to the MEF Container - protected override VisualStudioHostConfiguration GetHostConfiguration() - { - return new ProjectSystemHostConfiguration(); - } + protected override VisualStudioHostConfiguration GetHostConfiguration() + { + return new ProjectSystemHostConfiguration(); + } - protected override OperationsConfiguration GetOperationsConfiguration() - { - return new ProjectSystemOperationsConfiguration(TestContext); - } + protected override OperationsConfiguration GetOperationsConfiguration() + { + return new ProjectSystemOperationsConfiguration(TestContext); + } - public new void TryShutdownVisualStudioInstance() - { - base.TryShutdownVisualStudioInstance(); - } + public new void TryShutdownVisualStudioInstance() + { + base.TryShutdownVisualStudioInstance(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/MakeOmniLogTestSpecificLifetimeAction.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/MakeOmniLogTestSpecificLifetimeAction.cs index 4a07841344..539f56b506 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/MakeOmniLogTestSpecificLifetimeAction.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/MakeOmniLogTestSpecificLifetimeAction.cs @@ -4,21 +4,20 @@ using Microsoft.Test.Apex.Services; using Omni.Logging; -namespace Microsoft.VisualStudio.LifetimeActions +namespace Microsoft.VisualStudio.LifetimeActions; + +/// +/// Responsible for making the OmniLog test-specific and prevent incremental logging. +/// +[ProvidesOperationsExtension] +[Export(typeof(ITestLifetimeAction))] +public class MakeOmniLogTestSpecificLifetimeAction : ITestLifetimeAction { - /// - /// Responsible for making the OmniLog test-specific and prevent incremental logging. - /// - [ProvidesOperationsExtension] - [Export(typeof(ITestLifetimeAction))] - public class MakeOmniLogTestSpecificLifetimeAction : ITestLifetimeAction + public void OnTestLifeTimeAction(ApexTest testClass, Type classType, TestLifeTimeAction action) { - public void OnTestLifeTimeAction(ApexTest testClass, Type classType, TestLifeTimeAction action) + if (action == TestLifeTimeAction.PreTestInitialize) { - if (action == TestLifeTimeAction.PreTestInitialize) - { - Log.ResetLog($"{testClass.GetType().Name}.{testClass.TestContext.TestName}"); - } + Log.ResetLog($"{testClass.GetType().Name}.{testClass.TestContext.TestName}"); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/ShutdownVisualStudioAfterLastTestLifetimeAction.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/ShutdownVisualStudioAfterLastTestLifetimeAction.cs index 6bf1cf4913..f8743c922c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/ShutdownVisualStudioAfterLastTestLifetimeAction.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/LifetimeActions/ShutdownVisualStudioAfterLastTestLifetimeAction.cs @@ -4,31 +4,30 @@ using Microsoft.Test.Apex.Services; using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.LifetimeActions +namespace Microsoft.VisualStudio.LifetimeActions; + +/// +/// Responsible for shutting down Visual Studio after last test is done with it. +/// +[ProvidesOperationsExtension] +[Export(typeof(ITestLifetimeAction))] +internal sealed class ShutdownVisualStudioAfterLastTestLifetimeAction : ITestLifetimeAction { - /// - /// Responsible for shutting down Visual Studio after last test is done with it. - /// - [ProvidesOperationsExtension] - [Export(typeof(ITestLifetimeAction))] - internal sealed class ShutdownVisualStudioAfterLastTestLifetimeAction : ITestLifetimeAction - { - private static IntegrationTestBase? _lastTest; + private static IntegrationTestBase? _lastTest; - public void OnTestLifeTimeAction(ApexTest testClass, Type classType, TestLifeTimeAction action) + public void OnTestLifeTimeAction(ApexTest testClass, Type classType, TestLifeTimeAction action) + { + if (action == TestLifeTimeAction.PostTestCleanup) { - if (action == TestLifeTimeAction.PostTestCleanup) - { - _lastTest = testClass as IntegrationTestBase; - } + _lastTest = testClass as IntegrationTestBase; } + } - public static void OnAssemblyCleanup() - { - // To reduce integration test time, we want to reuse Visual Studio instances where possible. - // Apex will automatically close VS only if the previous test failed, this shuts down Visual Studio - // after all the tests have finished. - _lastTest?.TryShutdownVisualStudioInstance(); - } + public static void OnAssemblyCleanup() + { + // To reduce integration test time, we want to reuse Visual Studio instances where possible. + // Apex will automatically close VS only if the previous test failed, this shuts down Visual Studio + // after all the tests have finished. + _lastTest?.TryShutdownVisualStudioInstance(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/OpenProjectTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/OpenProjectTests.cs index a3c7926d94..4049fc3bd1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/OpenProjectTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/OpenProjectTests.cs @@ -3,64 +3,63 @@ using Microsoft.Test.Apex.VisualStudio.Solution; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[TestClass] +public sealed class CreateProjectTests : IntegrationTestBase { - [TestClass] - public sealed class CreateProjectTests : IntegrationTestBase + [TestMethod] + public void CreateProject_CreateAndBuild() { - [TestMethod] - public void CreateProject_CreateAndBuild() - { - var solution = VisualStudio.ObjectModel.Solution; + var solution = VisualStudio.ObjectModel.Solution; - ProjectTestExtension consoleProject; + ProjectTestExtension consoleProject; - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); + } - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + using (Scope.Enter("Verify Create Project")) + { + solution.Verify.HasProject(); + } - using (Scope.Enter("Wait for IntelliSense")) - { - solution.WaitForIntellisenseStage(); - } + using (Scope.Enter("Wait for IntelliSense")) + { + solution.WaitForIntellisenseStage(); + } - using (Scope.Enter("Verify dependency nodes")) - { - var dependencies = solution.SolutionExplorer.FindItemRecursive("Dependencies", expandToFind: true); - dependencies.Select(); - dependencies.ExpandAll(); - Assert.AreEqual("Dependencies", dependencies.Name); - var frameworks = dependencies.Items.FirstOrDefault(); - Assert.IsNotNull(frameworks); - Assert.AreEqual("Frameworks", frameworks.Name); - } + using (Scope.Enter("Verify dependency nodes")) + { + var dependencies = solution.SolutionExplorer.FindItemRecursive("Dependencies", expandToFind: true); + dependencies.Select(); + dependencies.ExpandAll(); + Assert.AreEqual("Dependencies", dependencies.Name); + var frameworks = dependencies.Items.FirstOrDefault(); + Assert.IsNotNull(frameworks); + Assert.AreEqual("Frameworks", frameworks.Name); + } - using (Scope.Enter("Build Project")) - { - solution.BuildManager.Build(); - solution.BuildManager.WaitForBuildFinished(); - var success = solution.BuildManager.Verify.HasFinished(); - Assert.IsTrue(success, $"project '{consoleProject.FileName}' failed to finish building."); - } + using (Scope.Enter("Build Project")) + { + solution.BuildManager.Build(); + solution.BuildManager.WaitForBuildFinished(); + var success = solution.BuildManager.Verify.HasFinished(); + Assert.IsTrue(success, $"project '{consoleProject.FileName}' failed to finish building."); + } - using (Scope.Enter("Verify Build Succeeded")) - { - var success = - solution.BuildManager.Verify.ProjectBuilt(consoleProject) && - solution.BuildManager.Verify.Succeeded(); + using (Scope.Enter("Verify Build Succeeded")) + { + var success = + solution.BuildManager.Verify.ProjectBuilt(consoleProject) && + solution.BuildManager.Verify.Succeeded(); - if (!success) - { - VisualStudio.ObjectModel.Shell.ToolWindows.ErrorList.WaitForErrorListItems(); - var errors = VisualStudio.ObjectModel.Shell.ToolWindows.ErrorList.Errors.Select(x => $"Description:'{x.Description}' Project:{x.ProjectName} Line:'{x.LineNumber}'").ToArray(); - Assert.Fail($"project '{consoleProject.FileName}' failed to build.{Environment.NewLine}errors:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}"); - } + if (!success) + { + VisualStudio.ObjectModel.Shell.ToolWindows.ErrorList.WaitForErrorListItems(); + var errors = VisualStudio.ObjectModel.Shell.ToolWindows.ErrorList.Errors.Select(x => $"Description:'{x.Description}' Project:{x.ProjectName} Line:'{x.LineNumber}'").ToArray(); + Assert.Fail($"project '{consoleProject.FileName}' failed to build.{Environment.NewLine}errors:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}"); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectFileEditorTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectFileEditorTests.cs index b50fbf06e8..a006ab7f28 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectFileEditorTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectFileEditorTests.cs @@ -5,251 +5,250 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +[TestClass] +public sealed class ProjectFileEditorTests : IntegrationTestBase { - [TestClass] - public sealed class ProjectFileEditorTests : IntegrationTestBase + [TestMethod] + public void ProjectFileEditor_DoubleClick_OpensEditor() { - [TestMethod] - public void ProjectFileEditor_DoubleClick_OpensEditor() + var solution = VisualStudio.ObjectModel.Solution; + + ProjectTestExtension consoleProject; + + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); + } + + using (Scope.Enter("Verify Create Project")) { - var solution = VisualStudio.ObjectModel.Solution; + solution.Verify.HasProject(); + } - ProjectTestExtension consoleProject; + using (Scope.Enter("Open project file editor")) + { + consoleProject.DoubleClick(); + } - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } + using (Scope.Enter("Verify Editor")) + { + var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; + editor.WaitForFullyLoaded(); + editor.Verify.FilePathIs(consoleProject.FullPath); + } + } - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + [TestMethod] + public void ProjectFileEditor_EditsMade_TabMarkedDirty() + { + var solution = VisualStudio.ObjectModel.Solution; - using (Scope.Enter("Open project file editor")) - { - consoleProject.DoubleClick(); - } + ProjectTestExtension consoleProject; - using (Scope.Enter("Verify Editor")) - { - var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; - editor.WaitForFullyLoaded(); - editor.Verify.FilePathIs(consoleProject.FullPath); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); } - [TestMethod] - public void ProjectFileEditor_EditsMade_TabMarkedDirty() + using (Scope.Enter("Verify Create Project")) { - var solution = VisualStudio.ObjectModel.Solution; + solution.Verify.HasProject(); + } - ProjectTestExtension consoleProject; + using (Scope.Enter("Open project file editor")) + { + consoleProject.DoubleClick(); + } - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } + using (Scope.Enter("Verify Editor")) + { + var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; + editor.WaitForFullyLoaded(); + editor.Editor.Caret.MoveDown(3); + editor.Editor.Edit.InsertLineBelow(2); + editor.Verify.IsDirty(); + } + } - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + [TestMethod] + public void ProjectFileEditor_ProjectPropertyChanged_TabOpensDirty() + { + var solution = VisualStudio.ObjectModel.Solution; - using (Scope.Enter("Open project file editor")) - { - consoleProject.DoubleClick(); - } + ProjectTestExtension consoleProject; - using (Scope.Enter("Verify Editor")) - { - var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; - editor.WaitForFullyLoaded(); - editor.Editor.Caret.MoveDown(3); - editor.Editor.Edit.InsertLineBelow(2); - editor.Verify.IsDirty(); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); } - [TestMethod] - public void ProjectFileEditor_ProjectPropertyChanged_TabOpensDirty() + using (Scope.Enter("Verify Create Project")) { - var solution = VisualStudio.ObjectModel.Solution; + solution.Verify.HasProject(); + } - ProjectTestExtension consoleProject; + using (Scope.Enter("Change project property")) + { + consoleProject.Properties.SetValue(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary); + } - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } + using (Scope.Enter("Open project file editor")) + { + consoleProject.DoubleClick(); + } - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + using (Scope.Enter("Verify Editor")) + { + var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; + editor.WaitForFullyLoaded(); + editor.Verify.IsDirty(); + } + } - using (Scope.Enter("Change project property")) - { - consoleProject.Properties.SetValue(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary); - } + [TestMethod] + public void ProjectFileEditor_ProjectPropertyChanged_EditorShowsProjectChanges() + { + var solution = VisualStudio.ObjectModel.Solution; - using (Scope.Enter("Open project file editor")) - { - consoleProject.DoubleClick(); - } + ProjectTestExtension consoleProject; - using (Scope.Enter("Verify Editor")) - { - var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; - editor.WaitForFullyLoaded(); - editor.Verify.IsDirty(); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); } - [TestMethod] - public void ProjectFileEditor_ProjectPropertyChanged_EditorShowsProjectChanges() + using (Scope.Enter("Verify Create Project")) { - var solution = VisualStudio.ObjectModel.Solution; + solution.Verify.HasProject(); + } - ProjectTestExtension consoleProject; + using (Scope.Enter("Change project property")) + { + consoleProject.Properties.SetValue(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary); + } - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } - - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + using (Scope.Enter("Open project file editor")) + { + consoleProject.DoubleClick(); + } - using (Scope.Enter("Change project property")) - { - consoleProject.Properties.SetValue(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary); - } + using (Scope.Enter("Verify Text Buffer Changed")) + { + var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; + editor.WaitForFullyLoaded(); + editor.Editor.Verify.ContainsText("Library"); + } + } - using (Scope.Enter("Open project file editor")) - { - consoleProject.DoubleClick(); - } + [TestMethod] + public void ProjectFileEditor_ReplaceAllWhileEditorLoaded_ReplacementsNotLoaded() + { + var solution = VisualStudio.ObjectModel.Solution; + + ProjectTestExtension consoleProject; - using (Scope.Enter("Verify Text Buffer Changed")) - { - var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; - editor.WaitForFullyLoaded(); - editor.Editor.Verify.ContainsText("Library"); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); } - [TestMethod] - public void ProjectFileEditor_ReplaceAllWhileEditorLoaded_ReplacementsNotLoaded() + using (Scope.Enter("Verify Create Project")) { - var solution = VisualStudio.ObjectModel.Solution; + solution.Verify.HasProject(); + } - ProjectTestExtension consoleProject; - - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } - - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } - - using (Scope.Enter("Open project file editor")) - { - consoleProject.DoubleClick(); - } + using (Scope.Enter("Open project file editor")) + { + consoleProject.DoubleClick(); + } - using (Scope.Enter("Change project property")) - { - VisualStudio.ObjectModel.Search.FindReplace.ReplaceAllInFiles("Exe", "Library", LookInFilesSearchScopes.EntireSolution); - } + using (Scope.Enter("Change project property")) + { + VisualStudio.ObjectModel.Search.FindReplace.ReplaceAllInFiles("Exe", "Library", LookInFilesSearchScopes.EntireSolution); + } - using (Scope.Enter("Verify Text Buffer Changed")) - { - var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; - editor.Editor.Verify.ContainsText("Library"); - } + using (Scope.Enter("Verify Text Buffer Changed")) + { + var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; + editor.Editor.Verify.ContainsText("Library"); + } - using (Scope.Enter("Verify Project Unchanged")) - { - consoleProject.Properties.Verify.PropertyValueIs(ProjectProperty.OutputType, prjOutputType.prjOutputTypeExe); - } - } - - [TestMethod] - public void ProjectFileEditor_ReplaceAllWhileEditorNotLoaded_ReplacementsLoaded() + using (Scope.Enter("Verify Project Unchanged")) { - var solution = VisualStudio.ObjectModel.Solution; - - ProjectTestExtension consoleProject; + consoleProject.Properties.Verify.PropertyValueIs(ProjectProperty.OutputType, prjOutputType.prjOutputTypeExe); + } + } - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } + [TestMethod] + public void ProjectFileEditor_ReplaceAllWhileEditorNotLoaded_ReplacementsLoaded() + { + var solution = VisualStudio.ObjectModel.Solution; - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + ProjectTestExtension consoleProject; - using (Scope.Enter("Change project property")) - { - VisualStudio.ObjectModel.Search.FindReplace.ReplaceAllInFiles("Exe", "Library", LookInFilesSearchScopes.EntireSolution, keepModifiedFilesOpen: KeepModifiedFilesOpen.No).WaitForReplaceInFilesCompletion(); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); + } - using (Scope.Enter("Open project file editor")) - { - consoleProject.DoubleClick(); - } + using (Scope.Enter("Verify Create Project")) + { + solution.Verify.HasProject(); + } - using (Scope.Enter("Verify Text Buffer Changed")) - { - var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; - editor.Editor.Verify.ContainsText("Library"); - } - - using (Scope.Enter("Verify Project Changed")) - { - // project reload is on a one second delay - Services.Synchronization.WaitFor(TimeSpan.FromSeconds(1), () => consoleProject.Properties.Verify.PropertyValueIs(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary)); - } - } - - [TestMethod] - [Ignore("The new find results pain is not exposed via Apex correctly so this test ends up timing out, though you can visually inspect the results while you're waiting if you like")] - public void ProjectFileEditor_FindAllAfterPropertyChange_FindsProjectFile() - { - var solution = VisualStudio.ObjectModel.Solution; - - ProjectTestExtension consoleProject; + using (Scope.Enter("Change project property")) + { + VisualStudio.ObjectModel.Search.FindReplace.ReplaceAllInFiles("Exe", "Library", LookInFilesSearchScopes.EntireSolution, keepModifiedFilesOpen: KeepModifiedFilesOpen.No).WaitForReplaceInFilesCompletion(); + } - using (Scope.Enter("Create Project")) - { - consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); - } + using (Scope.Enter("Open project file editor")) + { + consoleProject.DoubleClick(); + } + + using (Scope.Enter("Verify Text Buffer Changed")) + { + var editor = VisualStudio.ObjectModel.WindowManager.ActiveDocumentWindowAsTextEditor; + editor.Editor.Verify.ContainsText("Library"); + } + + using (Scope.Enter("Verify Project Changed")) + { + // project reload is on a one second delay + Services.Synchronization.WaitFor(TimeSpan.FromSeconds(1), () => consoleProject.Properties.Verify.PropertyValueIs(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary)); + } + } - using (Scope.Enter("Verify Create Project")) - { - solution.Verify.HasProject(); - } + [TestMethod] + [Ignore("The new find results pain is not exposed via Apex correctly so this test ends up timing out, though you can visually inspect the results while you're waiting if you like")] + public void ProjectFileEditor_FindAllAfterPropertyChange_FindsProjectFile() + { + var solution = VisualStudio.ObjectModel.Solution; - using (Scope.Enter("Change project property")) - { - consoleProject.Properties.SetValue(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary); - } + ProjectTestExtension consoleProject; - using (Scope.Enter("Find changed property")) - { - var find = VisualStudio.ObjectModel.Search.FindReplace.FindAll("Library", LookInFilesSearchScopes.EntireSolution); - find.WaitForReplaceInFilesCompletion(); - Assert.AreEqual(find.ReplacedItemCount, 1); - } + using (Scope.Enter("Create Project")) + { + consoleProject = solution.CreateProject(ProjectLanguage.CSharp, ProjectTemplate.NetCoreConsoleApp); + } + + using (Scope.Enter("Verify Create Project")) + { + solution.Verify.HasProject(); + } + + using (Scope.Enter("Change project property")) + { + consoleProject.Properties.SetValue(ProjectProperty.OutputType, prjOutputType.prjOutputTypeLibrary); + } + + using (Scope.Enter("Find changed property")) + { + var find = VisualStudio.ObjectModel.Search.FindReplace.FindAll("Library", LookInFilesSearchScopes.EntireSolution); + find.WaitForReplaceInFilesCompletion(); + Assert.AreEqual(find.ReplacedItemCount, 1); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectLayoutTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectLayoutTestBase.cs index 6b02ca4943..13df4e3ffb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectLayoutTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/ProjectLayoutTestBase.cs @@ -9,305 +9,304 @@ using Microsoft.VisualStudio.ProjectSystem.Imaging; using Microsoft.VisualStudio.Imaging; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +/// +/// Base class for integration tests that start with a specific solution/project layout on disk. +/// +public abstract class ProjectLayoutTestBase : IntegrationTestBase { /// - /// Base class for integration tests that start with a specific solution/project layout on disk. + /// Paths of temporary workspaces to delete when the test fixture completes. + /// Each path is a root, so should be deleted recursively. /// - public abstract class ProjectLayoutTestBase : IntegrationTestBase - { - /// - /// Paths of temporary workspaces to delete when the test fixture completes. - /// Each path is a root, so should be deleted recursively. - /// - private ImmutableList _rootPaths = ImmutableList.Empty; + private ImmutableList _rootPaths = ImmutableList.Empty; - /// - /// Verifies that the "Dependencies" node of has a structure that - /// matches . - /// - /// The project whose Dependencies node should be inspected. - /// The expected structure for the Dependencies node. - protected void VerifyDependenciesNode(Project project, params Node[] nodes) + /// + /// Verifies that the "Dependencies" node of has a structure that + /// matches . + /// + /// The project whose Dependencies node should be inspected. + /// The expected structure for the Dependencies node. + protected void VerifyDependenciesNode(Project project, params Node[] nodes) + { + using (Scope.Enter("Verify dependency nodes")) { - using (Scope.Enter("Verify dependency nodes")) - { - var item = VisualStudio.ObjectModel.Solution.SolutionExplorer.FindItemRecursive(project.ProjectName, expandToFind: true); + var item = VisualStudio.ObjectModel.Solution.SolutionExplorer.FindItemRecursive(project.ProjectName, expandToFind: true); - item.Expand(); + item.Expand(); - var actualDependencies = item.Items.FirstOrDefault(i => i.Name == "Dependencies"); + var actualDependencies = item.Items.FirstOrDefault(i => i.Name == "Dependencies"); - VerifyDependenciesNode(actualDependencies, nodes); - } + VerifyDependenciesNode(actualDependencies, nodes); } + } - /// - /// Verifies that the solution's first "Dependencies" node has a structure that - /// matches . - /// - /// The expected structure for the Dependencies node. - protected void VerifyDependenciesNode(params Node[] nodes) + /// + /// Verifies that the solution's first "Dependencies" node has a structure that + /// matches . + /// + /// The expected structure for the Dependencies node. + protected void VerifyDependenciesNode(params Node[] nodes) + { + using (Scope.Enter("Verify dependency nodes")) { - using (Scope.Enter("Verify dependency nodes")) - { - SolutionExplorerItemTestExtension actualDependencies = VisualStudio.ObjectModel.Solution.SolutionExplorer.FindItemRecursive("Dependencies", expandToFind: true); + SolutionExplorerItemTestExtension actualDependencies = VisualStudio.ObjectModel.Solution.SolutionExplorer.FindItemRecursive("Dependencies", expandToFind: true); - VerifyDependenciesNode(actualDependencies, nodes); - } + VerifyDependenciesNode(actualDependencies, nodes); } + } - private static void VerifyDependenciesNode(SolutionExplorerItemTestExtension actualDependencies, Node[] nodes) + private static void VerifyDependenciesNode(SolutionExplorerItemTestExtension actualDependencies, Node[] nodes) + { + var expectDependencies = new Node("Dependencies", KnownMonikers.ReferenceGroup) { - var expectDependencies = new Node("Dependencies", KnownMonikers.ReferenceGroup) - { - Children = new List(nodes) - }; + Children = new List(nodes) + }; - var expectOutput = new StringBuilder(); - var actualOutput = new StringBuilder(); + var expectOutput = new StringBuilder(); + var actualOutput = new StringBuilder(); - var same = true; + var same = true; - VerifyNode(expectDependencies, actualDependencies); + VerifyNode(expectDependencies, actualDependencies); - if (!same) - { - Assert.Fail($"Incorrect Dependencies tree.\n\nExpected:\n\n{expectOutput}\nActual:\n\n{actualOutput}"); - } + if (!same) + { + Assert.Fail($"Incorrect Dependencies tree.\n\nExpected:\n\n{expectOutput}\nActual:\n\n{actualOutput}"); + } - return; + return; - void VerifyNode(Node? expect, SolutionExplorerItemTestExtension? actual, int depth = 0) - { - Assert.IsTrue(expect is not null || actual is not null); + void VerifyNode(Node? expect, SolutionExplorerItemTestExtension? actual, int depth = 0) + { + Assert.IsTrue(expect is not null || actual is not null); - var thisSame = true; + var thisSame = true; - if (actual is not null && expect?.Text is not null && expect.Text != actual.Name) - { - same = false; - thisSame = false; - } + if (actual is not null && expect?.Text is not null && expect.Text != actual.Name) + { + same = false; + thisSame = false; + } - if (actual is not null && expect?.Icon != null && !AssertExtensions.AreEqual(expect.Icon.Value, actual.ExpandedIconMoniker)) - { - same = false; - thisSame = false; - } + if (actual is not null && expect?.Icon != null && !AssertExtensions.AreEqual(expect.Icon.Value, actual.ExpandedIconMoniker)) + { + same = false; + thisSame = false; + } - var actualIcon = actual?.ExpandedIconMoniker == null - ? "null" - : ImageMonikerDebuggerDisplay.FromImageMoniker(actual.ExpandedIconMoniker.Value.ToImageMoniker()); + var actualIcon = actual?.ExpandedIconMoniker == null + ? "null" + : ImageMonikerDebuggerDisplay.FromImageMoniker(actual.ExpandedIconMoniker.Value.ToImageMoniker()); - if (expect is not null) - { - expectOutput - .Append(' ', depth * 4) - .Append(expect.Text ?? actual!.Name) - .Append(' ') - .AppendLine(expect.Icon != null - ? ImageMonikerDebuggerDisplay.FromImageMoniker(expect.Icon.Value) - : actualIcon); - } + if (expect is not null) + { + expectOutput + .Append(' ', depth * 4) + .Append(expect.Text ?? actual!.Name) + .Append(' ') + .AppendLine(expect.Icon != null + ? ImageMonikerDebuggerDisplay.FromImageMoniker(expect.Icon.Value) + : actualIcon); + } - if (actual is not null) - { - actualOutput - .Append(' ', depth * 4) - .Append(actual.Name) - .Append(' ') - .Append(actualIcon) - .AppendLine(thisSame ? "" : " 🐛"); - } + if (actual is not null) + { + actualOutput + .Append(' ', depth * 4) + .Append(actual.Name) + .Append(' ') + .Append(actualIcon) + .AppendLine(thisSame ? "" : " 🐛"); + } - if (expect?.Children is not null) + if (expect?.Children is not null) + { + if (actual?.IsExpanded == false && expect.Children is not null && expect.Children.Count != 0) { - if (actual?.IsExpanded == false && expect.Children is not null && expect.Children.Count != 0) - { - actual.Expand(); - } + actual.Expand(); + } - var actualChildren = actual?.Items.ToList() ?? new List(); + var actualChildren = actual?.Items.ToList() ?? new List(); - if (actualChildren.Count != expect.Children!.Count) - same = false; + if (actualChildren.Count != expect.Children!.Count) + same = false; - var max = Math.Max(actualChildren.Count, expect.Children.Count); + var max = Math.Max(actualChildren.Count, expect.Children.Count); - for (int i = 0; i < max; i++) - { - var expectChild = expect.Children.Count > i ? expect.Children[i] : null; - var actualChild = actualChildren.Count > i ? actualChildren[i] : null; + for (int i = 0; i < max; i++) + { + var expectChild = expect.Children.Count > i ? expect.Children[i] : null; + var actualChild = actualChildren.Count > i ? actualChildren[i] : null; - VerifyNode( - expectChild, - actualChild, - depth + 1); - } + VerifyNode( + expectChild, + actualChild, + depth + 1); } } } + } - protected override bool TryShutdownHostInstance() - { - bool result = base.TryShutdownHostInstance(); + protected override bool TryShutdownHostInstance() + { + bool result = base.TryShutdownHostInstance(); - foreach (var projectPath in _rootPaths) + foreach (var projectPath in _rootPaths) + { + try { - try - { - Directory.Delete(projectPath, recursive: true); - } - catch - { - continue; - } - - ImmutableInterlocked.Update(ref _rootPaths, (list, item) => list.Remove(item), projectPath); + Directory.Delete(projectPath, recursive: true); } - - return result; - } - - private string CreateRootPath() - { - string rootPath; - - do + catch { - var name = "IntegrationTest_" + Guid.NewGuid().ToString("N").Substring(0, 12); - rootPath = Path.Combine(Path.GetTempPath(), name); + continue; } - while (Directory.Exists(rootPath)); - Directory.CreateDirectory(rootPath); + ImmutableInterlocked.Update(ref _rootPaths, (list, item) => list.Remove(item), projectPath); + } - ImmutableInterlocked.Update(ref _rootPaths, (list, path) => list.Add(path), rootPath); + return result; + } - return rootPath; - } + private string CreateRootPath() + { + string rootPath; - /// - /// Creates on disk and opens it, returning its test extension object. - /// - /// - protected ProjectTestExtension CreateProject(Project project) + do { - var rootPath = CreateRootPath(); + var name = "IntegrationTest_" + Guid.NewGuid().ToString("N").Substring(0, 12); + rootPath = Path.Combine(Path.GetTempPath(), name); + } + while (Directory.Exists(rootPath)); - project.Save(rootPath); + Directory.CreateDirectory(rootPath); - ProjectTestExtension projectExtension; + ImmutableInterlocked.Update(ref _rootPaths, (list, path) => list.Add(path), rootPath); - using (Scope.Enter("Open Project")) - { - projectExtension = VisualStudio.ObjectModel.Solution.OpenProject(Path.Combine(rootPath, project.RelativeProjectFilePath)); - } + return rootPath; + } - using (Scope.Enter("Verify Create Project")) - { - VisualStudio.ObjectModel.Solution.Verify.HasProject(projectExtension); - } + /// + /// Creates on disk and opens it, returning its test extension object. + /// + /// + protected ProjectTestExtension CreateProject(Project project) + { + var rootPath = CreateRootPath(); + + project.Save(rootPath); - WaitForIntellisense(); - WaitForDependenciesNodeSync(); + ProjectTestExtension projectExtension; - return projectExtension; + using (Scope.Enter("Open Project")) + { + projectExtension = VisualStudio.ObjectModel.Solution.OpenProject(Path.Combine(rootPath, project.RelativeProjectFilePath)); } - protected void CreateSolution(Solution solution) + using (Scope.Enter("Verify Create Project")) { - var rootPath = CreateRootPath(); - var solutionFilePath = Path.Combine(rootPath, "Solution.sln"); + VisualStudio.ObjectModel.Solution.Verify.HasProject(projectExtension); + } - solution.Save(solutionFilePath); + WaitForIntellisense(); + WaitForDependenciesNodeSync(); - solution.GlobalJson?.Save(rootPath); + return projectExtension; + } - foreach (var project in solution.Projects) - { - project.Save(rootPath); - } + protected void CreateSolution(Solution solution) + { + var rootPath = CreateRootPath(); + var solutionFilePath = Path.Combine(rootPath, "Solution.sln"); - using (Scope.Enter("Open Solution")) - { - VisualStudio.ObjectModel.Solution.Open(solutionFilePath); - } + solution.Save(solutionFilePath); - using (Scope.Enter("Verify Create Project")) - { - foreach (var project in solution.Projects) - { - project.Extension = VisualStudio.ObjectModel.Solution.GetProjectExtension(project.ProjectName); - } - } + solution.GlobalJson?.Save(rootPath); - WaitForIntellisense(); - WaitForDependenciesNodeSync(); + foreach (var project in solution.Projects) + { + project.Save(rootPath); } - protected void WaitForIntellisense() + using (Scope.Enter("Open Solution")) { - using (Scope.Enter("Wait for Intellisense")) - { - VisualStudio.ObjectModel.Solution.WaitForIntellisenseStage(); - } + VisualStudio.ObjectModel.Solution.Open(solutionFilePath); } - protected void WaitForDependenciesNodeSync() + using (Scope.Enter("Verify Create Project")) { - using (Scope.Enter("Wait for dependencies node to sync")) + foreach (var project in solution.Projects) { - // Wait for dataflow to update the nodes - // TODO create a more reliable (and usually faster) way of doing this - // https://github.com/dotnet/project-system/issues/3426 - Thread.Sleep(5 * 1000); + project.Extension = VisualStudio.ObjectModel.Solution.GetProjectExtension(project.ProjectName); } } - /// - /// Models the expected state of a node in the "Dependencies" node tree. - /// - /// - /// This type is designed to work with object and collection initializers. - /// For example: - /// - /// new Node(".NETCoreApp 2.1", KnownMonikers.Library) - /// { - /// new Node("SDK", KnownMonikers.SDK) - /// } - /// - /// - protected sealed class Node : IEnumerable + WaitForIntellisense(); + WaitForDependenciesNodeSync(); + } + + protected void WaitForIntellisense() + { + using (Scope.Enter("Wait for Intellisense")) { - public List? Children { get; set; } + VisualStudio.ObjectModel.Solution.WaitForIntellisenseStage(); + } + } - /// - /// Gets and sets the expected icon for this node. - /// A value will disable validation. - /// - public string? Text { get; set; } + protected void WaitForDependenciesNodeSync() + { + using (Scope.Enter("Wait for dependencies node to sync")) + { + // Wait for dataflow to update the nodes + // TODO create a more reliable (and usually faster) way of doing this + // https://github.com/dotnet/project-system/issues/3426 + Thread.Sleep(5 * 1000); + } + } - /// - /// Gets and sets the expected icon for this node. - /// A value will disable validation. - /// - public ImageMoniker? Icon { get; set; } + /// + /// Models the expected state of a node in the "Dependencies" node tree. + /// + /// + /// This type is designed to work with object and collection initializers. + /// For example: + /// + /// new Node(".NETCoreApp 2.1", KnownMonikers.Library) + /// { + /// new Node("SDK", KnownMonikers.SDK) + /// } + /// + /// + protected sealed class Node : IEnumerable + { + public List? Children { get; set; } - public Node(string? text = null, ImageMoniker? icon = null) - { - Text = text; - Icon = icon; - } + /// + /// Gets and sets the expected icon for this node. + /// A value will disable validation. + /// + public string? Text { get; set; } - public void Add(Node child) - { - Children ??= new List(); - Children.Add(child); - } + /// + /// Gets and sets the expected icon for this node. + /// A value will disable validation. + /// + public ImageMoniker? Icon { get; set; } - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + public Node(string? text = null, ImageMoniker? icon = null) + { + Text = text; + Icon = icon; } + + public void Add(Node child) + { + Children ??= new List(); + Children.Add(child); + } + + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Utilities/AssertExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Utilities/AssertExtensions.cs index 7c7a2a439c..e4c3062640 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Utilities/AssertExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.IntegrationTests/Utilities/AssertExtensions.cs @@ -5,45 +5,44 @@ using Microsoft.VisualStudio.ProjectSystem.Imaging; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class AssertExtensions { - internal static class AssertExtensions + public static void AreEqual(this Assert assert, ImageMoniker expected, ExportableImageMoniker? actual) { - public static void AreEqual(this Assert assert, ImageMoniker expected, ExportableImageMoniker? actual) + if (actual == null) { - if (actual == null) - { - throw new AssertFailedException($"ImageMoniker did not match.{Environment.NewLine}Expected: {S(expected)}{Environment.NewLine}Actual: null"); - } - - var actualMoniker = ToImageMoniker(actual.Value); + throw new AssertFailedException($"ImageMoniker did not match.{Environment.NewLine}Expected: {S(expected)}{Environment.NewLine}Actual: null"); + } - if (expected.Id != actualMoniker.Id || expected.Guid != actualMoniker.Guid) - { - throw new AssertFailedException($"ImageMoniker did not match.{Environment.NewLine}Expected: {S(expected)}{Environment.NewLine}Actual: {S(actualMoniker)}"); - } + var actualMoniker = ToImageMoniker(actual.Value); - static string S(ImageMoniker a) => ImageMonikerDebuggerDisplay.FromImageMoniker(a); + if (expected.Id != actualMoniker.Id || expected.Guid != actualMoniker.Guid) + { + throw new AssertFailedException($"ImageMoniker did not match.{Environment.NewLine}Expected: {S(expected)}{Environment.NewLine}Actual: {S(actualMoniker)}"); } - public static ImageMoniker ToImageMoniker(this ExportableImageMoniker actual) + static string S(ImageMoniker a) => ImageMonikerDebuggerDisplay.FromImageMoniker(a); + } + + public static ImageMoniker ToImageMoniker(this ExportableImageMoniker actual) + { + return new ImageMoniker { Id = actual.Id, Guid = actual.Guid }; + } + + public static bool AreEqual(ImageMoniker expected, ExportableImageMoniker? actual) + { + if (actual == null) { - return new ImageMoniker { Id = actual.Id, Guid = actual.Guid }; + return false; } - public static bool AreEqual(ImageMoniker expected, ExportableImageMoniker? actual) + if (expected.Id != actual.Value.Id || expected.Guid != actual.Value.Guid) { - if (actual == null) - { - return false; - } - - if (expected.Id != actual.Value.Id || expected.Guid != actual.Value.Guid) - { - return false; - } - - return true; + return false; } + + return true; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices.UnitTests/ProjectSystem/ProjectTreeParserTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices.UnitTests/ProjectSystem/ProjectTreeParserTests.cs index 6f8b05b65a..9cffb5fd14 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices.UnitTests/ProjectSystem/ProjectTreeParserTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices.UnitTests/ProjectSystem/ProjectTreeParserTests.cs @@ -1,444 +1,443 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class ProjectTreeParserTests { - public class ProjectTreeParserTests + [Fact] + public void Constructor_NullAsValue_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsValue_ThrowsArgumentNull() - { - Assert.Throws("value", () => - { - new ProjectTreeParser(null!); - }); - } - - [Fact] - public void Constructor_EmptyAsValue_ThrowsArgument() - { - Assert.Throws("value", () => - { - new ProjectTreeParser(""); - }); - } - - [Theory] - [InlineData(@" ")] - [InlineData(@" ")] - [InlineData(@" ")] - [InlineData(@" ")] - public void Parse_IdExpected_EncounteredOnlyWhiteSpace_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.IdExpected_EncounteredOnlyWhiteSpace); - } - - [Theory] - [InlineData(@"(")] - [InlineData(@",")] - [InlineData(@"Root, ")] - [InlineData(@"Root, ")] - [InlineData(@"Root ( ")] - [InlineData(@"Root (visibility: ")] - [InlineData(@"Root (visibility: visible, ")] - [InlineData(@"Root (visibility: visible, ")] - [InlineData(@"Root (flags: { ")] - [InlineData(@"Root (flags: { ")] - [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F }")] - public void Parse_IdExpected_EncounteredDelimiter_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.IdExpected_EncounteredDelimiter); - } - - [Theory] - [InlineData(@"Root, ")] - [InlineData(@"Root (")] - [InlineData(@"Root (visibility: ")] - [InlineData(@"Root (visibility: visible, ")] - [InlineData(@"Root (flags: {")] - [InlineData(@"Root, Icon: {")] - public void Parse_IdExpected_EncounteredEndOfString_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.IdExpected_EncounteredEndOfString); - } - - [Theory] - [InlineData(@"Root, FilePath:")] - [InlineData(@"Root, FilePath: ""C:\Temp"",")] - [InlineData(@"Root (),")] - [InlineData(@"Root (visibility")] - [InlineData(@"Root (visibility:")] - [InlineData(@"Root (flags")] - [InlineData(@"Root (flags:")] - [InlineData(@"Root (flags: ")] - [InlineData(@"Root (flags: {}")] - public void Parse_DelimiterExpected_EncounteredEndOfString_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.DelimiterExpected_EncounteredEndOfString); - } - - [Theory] - [InlineData(@"Root, FilePath ")] - [InlineData(@"Root,FilePath: """"")] - [InlineData(@"Root, FilePath:""""")] - [InlineData(@"Root, FilePath:,")] - [InlineData(@"Root (),,")] - [InlineData(@"Root (visibility ")] - public void Parse_DelimiterExpected_ThrowsFormat(string input) + Assert.Throws("value", () => { - AssertThrows(input, ProjectTreeFormatError.DelimiterExpected); - } - - [Theory] - [InlineData(@"Root, FilePath,")] - [InlineData(@"Root (Foo:")] - [InlineData(@"Root (Visibility:")] - [InlineData(@"Root (Capabilities: ")] - [InlineData(@"Root (Foo")] - [InlineData(@"Root (Visibility")] - [InlineData(@"Root (Capabilities ")] - [InlineData(@"Root (), File")] - [InlineData(@"Root (), Filepath")] - [InlineData(@"Root (), filepath")] - [InlineData(@"Root (), filepath:")] - [InlineData(@"Root (), icon:")] - [InlineData(@"Root (), expandedIcon:")] - [InlineData(@"Root (), Unrecognized:")] - [InlineData(@"Root (), Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, unrecognized:")] - public void Parse_UnrecognizedPropertyName_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.UnrecognizedPropertyName); - } - - [Theory] - [InlineData(@"Root (visibility: Visible")] - [InlineData(@"Root (visibility: Invisible")] - [InlineData(@"Root (visibility: VISIBLE")] - [InlineData(@"Root (visibility: INVISIBLE")] - [InlineData(@"Root (visibility: v")] - [InlineData(@"Root (visibility: i")] - public void Parse_UnrecognizedPropertyValue_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.UnrecognizedPropertyValue); - } + new ProjectTreeParser(null!); + }); + } - [Theory] - [InlineData(@"Root, FilePath: ""C:\Temp""""")] - [InlineData(@"Root, FilePath: ""C:\Temp""A")] - public void Parse_EndOfStringExpected_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.EndOfStringExpected); - } - - [Theory] - [InlineData(@"Root, Icon: {1}")] - [InlineData(@"Root, Icon: {A}")] - [InlineData(@"Root, Icon: {{407FAC73-908A-4477-8176-A3128544AE4F}")] - [InlineData(@"Root, ExpandedIcon: {1}")] - [InlineData(@"Root, ExpandedIcon: {A}")] - [InlineData(@"Root, ExpandedIcon: {{407FAC73-908A-4477-8176-A3128544AE4F}")] - [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {1}")] - [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {A}")] - [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {{407FAC73-908A-4477-8176-A3128544AE4F}")] - public void Parse_GuidExpected_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.GuidExpected); - } - - [Theory] - [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F A}")] - [InlineData(@"Root, ExpandedIcon: {407FAC73-908A-4477-8176-A3128544AE4F A}")] - [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {407FAC73-908A-4477-8176-A3128544AE4F A}")] - public void Parse_IntegerExpected_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.IntegerExpected); - } - - // Input // Expected - [Theory] - [InlineData(@"R", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""" )] - [InlineData(@"Ro", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Root", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project:Root", @"Project:Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project Root", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"This is the project root", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"R, FilePath: """"", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Ro, FilePath: """"", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Root, FilePath: """"", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project Root, FilePath: """"", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"This is the project root, FilePath: """"", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - public void Parse_RootWithNoProperties_CanParse(string input, string expected) - { - AssertProjectTree(input, expected); - } - - // Input // Expected - [Theory] - [InlineData(@"R()", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Ro()", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Root()", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project Root()", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"This is the project root()", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"R ()", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Ro ()", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Root ()", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project Root ()", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"This is the project root ()", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"R(), FilePath: """"", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Ro(), FilePath: """"", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Root(), FilePath: """"", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project Root(), FilePath: """"", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"This is the project root(), FilePath: """"", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"R (), FilePath: """"", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Ro (), FilePath: """"", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Root (), FilePath: """"", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"Project Root (), FilePath: """"", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - [InlineData(@"This is the project root (), FilePath: """"", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] - - public void Parse_RootWithEmptyProperties_CanParse(string input, string expected) - { - AssertProjectTree(input, expected); - } - - // Input // Expected - [Theory] - [InlineData(@"R(visibility: visible)", @"R[caption] (visibility: visible)")] - [InlineData(@"Ro(visibility: visible)", @"Ro[caption] (visibility: visible)")] - [InlineData(@"Root(visibility: visible)", @"Root[caption] (visibility: visible)")] - [InlineData(@"Project Root(visibility: visible)", @"Project Root[caption] (visibility: visible)")] - [InlineData(@"This is the project root(visibility: visible)", @"This is the project root[caption] (visibility: visible)")] - [InlineData(@"R (visibility: visible)", @"R[caption] (visibility: visible)")] - [InlineData(@"Ro (visibility: visible)", @"Ro[caption] (visibility: visible)")] - [InlineData(@"Root (visibility: visible)", @"Root[caption] (visibility: visible)")] - [InlineData(@"Project Root (visibility: visible)", @"Project Root[caption] (visibility: visible)")] - [InlineData(@"This is the project root (visibility: visible)", @"This is the project root[caption] (visibility: visible)")] - [InlineData(@"R(visibility: invisible)", @"R[caption] (visibility: invisible)")] - [InlineData(@"Ro(visibility: invisible)", @"Ro[caption] (visibility: invisible)")] - [InlineData(@"Root(visibility: invisible)", @"Root[caption] (visibility: invisible)")] - [InlineData(@"Project Root(visibility: invisible)", @"Project Root[caption] (visibility: invisible)")] - [InlineData(@"This is the project root(visibility: invisible)", @"This is the project root[caption] (visibility: invisible)")] - [InlineData(@"R (visibility: invisible)", @"R[caption] (visibility: invisible)")] - [InlineData(@"Ro (visibility: invisible)", @"Ro[caption] (visibility: invisible)")] - [InlineData(@"Root (visibility: invisible)", @"Root[caption] (visibility: invisible)")] - [InlineData(@"Project Root (visibility: invisible)", @"Project Root[caption] (visibility: invisible)")] - [InlineData(@"This is the project root (visibility: invisible)", @"This is the project root[caption] (visibility: invisible)")] - public void Parse_RootWithVisibility_CanParse(string input, string expected) - { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.Visibility); - } - - // Input // Expected - [Theory] - [InlineData(@"Project Root (flags: {})", @"Project Root[caption] (flags: {})")] - [InlineData(@"Project Root (flags: {A})", @"Project Root[caption] (flags: {A[capability]})")] - [InlineData(@"Project Root (flags: {A B})", @"Project Root[caption] (flags: {A[capability] B[capability]})")] - [InlineData(@"Project Root (flags: {A B C})", @"Project Root[caption] (flags: {A[capability] B[capability] C[capability]})")] - [InlineData(@"Project Root (flags: {Folder})", @"Project Root[caption] (flags: {Folder[capability]})")] - [InlineData(@"Project Root (flags: {Folder IncludeInProjectCandidate})", @"Project Root[caption] (flags: {Folder[capability] IncludeInProjectCandidate[capability]})")] - [InlineData(@"Project Root (flags: {AppDesigner Folder IncludeInProjectCandidate})", @"Project Root[caption] (flags: {AppDesigner[capability] Folder[capability] IncludeInProjectCandidate[capability]})")] - [InlineData(@"Project Root (flags: {App:Designer})", @"Project Root[caption] (flags: {App:Designer[capability]})")] - [InlineData(@"Project Root (visibility: visible, flags: {App:Designer})", @"Project Root[caption] (flags: {App:Designer[capability]})")] - [InlineData(@"Project Root (flags: {App:Designer}, visibility: visible)", @"Project Root[caption] (flags: {App:Designer[capability]})")] - public void Parse_RootWithCapabilities_CanParse(string input, string expected) - { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.Flags); - } - - // Input // Expected - [Theory] - [InlineData(@"Project Root (), FilePath: """"", @"Project Root[caption], FilePath: ""[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""C""", @"Project Root[caption], FilePath: ""C[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""C:""", @"Project Root[caption], FilePath: ""C:[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""C:\""", @"Project Root[caption], FilePath: ""C:\[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""C:\Project""", @"Project Root[caption], FilePath: ""C:\Project[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""C:\Project Root""", @"Project Root[caption], FilePath: ""C:\Project Root[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""Project Root""", @"Project Root[caption], FilePath: ""Project Root[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""Project Root.csproj""", @"Project Root[caption], FilePath: ""Project Root.csproj[filepath]""")] - [InlineData(@"Project Root (), FilePath: ""Folder\Project Root.csproj""", @"Project Root[caption], FilePath: ""Folder\Project Root.csproj[filepath]""")] - public void Parse_RootWithFilePath_CanParse(string input, string expected) - { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.FilePath); - } - - // Input // Expected - [Theory] - [InlineData(@"Project Root, ItemType: Compile", @"Project Root[caption], ItemType: Compile[itemtype]")] - [InlineData(@"Project Root, ItemType: None", @"Project Root[caption], ItemType: None[itemtype]")] - public void Parse_RootWithItemType_CanParse(string input, string expected) - { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.ItemType); - } - - // Input // Expected - [Theory] - [InlineData(@"Project Root, SubType: Designer", @"Project Root[caption], SubType: Designer[subtype]")] - [InlineData(@"Project Root, SubType: Form", @"Project Root[caption], SubType: Form[subtype]")] - public void Parse_RootWithSubType_CanParse(string input, string expected) - { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.SubType); - } - - // Input // Expected - [Theory] - [InlineData(@"Project Root (), Icon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), Icon: {}, ExpandedIcon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), ExpandedIcon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), ExpandedIcon: {}, Icon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 0}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 0[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 1}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 1[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 2}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 2[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10[icon]}, ExpandedIcon: {[icon]}")] - [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10}, ExpandedIcon: {41F80260-959C-4556-852C-F7D1B31DD201 0}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10[icon]}, ExpandedIcon: {41F80260-959C-4556-852C-F7D1B31DD201 0[icon]}")] - [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10}, ExpandedIcon: {F88CB78E-D07D-4131-8E53-B601CEB30517 0}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10[icon]}, ExpandedIcon: {F88CB78E-D07D-4131-8E53-B601CEB30517 0[icon]}")] - public void Parse_RootWithIcons_CanParse(string input, string expected) + [Fact] + public void Constructor_EmptyAsValue_ThrowsArgument() + { + Assert.Throws("value", () => { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.Icons); - } + new ProjectTreeParser(""); + }); + } - [Theory] - [InlineData( - """ - Root - Parent - """, - """ - Root[caption] - [indent]Parent[caption] - """)] - [InlineData( - """ - Root - Parent1 - Parent2 - """, - """ - Root[caption] - [indent]Parent1[caption] - [indent]Parent2[caption] - """)] - [InlineData( - """ - Root - Parent1 - Child - Parent2 - """, - """ - Root[caption] - [indent]Parent1[caption] - [indent][indent]Child[caption] - [indent]Parent2[caption] - """)] - [InlineData( - """ - Root - Parent1 - Child1 - Child2 - Parent2 - Parent3 - Child3 - Child4 - Grandchild - Parent4 - """, - """ - Root[caption] - [indent]Parent1[caption] - [indent][indent]Child1[caption] - [indent][indent]Child2[caption] - [indent]Parent2[caption] - [indent]Parent3[caption] - [indent][indent]Child3[caption] - [indent][indent]Child4[caption] - [indent][indent][indent]Grandchild[caption] - [indent]Parent4[caption] - """)] - public void Parse_RootWithChildren_CanParse(string input, string expected) - { - AssertProjectTree(input, expected, ProjectTreeWriterOptions.None); - } - - [Theory] - [InlineData( - """ - Root - Root - """)] - [InlineData( - """ - Root - Parent - Root - """)] - [InlineData( - """ - Root - Parent - Child - Root - """)] - [InlineData( - """ - Root - Parent - Child - Child - Root - """)] - public void Parse_MultipleRoots_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.MultipleRoots); - } + [Theory] + [InlineData(@" ")] + [InlineData(@" ")] + [InlineData(@" ")] + [InlineData(@" ")] + public void Parse_IdExpected_EncounteredOnlyWhiteSpace_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.IdExpected_EncounteredOnlyWhiteSpace); + } + + [Theory] + [InlineData(@"(")] + [InlineData(@",")] + [InlineData(@"Root, ")] + [InlineData(@"Root, ")] + [InlineData(@"Root ( ")] + [InlineData(@"Root (visibility: ")] + [InlineData(@"Root (visibility: visible, ")] + [InlineData(@"Root (visibility: visible, ")] + [InlineData(@"Root (flags: { ")] + [InlineData(@"Root (flags: { ")] + [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F }")] + public void Parse_IdExpected_EncounteredDelimiter_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.IdExpected_EncounteredDelimiter); + } + + [Theory] + [InlineData(@"Root, ")] + [InlineData(@"Root (")] + [InlineData(@"Root (visibility: ")] + [InlineData(@"Root (visibility: visible, ")] + [InlineData(@"Root (flags: {")] + [InlineData(@"Root, Icon: {")] + public void Parse_IdExpected_EncounteredEndOfString_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.IdExpected_EncounteredEndOfString); + } + + [Theory] + [InlineData(@"Root, FilePath:")] + [InlineData(@"Root, FilePath: ""C:\Temp"",")] + [InlineData(@"Root (),")] + [InlineData(@"Root (visibility")] + [InlineData(@"Root (visibility:")] + [InlineData(@"Root (flags")] + [InlineData(@"Root (flags:")] + [InlineData(@"Root (flags: ")] + [InlineData(@"Root (flags: {}")] + public void Parse_DelimiterExpected_EncounteredEndOfString_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.DelimiterExpected_EncounteredEndOfString); + } + + [Theory] + [InlineData(@"Root, FilePath ")] + [InlineData(@"Root,FilePath: """"")] + [InlineData(@"Root, FilePath:""""")] + [InlineData(@"Root, FilePath:,")] + [InlineData(@"Root (),,")] + [InlineData(@"Root (visibility ")] + public void Parse_DelimiterExpected_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.DelimiterExpected); + } + + [Theory] + [InlineData(@"Root, FilePath,")] + [InlineData(@"Root (Foo:")] + [InlineData(@"Root (Visibility:")] + [InlineData(@"Root (Capabilities: ")] + [InlineData(@"Root (Foo")] + [InlineData(@"Root (Visibility")] + [InlineData(@"Root (Capabilities ")] + [InlineData(@"Root (), File")] + [InlineData(@"Root (), Filepath")] + [InlineData(@"Root (), filepath")] + [InlineData(@"Root (), filepath:")] + [InlineData(@"Root (), icon:")] + [InlineData(@"Root (), expandedIcon:")] + [InlineData(@"Root (), Unrecognized:")] + [InlineData(@"Root (), Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, unrecognized:")] + public void Parse_UnrecognizedPropertyName_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.UnrecognizedPropertyName); + } + + [Theory] + [InlineData(@"Root (visibility: Visible")] + [InlineData(@"Root (visibility: Invisible")] + [InlineData(@"Root (visibility: VISIBLE")] + [InlineData(@"Root (visibility: INVISIBLE")] + [InlineData(@"Root (visibility: v")] + [InlineData(@"Root (visibility: i")] + public void Parse_UnrecognizedPropertyValue_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.UnrecognizedPropertyValue); + } + + [Theory] + [InlineData(@"Root, FilePath: ""C:\Temp""""")] + [InlineData(@"Root, FilePath: ""C:\Temp""A")] + public void Parse_EndOfStringExpected_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.EndOfStringExpected); + } - [Theory] - [InlineData( - """ - Root + [Theory] + [InlineData(@"Root, Icon: {1}")] + [InlineData(@"Root, Icon: {A}")] + [InlineData(@"Root, Icon: {{407FAC73-908A-4477-8176-A3128544AE4F}")] + [InlineData(@"Root, ExpandedIcon: {1}")] + [InlineData(@"Root, ExpandedIcon: {A}")] + [InlineData(@"Root, ExpandedIcon: {{407FAC73-908A-4477-8176-A3128544AE4F}")] + [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {1}")] + [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {A}")] + [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {{407FAC73-908A-4477-8176-A3128544AE4F}")] + public void Parse_GuidExpected_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.GuidExpected); + } + + [Theory] + [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F A}")] + [InlineData(@"Root, ExpandedIcon: {407FAC73-908A-4477-8176-A3128544AE4F A}")] + [InlineData(@"Root, Icon: {407FAC73-908A-4477-8176-A3128544AE4F 1}, ExpandedIcon: {407FAC73-908A-4477-8176-A3128544AE4F A}")] + public void Parse_IntegerExpected_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.IntegerExpected); + } + + // Input // Expected + [Theory] + [InlineData(@"R", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""" )] + [InlineData(@"Ro", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Root", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project:Root", @"Project:Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project Root", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"This is the project root", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"R, FilePath: """"", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Ro, FilePath: """"", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Root, FilePath: """"", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project Root, FilePath: """"", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"This is the project root, FilePath: """"", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + public void Parse_RootWithNoProperties_CanParse(string input, string expected) + { + AssertProjectTree(input, expected); + } + + // Input // Expected + [Theory] + [InlineData(@"R()", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Ro()", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Root()", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project Root()", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"This is the project root()", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"R ()", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Ro ()", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Root ()", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project Root ()", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"This is the project root ()", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"R(), FilePath: """"", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Ro(), FilePath: """"", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Root(), FilePath: """"", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project Root(), FilePath: """"", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"This is the project root(), FilePath: """"", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"R (), FilePath: """"", @"R[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Ro (), FilePath: """"", @"Ro[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Root (), FilePath: """"", @"Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"Project Root (), FilePath: """"", @"Project Root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + [InlineData(@"This is the project root (), FilePath: """"", @"This is the project root[caption] (visibility: visible, flags: {}), FilePath: ""[filepath]""")] + + public void Parse_RootWithEmptyProperties_CanParse(string input, string expected) + { + AssertProjectTree(input, expected); + } + + // Input // Expected + [Theory] + [InlineData(@"R(visibility: visible)", @"R[caption] (visibility: visible)")] + [InlineData(@"Ro(visibility: visible)", @"Ro[caption] (visibility: visible)")] + [InlineData(@"Root(visibility: visible)", @"Root[caption] (visibility: visible)")] + [InlineData(@"Project Root(visibility: visible)", @"Project Root[caption] (visibility: visible)")] + [InlineData(@"This is the project root(visibility: visible)", @"This is the project root[caption] (visibility: visible)")] + [InlineData(@"R (visibility: visible)", @"R[caption] (visibility: visible)")] + [InlineData(@"Ro (visibility: visible)", @"Ro[caption] (visibility: visible)")] + [InlineData(@"Root (visibility: visible)", @"Root[caption] (visibility: visible)")] + [InlineData(@"Project Root (visibility: visible)", @"Project Root[caption] (visibility: visible)")] + [InlineData(@"This is the project root (visibility: visible)", @"This is the project root[caption] (visibility: visible)")] + [InlineData(@"R(visibility: invisible)", @"R[caption] (visibility: invisible)")] + [InlineData(@"Ro(visibility: invisible)", @"Ro[caption] (visibility: invisible)")] + [InlineData(@"Root(visibility: invisible)", @"Root[caption] (visibility: invisible)")] + [InlineData(@"Project Root(visibility: invisible)", @"Project Root[caption] (visibility: invisible)")] + [InlineData(@"This is the project root(visibility: invisible)", @"This is the project root[caption] (visibility: invisible)")] + [InlineData(@"R (visibility: invisible)", @"R[caption] (visibility: invisible)")] + [InlineData(@"Ro (visibility: invisible)", @"Ro[caption] (visibility: invisible)")] + [InlineData(@"Root (visibility: invisible)", @"Root[caption] (visibility: invisible)")] + [InlineData(@"Project Root (visibility: invisible)", @"Project Root[caption] (visibility: invisible)")] + [InlineData(@"This is the project root (visibility: invisible)", @"This is the project root[caption] (visibility: invisible)")] + public void Parse_RootWithVisibility_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.Visibility); + } + + // Input // Expected + [Theory] + [InlineData(@"Project Root (flags: {})", @"Project Root[caption] (flags: {})")] + [InlineData(@"Project Root (flags: {A})", @"Project Root[caption] (flags: {A[capability]})")] + [InlineData(@"Project Root (flags: {A B})", @"Project Root[caption] (flags: {A[capability] B[capability]})")] + [InlineData(@"Project Root (flags: {A B C})", @"Project Root[caption] (flags: {A[capability] B[capability] C[capability]})")] + [InlineData(@"Project Root (flags: {Folder})", @"Project Root[caption] (flags: {Folder[capability]})")] + [InlineData(@"Project Root (flags: {Folder IncludeInProjectCandidate})", @"Project Root[caption] (flags: {Folder[capability] IncludeInProjectCandidate[capability]})")] + [InlineData(@"Project Root (flags: {AppDesigner Folder IncludeInProjectCandidate})", @"Project Root[caption] (flags: {AppDesigner[capability] Folder[capability] IncludeInProjectCandidate[capability]})")] + [InlineData(@"Project Root (flags: {App:Designer})", @"Project Root[caption] (flags: {App:Designer[capability]})")] + [InlineData(@"Project Root (visibility: visible, flags: {App:Designer})", @"Project Root[caption] (flags: {App:Designer[capability]})")] + [InlineData(@"Project Root (flags: {App:Designer}, visibility: visible)", @"Project Root[caption] (flags: {App:Designer[capability]})")] + public void Parse_RootWithCapabilities_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.Flags); + } + + // Input // Expected + [Theory] + [InlineData(@"Project Root (), FilePath: """"", @"Project Root[caption], FilePath: ""[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""C""", @"Project Root[caption], FilePath: ""C[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""C:""", @"Project Root[caption], FilePath: ""C:[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""C:\""", @"Project Root[caption], FilePath: ""C:\[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""C:\Project""", @"Project Root[caption], FilePath: ""C:\Project[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""C:\Project Root""", @"Project Root[caption], FilePath: ""C:\Project Root[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""Project Root""", @"Project Root[caption], FilePath: ""Project Root[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""Project Root.csproj""", @"Project Root[caption], FilePath: ""Project Root.csproj[filepath]""")] + [InlineData(@"Project Root (), FilePath: ""Folder\Project Root.csproj""", @"Project Root[caption], FilePath: ""Folder\Project Root.csproj[filepath]""")] + public void Parse_RootWithFilePath_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.FilePath); + } + + // Input // Expected + [Theory] + [InlineData(@"Project Root, ItemType: Compile", @"Project Root[caption], ItemType: Compile[itemtype]")] + [InlineData(@"Project Root, ItemType: None", @"Project Root[caption], ItemType: None[itemtype]")] + public void Parse_RootWithItemType_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.ItemType); + } + + // Input // Expected + [Theory] + [InlineData(@"Project Root, SubType: Designer", @"Project Root[caption], SubType: Designer[subtype]")] + [InlineData(@"Project Root, SubType: Form", @"Project Root[caption], SubType: Form[subtype]")] + public void Parse_RootWithSubType_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.SubType); + } + + // Input // Expected + [Theory] + [InlineData(@"Project Root (), Icon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), Icon: {}, ExpandedIcon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), ExpandedIcon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), ExpandedIcon: {}, Icon: {}", @"Project Root[caption], Icon: {[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 0}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 0[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 1}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 1[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 2}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 2[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10[icon]}, ExpandedIcon: {[icon]}")] + [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10}, ExpandedIcon: {41F80260-959C-4556-852C-F7D1B31DD201 0}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10[icon]}, ExpandedIcon: {41F80260-959C-4556-852C-F7D1B31DD201 0[icon]}")] + [InlineData(@"Project Root (), Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10}, ExpandedIcon: {F88CB78E-D07D-4131-8E53-B601CEB30517 0}", @"Project Root[caption], Icon: {41F80260-959C-4556-852C-F7D1B31DD201 10[icon]}, ExpandedIcon: {F88CB78E-D07D-4131-8E53-B601CEB30517 0[icon]}")] + public void Parse_RootWithIcons_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.Icons); + } + + [Theory] + [InlineData( + """ + Root + Parent + """, + """ + Root[caption] + [indent]Parent[caption] + """)] + [InlineData( + """ + Root + Parent1 + Parent2 + """, + """ + Root[caption] + [indent]Parent1[caption] + [indent]Parent2[caption] + """)] + [InlineData( + """ + Root + Parent1 + Child + Parent2 + """, + """ + Root[caption] + [indent]Parent1[caption] + [indent][indent]Child[caption] + [indent]Parent2[caption] + """)] + [InlineData( + """ + Root + Parent1 + Child1 + Child2 + Parent2 + Parent3 + Child3 + Child4 + Grandchild + Parent4 + """, + """ + Root[caption] + [indent]Parent1[caption] + [indent][indent]Child1[caption] + [indent][indent]Child2[caption] + [indent]Parent2[caption] + [indent]Parent3[caption] + [indent][indent]Child3[caption] + [indent][indent]Child4[caption] + [indent][indent][indent]Grandchild[caption] + [indent]Parent4[caption] + """)] + public void Parse_RootWithChildren_CanParse(string input, string expected) + { + AssertProjectTree(input, expected, ProjectTreeWriterOptions.None); + } + + [Theory] + [InlineData( + """ + Root + Root + """)] + [InlineData( + """ + Root + Parent + Root + """)] + [InlineData( + """ + Root + Parent + Child + Root + """)] + [InlineData( + """ + Root + Parent + Child + Child + Root + """)] + public void Parse_MultipleRoots_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.MultipleRoots); + } + + [Theory] + [InlineData( + """ + Root + Parent + """)] + [InlineData( + """ + Root + Parent Parent - """)] - [InlineData( - """ - Root - Parent - Parent - """)] - [InlineData( - """ - Root - Parent - """)] - public void Parse_IndentTooManyLevels_ThrowsFormat(string input) - { - AssertThrows(input, ProjectTreeFormatError.IndentTooManyLevels); - } + """)] + [InlineData( + """ + Root + Parent + """)] + public void Parse_IndentTooManyLevels_ThrowsFormat(string input) + { + AssertThrows(input, ProjectTreeFormatError.IndentTooManyLevels); + } - private static void AssertProjectTree(string input, string expected, ProjectTreeWriterOptions options = ProjectTreeWriterOptions.Flags | ProjectTreeWriterOptions.FilePath | ProjectTreeWriterOptions.Visibility) - { - // Remove the newlines from the start and end of input and expected so that - // it makes it easier inside the test to layout the repro. - input = input.Trim(new[] { '\n', '\r' }); - expected = expected.Trim(new[] { '\n', '\r' }); + private static void AssertProjectTree(string input, string expected, ProjectTreeWriterOptions options = ProjectTreeWriterOptions.Flags | ProjectTreeWriterOptions.FilePath | ProjectTreeWriterOptions.Visibility) + { + // Remove the newlines from the start and end of input and expected so that + // it makes it easier inside the test to layout the repro. + input = input.Trim(new[] { '\n', '\r' }); + expected = expected.Trim(new[] { '\n', '\r' }); - var parser = new ProjectTreeParser(input); - var writer = new ProjectTreeWriter(parser.Parse(), options | ProjectTreeWriterOptions.Tags); + var parser = new ProjectTreeParser(input); + var writer = new ProjectTreeWriter(parser.Parse(), options | ProjectTreeWriterOptions.Tags); - string result = writer.WriteToString(); + string result = writer.WriteToString(); - Assert.Equal(expected, result, ignoreLineEndingDifferences: true); - } + Assert.Equal(expected, result, ignoreLineEndingDifferences: true); + } - private static void AssertThrows(string input, ProjectTreeFormatError error) - { - input = input.Trim(new[] { '\n', '\r' }); + private static void AssertThrows(string input, ProjectTreeFormatError error) + { + input = input.Trim(new[] { '\n', '\r' }); - var parser = new ProjectTreeParser(input); + var parser = new ProjectTreeParser(input); - var exception = Assert.Throws(() => - { - parser.Parse(); - }); + var exception = Assert.Throws(() => + { + parser.Parse(); + }); - Assert.Equal(error, exception.ErrorId); - } + Assert.Equal(error, exception.ErrorId); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/AssertEx.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/AssertEx.cs index 32d1dc8994..8f23232aa4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/AssertEx.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/AssertEx.cs @@ -3,47 +3,46 @@ using System.Collections; using Xunit.Sdk; -namespace Xunit +namespace Xunit; + +internal static class AssertEx { - internal static class AssertEx + public static void CollectionLength(IEnumerable collection, int expectedCount) { - public static void CollectionLength(IEnumerable collection, int expectedCount) - { - int actualCount = collection.Count(); - - if (actualCount != expectedCount) - { - throw new CollectionException(collection, expectedCount, actualCount); - } - } + int actualCount = collection.Count(); - public static void CollectionLength(IEnumerable collection, int expectedCount) + if (actualCount != expectedCount) { - CollectionLength(collection.Cast(), expectedCount); + throw new CollectionException(collection, expectedCount, actualCount); } + } + + public static void CollectionLength(IEnumerable collection, int expectedCount) + { + CollectionLength(collection.Cast(), expectedCount); + } + + public static void SequenceSame(IEnumerable expected, IEnumerable actual) where T : class + { + using IEnumerator expectedEnumerator = expected.GetEnumerator(); + using IEnumerator actualEnumerator = actual.GetEnumerator(); - public static void SequenceSame(IEnumerable expected, IEnumerable actual) where T : class + while (true) { - using IEnumerator expectedEnumerator = expected.GetEnumerator(); - using IEnumerator actualEnumerator = actual.GetEnumerator(); + bool nextExpected = expectedEnumerator.MoveNext(); + bool nextActual = actualEnumerator.MoveNext(); - while (true) + if (nextExpected && nextActual) + { + Assert.Same(expectedEnumerator.Current, actualEnumerator.Current); + } + else if (!nextExpected && !nextActual) + { + return; + } + else { - bool nextExpected = expectedEnumerator.MoveNext(); - bool nextActual = actualEnumerator.MoveNext(); - - if (nextExpected && nextActual) - { - Assert.Same(expectedEnumerator.Current, actualEnumerator.Current); - } - else if (!nextExpected && !nextActual) - { - return; - } - else - { - throw new XunitException("Sequences have different lengths"); - } + throw new XunitException("Sequences have different lengths"); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/MSBuildAssert.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/MSBuildAssert.cs index f712d0bd5d..c3ceca5c88 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/MSBuildAssert.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/MSBuildAssert.cs @@ -3,36 +3,35 @@ using System.Text; using Microsoft.Build.Construction; -namespace Microsoft.Build +namespace Microsoft.Build; + +public static class MSBuildAssert { - public static class MSBuildAssert + public static void AssertProjectXml(string expected, ProjectRootElement actual) + { + AssertProjectXml(ProjectRootElementFactory.Create(expected), actual); + } + + public static void AssertProjectXml(ProjectRootElement expected, ProjectRootElement actual) + { + string expectedXml = ProjectXmlToString(expected); + string actualXml = ProjectXmlToString(actual); + + Assert.Equal(expectedXml, actualXml); + } + + private static string ProjectXmlToString(ProjectRootElement projectXml) + { + using var writer = new StringWriterWithUtf8Encoding(); + projectXml.Save(writer); + + return writer.ToString(); + } + + // MSBuild will write out the XML declaration if the encoding isn't UTF8, + // force it into thinking it is to make comparison easier. + private class StringWriterWithUtf8Encoding : StringWriter { - public static void AssertProjectXml(string expected, ProjectRootElement actual) - { - AssertProjectXml(ProjectRootElementFactory.Create(expected), actual); - } - - public static void AssertProjectXml(ProjectRootElement expected, ProjectRootElement actual) - { - string expectedXml = ProjectXmlToString(expected); - string actualXml = ProjectXmlToString(actual); - - Assert.Equal(expectedXml, actualXml); - } - - private static string ProjectXmlToString(ProjectRootElement projectXml) - { - using var writer = new StringWriterWithUtf8Encoding(); - projectXml.Save(writer); - - return writer.ToString(); - } - - // MSBuild will write out the XML declaration if the encoding isn't UTF8, - // force it into thinking it is to make comparison easier. - private class StringWriterWithUtf8Encoding : StringWriter - { - public override Encoding Encoding => Encoding.UTF8; - } + public override Encoding Encoding => Encoding.UTF8; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectInstanceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectInstanceFactory.cs index 6af1c0b401..86dcf51420 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectInstanceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectInstanceFactory.cs @@ -3,15 +3,14 @@ using Microsoft.Build.Construction; using Microsoft.Build.Definition; -namespace Microsoft.Build.Execution +namespace Microsoft.Build.Execution; + +internal static class ProjectInstanceFactory { - internal static class ProjectInstanceFactory + public static ProjectInstance Create(string? xml = null) { - public static ProjectInstance Create(string? xml = null) - { - var element = ProjectRootElementFactory.Create(xml); + var element = ProjectRootElementFactory.Create(xml); - return ProjectInstance.FromProjectRootElement(element, new ProjectOptions()); - } + return ProjectInstance.FromProjectRootElement(element, new ProjectOptions()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectRootElementFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectRootElementFactory.cs index 34a448ee57..3b8fad2e21 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectRootElementFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Build/ProjectRootElementFactory.cs @@ -2,17 +2,16 @@ using System.Xml; -namespace Microsoft.Build.Construction +namespace Microsoft.Build.Construction; + +internal static class ProjectRootElementFactory { - internal static class ProjectRootElementFactory + public static ProjectRootElement Create(string? xml = null) { - public static ProjectRootElement Create(string? xml = null) - { - if (string.IsNullOrEmpty(xml)) - xml = ""; + if (string.IsNullOrEmpty(xml)) + xml = ""; - using var reader = XmlReader.Create(new StringReader(xml)); - return ProjectRootElement.Create(reader); - } + using var reader = XmlReader.Create(new StringReader(xml)); + return ProjectRootElement.Create(reader); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/FuncWithOut.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/FuncWithOut.cs index eb93dc8e71..025ff44857 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/FuncWithOut.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/FuncWithOut.cs @@ -1,10 +1,9 @@ // 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.md file in the project root for more information. -namespace Microsoft -{ - public delegate TResult FuncWithOut(out TOut result); - public delegate TResult FuncWithOut(T1 arg1, out TOut result); - public delegate TResult FuncWithOut(T1 arg1, out TOut1 result1, out TOut2 result2); - public delegate TResult FuncWithOut(T1 arg1, T2 arg2, out TOut1 result1, out TOut2 result2); - public delegate TResult FuncWithOutThreeArgs(out TOut1 result1, out TOut2 result2, out TOut3 result3); -} +namespace Microsoft; + +public delegate TResult FuncWithOut(out TOut result); +public delegate TResult FuncWithOut(T1 arg1, out TOut result); +public delegate TResult FuncWithOut(T1 arg1, out TOut1 result1, out TOut2 result2); +public delegate TResult FuncWithOut(T1 arg1, T2 arg2, out TOut1 result1, out TOut2 result2); +public delegate TResult FuncWithOutThreeArgs(out TOut1 result1, out TOut2 result2, out TOut3 result3); diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/LazyExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/LazyExtensions.cs index 97d56c5321..d1f32a9413 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/LazyExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/LazyExtensions.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft +namespace Microsoft; + +public static class LazyExtensions { - public static class LazyExtensions + public static Lazy AsLazy(this T instance) { - public static Lazy AsLazy(this T instance) - { - return new Lazy(() => instance); - } + return new Lazy(() => instance); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/AbstractMock.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/AbstractMock.cs index 37cb2575e4..c0c71ada94 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/AbstractMock.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/AbstractMock.cs @@ -1,14 +1,13 @@ // 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.md file in the project root for more information. -namespace Moq +namespace Moq; + +internal abstract class AbstractMock : Mock + where T : class { - internal abstract class AbstractMock : Mock - where T : class + protected AbstractMock(MockBehavior behavior = MockBehavior.Loose) + : base(behavior) { - protected AbstractMock(MockBehavior behavior = MockBehavior.Loose) - : base(behavior) - { - Switches = Switches.CollectDiagnosticFileInfoForSetups; - } + Switches = Switches.CollectDiagnosticFileInfoForSetups; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/ReturnsExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/ReturnsExtensions.cs index cb84e6068f..16500fbce2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/ReturnsExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Moq/ReturnsExtensions.cs @@ -3,38 +3,37 @@ using Moq.Language; using Moq.Language.Flow; -namespace Moq +namespace Moq; + +internal static class ReturnsExtensions { - internal static class ReturnsExtensions + public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class + { + return mock.Returns(() => { action(); return Task.CompletedTask; }); + } + + public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class + { + return mock.Returns((T1 arg1) => { action(arg1); return Task.CompletedTask; }); + } + + public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class + { + return mock.Returns((T1 arg1, T2 arg2) => { action(arg1, arg2); return Task.CompletedTask; }); + } + + public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class + { + return mock.Returns((T1 arg1, T2 arg2, T3 arg3) => { action(arg1, arg2, arg3); return Task.CompletedTask; }); + } + + public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class + { + return mock.Returns((T1 arg1, T2 arg2, T3 arg3, T4 arg4) => { action(arg1, arg2, arg3, arg4); return Task.CompletedTask; }); + } + + public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class { - public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class - { - return mock.Returns(() => { action(); return Task.CompletedTask; }); - } - - public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class - { - return mock.Returns((T1 arg1) => { action(arg1); return Task.CompletedTask; }); - } - - public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class - { - return mock.Returns((T1 arg1, T2 arg2) => { action(arg1, arg2); return Task.CompletedTask; }); - } - - public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class - { - return mock.Returns((T1 arg1, T2 arg2, T3 arg3) => { action(arg1, arg2, arg3); return Task.CompletedTask; }); - } - - public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class - { - return mock.Returns((T1 arg1, T2 arg2, T3 arg3, T4 arg4) => { action(arg1, arg2, arg3, arg4); return Task.CompletedTask; }); - } - - public static IReturnsResult ReturnsAsync(this IReturns mock, Action action) where TMock : class - { - return mock.Returns((T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => { action(arg1, arg2, arg3, arg4, arg5); return Task.CompletedTask; }); - } + return mock.Returns((T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => { action(arg1, arg2, arg3, arg4, arg5); return Task.CompletedTask; }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/OrderPrecedenceImportCollectionTestExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/OrderPrecedenceImportCollectionTestExtensions.cs index 3e982d390f..e3960a0333 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/OrderPrecedenceImportCollectionTestExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/OrderPrecedenceImportCollectionTestExtensions.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class OrderPrecedenceImportCollectionTestExtensions { - internal static class OrderPrecedenceImportCollectionTestExtensions + public static void Add(this OrderPrecedenceImportCollection collection, string name, T item) { - public static void Add(this OrderPrecedenceImportCollection collection, string name, T item) - { - var mock = new Mock(); - mock.Setup(v => v.Name) - .Returns(name); + var mock = new Mock(); + mock.Setup(v => v.Name) + .Returns(name); - var result = new Lazy(() => item, mock.Object); + var result = new Lazy(() => item, mock.Object); - collection.Add(result); - } + collection.Add(result); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Delimiters.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Delimiters.cs index 48d853024f..c570fed407 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Delimiters.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Delimiters.cs @@ -2,16 +2,15 @@ using static Microsoft.VisualStudio.ProjectSystem.Tokenizer; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class Delimiters { - internal static class Delimiters - { - public static readonly ImmutableArray QuotedPropertyValue = ImmutableArray.Create(TokenType.Quote, TokenType.CarriageReturn, TokenType.NewLine); - public static readonly ImmutableArray BracedPropertyValueBlock = ImmutableArray.Create(TokenType.RightBrace, TokenType.LeftBrace, TokenType.WhiteSpace, TokenType.CarriageReturn, TokenType.NewLine); - public static readonly ImmutableArray BracedPropertyValue = ImmutableArray.Create(TokenType.RightBrace, TokenType.WhiteSpace, TokenType.CarriageReturn, TokenType.NewLine); - public static readonly ImmutableArray Caption = ImmutableArray.Create(TokenType.LeftParenthesis, TokenType.Comma, TokenType.CarriageReturn, TokenType.NewLine); - public static readonly ImmutableArray PropertyName = ImmutableArray.Create(TokenType.Colon, TokenType.CarriageReturn, TokenType.NewLine, TokenType.WhiteSpace); - public static readonly ImmutableArray PropertyValue = ImmutableArray.Create(TokenType.WhiteSpace, TokenType.Comma, TokenType.RightParenthesis, TokenType.CarriageReturn, TokenType.NewLine); - public static readonly ImmutableArray Structural = ImmutableArray.Create(TokenType.Comma, TokenType.LeftParenthesis, TokenType.RightParenthesis, TokenType.WhiteSpace, TokenType.NewLine, TokenType.CarriageReturn); - } + public static readonly ImmutableArray QuotedPropertyValue = ImmutableArray.Create(TokenType.Quote, TokenType.CarriageReturn, TokenType.NewLine); + public static readonly ImmutableArray BracedPropertyValueBlock = ImmutableArray.Create(TokenType.RightBrace, TokenType.LeftBrace, TokenType.WhiteSpace, TokenType.CarriageReturn, TokenType.NewLine); + public static readonly ImmutableArray BracedPropertyValue = ImmutableArray.Create(TokenType.RightBrace, TokenType.WhiteSpace, TokenType.CarriageReturn, TokenType.NewLine); + public static readonly ImmutableArray Caption = ImmutableArray.Create(TokenType.LeftParenthesis, TokenType.Comma, TokenType.CarriageReturn, TokenType.NewLine); + public static readonly ImmutableArray PropertyName = ImmutableArray.Create(TokenType.Colon, TokenType.CarriageReturn, TokenType.NewLine, TokenType.WhiteSpace); + public static readonly ImmutableArray PropertyValue = ImmutableArray.Create(TokenType.WhiteSpace, TokenType.Comma, TokenType.RightParenthesis, TokenType.CarriageReturn, TokenType.NewLine); + public static readonly ImmutableArray Structural = ImmutableArray.Create(TokenType.Comma, TokenType.LeftParenthesis, TokenType.RightParenthesis, TokenType.WhiteSpace, TokenType.NewLine, TokenType.CarriageReturn); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatError.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatError.cs index 0d2ee22d0a..3bfb5af45f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatError.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatError.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public enum ProjectTreeFormatError { - public enum ProjectTreeFormatError - { - IdExpected_EncounteredOnlyWhiteSpace, - IdExpected_EncounteredDelimiter, - IdExpected_EncounteredEndOfString, - DelimiterExpected, - DelimiterExpected_EncounteredEndOfString, - EndOfStringExpected, - UnrecognizedPropertyName, - UnrecognizedPropertyValue, - IndentTooManyLevels, - MultipleRoots, - IntegerExpected, - GuidExpected, - } + IdExpected_EncounteredOnlyWhiteSpace, + IdExpected_EncounteredDelimiter, + IdExpected_EncounteredEndOfString, + DelimiterExpected, + DelimiterExpected_EncounteredEndOfString, + EndOfStringExpected, + UnrecognizedPropertyName, + UnrecognizedPropertyValue, + IndentTooManyLevels, + MultipleRoots, + IntegerExpected, + GuidExpected, } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatException.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatException.cs index b2bb60be99..0ad96e9aba 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatException.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeFormatException.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal class ProjectTreeFormatException : FormatException { - internal class ProjectTreeFormatException : FormatException + public ProjectTreeFormatException(string message, ProjectTreeFormatError errorId) + : base(message) { - public ProjectTreeFormatException(string message, ProjectTreeFormatError errorId) - : base(message) - { - ErrorId = errorId; - } - - public ProjectTreeFormatError ErrorId { get; } + ErrorId = errorId; } + + public ProjectTreeFormatError ErrorId { get; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectItemTree.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectItemTree.cs index b464d49f47..475a41209d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectItemTree.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectItemTree.cs @@ -2,118 +2,117 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class ProjectTreeParser { - internal partial class ProjectTreeParser + private class MutableProjectItemTree : MutableProjectTree, IProjectItemTree2 { - private class MutableProjectItemTree : MutableProjectTree, IProjectItemTree2 + public MutableProjectItemTree() { - public MutableProjectItemTree() - { - Item = new MutableProjectPropertiesContext(); - } + Item = new MutableProjectPropertiesContext(); + } - public MutableProjectPropertiesContext Item { get; } + public MutableProjectPropertiesContext Item { get; } - public bool IsLinked => throw new NotImplementedException(); + public bool IsLinked => throw new NotImplementedException(); - public IPropertySheet PropertySheet => throw new NotImplementedException(); + public IPropertySheet PropertySheet => throw new NotImplementedException(); - public IProjectTree ClearItem() - { - throw new NotImplementedException(); - } + public IProjectTree ClearItem() + { + throw new NotImplementedException(); + } - public IProjectItemTree SetBrowseObjectProperties(IRule? browseObjectProperties) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetBrowseObjectProperties(IRule? browseObjectProperties) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetCaption(string caption) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetCaption(string caption) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetExpandedIcon(ProjectImageMoniker expandedIcon) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetExpandedIcon(ProjectImageMoniker expandedIcon) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetIcon(ProjectImageMoniker icon) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetIcon(ProjectImageMoniker icon) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetIsLinked(bool isLinked) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetIsLinked(bool isLinked) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetItem(IProjectPropertiesContext projectPropertiesContext) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetItem(IProjectPropertiesContext projectPropertiesContext) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetPropertySheet(IPropertySheet propertySheet) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetPropertySheet(IPropertySheet propertySheet) + { + throw new NotImplementedException(); + } - public IProjectItemTree SetVisible(bool visible) - { - throw new NotImplementedException(); - } + public IProjectItemTree SetVisible(bool visible) + { + throw new NotImplementedException(); + } - IProjectItemTree IProjectItemTree.SetFlags(ProjectTreeFlags flags) - { - throw new NotImplementedException(); - } + IProjectItemTree IProjectItemTree.SetFlags(ProjectTreeFlags flags) + { + throw new NotImplementedException(); + } - IProjectPropertiesContext IProjectItemTree.Item - { - get { return Item; } - } + IProjectPropertiesContext IProjectItemTree.Item + { + get { return Item; } + } - IProjectItemTree2 IProjectItemTree2.SetProperties(string? caption, string? filePath, IRule? browseObjectProperties, ProjectImageMoniker? icon, ProjectImageMoniker? expandedIcon, bool? visible, ProjectTreeFlags? flags, IProjectPropertiesContext? context, IPropertySheet? propertySheet, bool? isLinked, bool resetFilePath, bool resetBrowseObjectProperties, bool resetIcon, bool resetExpandedIcon, int? displayOrder) - { - throw new NotImplementedException(); - } + IProjectItemTree2 IProjectItemTree2.SetProperties(string? caption, string? filePath, IRule? browseObjectProperties, ProjectImageMoniker? icon, ProjectImageMoniker? expandedIcon, bool? visible, ProjectTreeFlags? flags, IProjectPropertiesContext? context, IPropertySheet? propertySheet, bool? isLinked, bool resetFilePath, bool resetBrowseObjectProperties, bool resetIcon, bool resetExpandedIcon, int? displayOrder) + { + throw new NotImplementedException(); + } + + IProjectItemTree IProjectItemTree.SetProperties(string? caption, string? filePath, IRule? browseObjectProperties, ProjectImageMoniker? icon, ProjectImageMoniker? expandedIcon, bool? visible, ProjectTreeFlags? flags, IProjectPropertiesContext? context, IPropertySheet? propertySheet, bool? isLinked, bool resetFilePath, bool resetBrowseObjectProperties, bool resetIcon, bool resetExpandedIcon) + { + throw new NotImplementedException(); + } - IProjectItemTree IProjectItemTree.SetProperties(string? caption, string? filePath, IRule? browseObjectProperties, ProjectImageMoniker? icon, ProjectImageMoniker? expandedIcon, bool? visible, ProjectTreeFlags? flags, IProjectPropertiesContext? context, IPropertySheet? propertySheet, bool? isLinked, bool resetFilePath, bool resetBrowseObjectProperties, bool resetIcon, bool resetExpandedIcon) + public MutableProjectTree BuildProjectTree() + { + // MutableProjectItemTree acts as a builder for both MutableProjectItemTree and MutableProjectTree + // + // Once we've finished building, return either ourselves if we are already are a MutableProjectItemTree + // otherwise, copy ourselves to a MutableProjectTree. + + if (!string.IsNullOrEmpty(Item.ItemName) || !string.IsNullOrEmpty(Item.ItemType)) { - throw new NotImplementedException(); + return this; } - public MutableProjectTree BuildProjectTree() + var tree = new MutableProjectTree(); + foreach (MutableProjectTree child in Children) { - // MutableProjectItemTree acts as a builder for both MutableProjectItemTree and MutableProjectTree - // - // Once we've finished building, return either ourselves if we are already are a MutableProjectItemTree - // otherwise, copy ourselves to a MutableProjectTree. - - if (!string.IsNullOrEmpty(Item.ItemName) || !string.IsNullOrEmpty(Item.ItemType)) - { - return this; - } - - var tree = new MutableProjectTree(); - foreach (MutableProjectTree child in Children) - { - tree.Children.Add(child); - } - - tree.Caption = Caption; - tree.Flags = Flags; - tree.FilePath = FilePath; - tree.Visible = Visible; - tree.Parent = Parent; - tree.Icon = Icon; - tree.SubType = SubType; - tree.ExpandedIcon = ExpandedIcon; - tree.DisplayOrder = DisplayOrder; - - return tree; + tree.Children.Add(child); } + + tree.Caption = Caption; + tree.Flags = Flags; + tree.FilePath = FilePath; + tree.Visible = Visible; + tree.Parent = Parent; + tree.Icon = Icon; + tree.SubType = SubType; + tree.ExpandedIcon = ExpandedIcon; + tree.DisplayOrder = DisplayOrder; + + return tree; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectPropertiesContext.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectPropertiesContext.cs index 2c537ef3f4..58f9228576 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectPropertiesContext.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectPropertiesContext.cs @@ -2,19 +2,18 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class ProjectTreeParser { - internal partial class ProjectTreeParser + private class MutableProjectPropertiesContext : IProjectPropertiesContext { - private class MutableProjectPropertiesContext : IProjectPropertiesContext - { - public bool IsProjectFile => throw new NotImplementedException(); + public bool IsProjectFile => throw new NotImplementedException(); - public string File => throw new NotImplementedException(); + public string File => throw new NotImplementedException(); - public string ItemType { get; set; } = ""; + public string ItemType { get; set; } = ""; - public string? ItemName { get; set; } - } + public string? ItemName { get; set; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.SubTypeRule.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.SubTypeRule.cs index 012f4f36f0..c7f4bc3ab0 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.SubTypeRule.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.SubTypeRule.cs @@ -3,56 +3,55 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class ProjectTreeParser { - internal partial class ProjectTreeParser + private partial class MutableProjectTree { - private partial class MutableProjectTree + // Does nothing other than return the "SubType" property + private class SubTypeRule : IRule { - // Does nothing other than return the "SubType" property - private class SubTypeRule : IRule + private readonly MutableProjectTree _tree; + + public SubTypeRule(MutableProjectTree tree) { - private readonly MutableProjectTree _tree; + _tree = tree; + } - public SubTypeRule(MutableProjectTree tree) - { - _tree = tree; - } + public IPropertyGroup this[string categoryName] => throw new NotImplementedException(); + + public string Name => throw new NotImplementedException(); + public string DisplayName => throw new NotImplementedException(); + public string Description => throw new NotImplementedException(); + public string HelpString => throw new NotImplementedException(); + public string PageTemplate => throw new NotImplementedException(); + public string SwitchPrefix => throw new NotImplementedException(); + public string Separator => throw new NotImplementedException(); + public IReadOnlyList Categories => throw new NotImplementedException(); + public Rule Schema => throw new NotImplementedException(); + public int Order => throw new NotImplementedException(); + public string File => throw new NotImplementedException(); + public string ItemType => throw new NotImplementedException(); + public string ItemName => throw new NotImplementedException(); + public IReadOnlyList PropertyGroups => throw new NotImplementedException(); + public IEnumerable Properties => throw new NotImplementedException(); + public IProjectPropertiesContext Context => throw new NotImplementedException(); + public bool PropertyPagesHidden => throw new NotImplementedException(); + + public IProperty GetProperty(string propertyName) + { + throw new NotImplementedException(); + } - public IPropertyGroup this[string categoryName] => throw new NotImplementedException(); - - public string Name => throw new NotImplementedException(); - public string DisplayName => throw new NotImplementedException(); - public string Description => throw new NotImplementedException(); - public string HelpString => throw new NotImplementedException(); - public string PageTemplate => throw new NotImplementedException(); - public string SwitchPrefix => throw new NotImplementedException(); - public string Separator => throw new NotImplementedException(); - public IReadOnlyList Categories => throw new NotImplementedException(); - public Rule Schema => throw new NotImplementedException(); - public int Order => throw new NotImplementedException(); - public string File => throw new NotImplementedException(); - public string ItemType => throw new NotImplementedException(); - public string ItemName => throw new NotImplementedException(); - public IReadOnlyList PropertyGroups => throw new NotImplementedException(); - public IEnumerable Properties => throw new NotImplementedException(); - public IProjectPropertiesContext Context => throw new NotImplementedException(); - public bool PropertyPagesHidden => throw new NotImplementedException(); - - public IProperty GetProperty(string propertyName) + public Task GetPropertyValueAsync(string propertyName) + { + if (propertyName == "SubType") { - throw new NotImplementedException(); + return Task.FromResult(_tree.SubType ?? ""); } - public Task GetPropertyValueAsync(string propertyName) - { - if (propertyName == "SubType") - { - return Task.FromResult(_tree.SubType ?? ""); - } - - throw new NotImplementedException(); - } + throw new NotImplementedException(); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.cs index 89ad9b53a4..5f9e9be644 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.MutableProjectTree.cs @@ -3,233 +3,232 @@ using System.Collections.ObjectModel; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class ProjectTreeParser { - internal partial class ProjectTreeParser + private partial class MutableProjectTree : IProjectTree2 { - private partial class MutableProjectTree : IProjectTree2 + public MutableProjectTree() { - public MutableProjectTree() - { - Children = new Collection(); - Visible = true; - Caption = ""; - BrowseObjectProperties = new SubTypeRule(this); - } + Children = new Collection(); + Visible = true; + Caption = ""; + BrowseObjectProperties = new SubTypeRule(this); + } - public Collection Children { get; } + public Collection Children { get; } - public string Caption { get; set; } + public string Caption { get; set; } - public ProjectTreeFlags Flags { get; set; } + public ProjectTreeFlags Flags { get; set; } - public string? SubType { get; set; } + public string? SubType { get; set; } - public bool IsFolder - { - get { return Flags.Contains(ProjectTreeFlags.Common.Folder); } - } + public bool IsFolder + { + get { return Flags.Contains(ProjectTreeFlags.Common.Folder); } + } - public string? FilePath { get; set; } + public string? FilePath { get; set; } - public bool Visible { get; set; } + public bool Visible { get; set; } - public MutableProjectTree? Parent { get; set; } + public MutableProjectTree? Parent { get; set; } - IProjectTree? IProjectTree.Parent - { - get { return Parent; } - } + IProjectTree? IProjectTree.Parent + { + get { return Parent; } + } - public IRule BrowseObjectProperties { get; } + public IRule BrowseObjectProperties { get; } - IReadOnlyList IProjectTree.Children - { - get { return Children; } - } + IReadOnlyList IProjectTree.Children + { + get { return Children; } + } - public ProjectImageMoniker? Icon { get; set; } + public ProjectImageMoniker? Icon { get; set; } - public ProjectImageMoniker? ExpandedIcon { get; set; } + public ProjectImageMoniker? ExpandedIcon { get; set; } - IntPtr IProjectTree.Identity + IntPtr IProjectTree.Identity + { + get { - get - { - return new IntPtr(Caption!.GetHashCode()); - } + return new IntPtr(Caption!.GetHashCode()); } + } - IProjectTree IProjectTree.Root + IProjectTree IProjectTree.Root + { + get { - get + MutableProjectTree root = this; + while (root.Parent is not null) { - MutableProjectTree root = this; - while (root.Parent is not null) - { - root = root.Parent; - } - - return root; + root = root.Parent; } - } - - public IProjectTree AddFlag(string flag) - { - if (!Flags.Contains(flag)) - Flags = Flags.Add(flag); - return this; + return root; } + } - int IProjectTree.Size - { - get - { - throw new NotImplementedException(); - } - } + public IProjectTree AddFlag(string flag) + { + if (!Flags.Contains(flag)) + Flags = Flags.Add(flag); - public int DisplayOrder { get; set; } + return this; + } - IProjectItemTree IProjectTree.Add(IProjectItemTree subtree) + int IProjectTree.Size + { + get { throw new NotImplementedException(); } + } - IProjectTree IProjectTree.Add(IProjectTree subtree) - { - if (subtree is MutableProjectTree mutableTree) - { - Children.Add(mutableTree); - } + public int DisplayOrder { get; set; } - return this; - } + IProjectItemTree IProjectTree.Add(IProjectItemTree subtree) + { + throw new NotImplementedException(); + } - IEnumerable IProjectTree.ChangesSince(IProjectTree priorVersion) + IProjectTree IProjectTree.Add(IProjectTree subtree) + { + if (subtree is MutableProjectTree mutableTree) { - throw new NotImplementedException(); + Children.Add(mutableTree); } - bool IProjectTree.Contains(IntPtr nodeId) - { - throw new NotImplementedException(); - } + return this; + } - IProjectTree IProjectTree.Find(IntPtr nodeId) - { - throw new NotImplementedException(); - } + IEnumerable IProjectTree.ChangesSince(IProjectTree priorVersion) + { + throw new NotImplementedException(); + } - IProjectTree IProjectTree.Remove() - { - throw new NotImplementedException(); - } + bool IProjectTree.Contains(IntPtr nodeId) + { + throw new NotImplementedException(); + } - IProjectTree IProjectTree.Remove(IProjectTree subtree) + IProjectTree IProjectTree.Find(IntPtr nodeId) + { + throw new NotImplementedException(); + } + + IProjectTree IProjectTree.Remove() + { + throw new NotImplementedException(); + } + + IProjectTree IProjectTree.Remove(IProjectTree subtree) + { + if (subtree is MutableProjectTree mutableTree) { - if (subtree is MutableProjectTree mutableTree) + if (Children.Contains(mutableTree)) { - if (Children.Contains(mutableTree)) - { - Children.Remove(mutableTree); - } + Children.Remove(mutableTree); } - - return this; } - IProjectItemTree IProjectTree.Replace(IProjectItemTree subtree) - { - return subtree; - } + return this; + } - IProjectTree IProjectTree.Replace(IProjectTree subtree) - { - return subtree; - } + IProjectItemTree IProjectTree.Replace(IProjectItemTree subtree) + { + return subtree; + } - IProjectTree IProjectTree.SetBrowseObjectProperties(IRule? browseObjectProperties) - { - throw new NotImplementedException(); - } + IProjectTree IProjectTree.Replace(IProjectTree subtree) + { + return subtree; + } - IProjectTree IProjectTree.SetCaption(string caption) - { - throw new NotImplementedException(); - } + IProjectTree IProjectTree.SetBrowseObjectProperties(IRule? browseObjectProperties) + { + throw new NotImplementedException(); + } - IProjectTree IProjectTree.SetExpandedIcon(ProjectImageMoniker? expandedIcon) - { - ExpandedIcon = expandedIcon; + IProjectTree IProjectTree.SetCaption(string caption) + { + throw new NotImplementedException(); + } - return this; - } + IProjectTree IProjectTree.SetExpandedIcon(ProjectImageMoniker? expandedIcon) + { + ExpandedIcon = expandedIcon; - IProjectTree IProjectTree.SetIcon(ProjectImageMoniker? icon) - { - Icon = icon; + return this; + } - return this; - } + IProjectTree IProjectTree.SetIcon(ProjectImageMoniker? icon) + { + Icon = icon; - IProjectItemTree IProjectTree.SetItem(IProjectPropertiesContext context, IPropertySheet? propertySheet, bool isLinked) - { - throw new NotImplementedException(); - } + return this; + } - IProjectTree IProjectTree.SetVisible(bool visible) - { - throw new NotImplementedException(); - } + IProjectItemTree IProjectTree.SetItem(IProjectPropertiesContext context, IPropertySheet? propertySheet, bool isLinked) + { + throw new NotImplementedException(); + } - bool IProjectTree.TryFind(IntPtr nodeId, out IProjectTree subtree) - { - throw new NotImplementedException(); - } + IProjectTree IProjectTree.SetVisible(bool visible) + { + throw new NotImplementedException(); + } - bool IProjectTree.TryFindImmediateChild(string caption, out IProjectTree subtree) - { - subtree = Children.FirstOrDefault(c => c.Caption == caption); - return subtree is not null; - } + bool IProjectTree.TryFind(IntPtr nodeId, out IProjectTree subtree) + { + throw new NotImplementedException(); + } - public IProjectTree SetProperties(string? caption = null, string? filePath = null, IRule? browseObjectProperties = null, ProjectImageMoniker? icon = null, ProjectImageMoniker? expandedIcon = null, bool? visible = null, ProjectTreeFlags? flags = null, IProjectPropertiesContext? context = null, IPropertySheet? propertySheet = null, bool? isLinked = null, bool resetFilePath = false, bool resetBrowseObjectProperties = false, bool resetIcon = false, bool resetExpandedIcon = false) - { - if (caption is not null) - Caption = caption; + bool IProjectTree.TryFindImmediateChild(string caption, out IProjectTree subtree) + { + subtree = Children.FirstOrDefault(c => c.Caption == caption); + return subtree is not null; + } - if (filePath is not null) - FilePath = filePath; + public IProjectTree SetProperties(string? caption = null, string? filePath = null, IRule? browseObjectProperties = null, ProjectImageMoniker? icon = null, ProjectImageMoniker? expandedIcon = null, bool? visible = null, ProjectTreeFlags? flags = null, IProjectPropertiesContext? context = null, IPropertySheet? propertySheet = null, bool? isLinked = null, bool resetFilePath = false, bool resetBrowseObjectProperties = false, bool resetIcon = false, bool resetExpandedIcon = false) + { + if (caption is not null) + Caption = caption; - if (visible != null) - Visible = visible.Value; + if (filePath is not null) + FilePath = filePath; - if (flags is not null) - Flags = flags.Value; + if (visible != null) + Visible = visible.Value; - return this; - } + if (flags is not null) + Flags = flags.Value; - public IProjectTree SetFlags(ProjectTreeFlags flags) - { - Flags = flags; + return this; + } - return this; - } + public IProjectTree SetFlags(ProjectTreeFlags flags) + { + Flags = flags; - public IProjectTree2 SetProperties(string? caption = null, string? filePath = null, IRule? browseObjectProperties = null, ProjectImageMoniker? icon = null, ProjectImageMoniker? expandedIcon = null, bool? visible = null, ProjectTreeFlags? flags = null, IProjectPropertiesContext? context = null, IPropertySheet? propertySheet = null, bool? isLinked = null, bool resetFilePath = false, bool resetBrowseObjectProperties = false, bool resetIcon = false, bool resetExpandedIcon = false, int? displayOrder = null) - { - throw new NotImplementedException(); - } + return this; + } - public IProjectTree2 SetDisplayOrder(int displayOrder) - { - DisplayOrder = displayOrder; + public IProjectTree2 SetProperties(string? caption = null, string? filePath = null, IRule? browseObjectProperties = null, ProjectImageMoniker? icon = null, ProjectImageMoniker? expandedIcon = null, bool? visible = null, ProjectTreeFlags? flags = null, IProjectPropertiesContext? context = null, IPropertySheet? propertySheet = null, bool? isLinked = null, bool resetFilePath = false, bool resetBrowseObjectProperties = false, bool resetIcon = false, bool resetExpandedIcon = false, int? displayOrder = null) + { + throw new NotImplementedException(); + } - return this; - } + public IProjectTree2 SetDisplayOrder(int displayOrder) + { + DisplayOrder = displayOrder; + + return this; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.cs index 81ce691bf8..28ff219a26 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/ProjectTreeParser.cs @@ -2,378 +2,377 @@ using static Microsoft.VisualStudio.ProjectSystem.Tokenizer; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +// Parses a string into a project tree +// +internal partial class ProjectTreeParser { - // Parses a string into a project tree - // - internal partial class ProjectTreeParser + private readonly Tokenizer _tokenizer; + private int _indentLevel; + + public ProjectTreeParser(string value) { - private readonly Tokenizer _tokenizer; - private int _indentLevel; + Requires.NotNullOrEmpty(value); - public ProjectTreeParser(string value) - { - Requires.NotNullOrEmpty(value); + _tokenizer = new Tokenizer(new SimpleStringReader(value), Delimiters.Structural); + } - _tokenizer = new Tokenizer(new SimpleStringReader(value), Delimiters.Structural); - } + public static IProjectTree Parse(string value) + { + value = value.Trim(new char[] { '\r', '\n' }); - public static IProjectTree Parse(string value) - { - value = value.Trim(new char[] { '\r', '\n' }); + var parser = new ProjectTreeParser(value); - var parser = new ProjectTreeParser(value); + return parser.Parse(); + } - return parser.Parse(); - } + public IProjectTree Parse() + { + MutableProjectTree root = ReadProjectRoot(); + + _tokenizer.Close(); + + return root; + } - public IProjectTree Parse() + private MutableProjectTree ReadProjectRoot() + { + // We always start with the root, with zero indent + MutableProjectTree root = ReadProjectItem(); + MutableProjectTree? current = root; + do { - MutableProjectTree root = ReadProjectRoot(); + current = ReadNextProjectItem(current); - _tokenizer.Close(); + } while (current is not null); - return root; - } + return root; + } - private MutableProjectTree ReadProjectRoot() - { - // We always start with the root, with zero indent - MutableProjectTree root = ReadProjectItem(); - MutableProjectTree? current = root; - do - { - current = ReadNextProjectItem(current); + private MutableProjectTree? ReadNextProjectItem(MutableProjectTree current) + { + if (!TryReadNewLine()) + return null; - } while (current is not null); + MutableProjectTree? parent = current; + int indent = ReadIndentLevel(out int previousIndentLevel); - return root; + while (indent <= previousIndentLevel) + { + parent = parent?.Parent; + indent++; } - private MutableProjectTree? ReadNextProjectItem(MutableProjectTree current) + if (parent is null) + throw FormatException(ProjectTreeFormatError.MultipleRoots, "Encountered another project root, when tree can only have one."); + + MutableProjectTree tree = ReadProjectItem(); + tree.Parent = parent; + parent.Children.Add(tree); + return tree; + } + + private int ReadIndentLevel(out int previousIndentLevel) + { + // Attempts to read the indent level of the current project item + // + // Root <--- IndentLevel: 0 + // Parent <--- IndentLevel: 1 + // Child <--- IndentLevel: 2 + + previousIndentLevel = _indentLevel; + int indentLevel = 0; + + while (_tokenizer.Peek() == TokenType.WhiteSpace) { - if (!TryReadNewLine()) - return null; + _tokenizer.Skip(TokenType.WhiteSpace); + _tokenizer.Skip(TokenType.WhiteSpace); + _tokenizer.Skip(TokenType.WhiteSpace); + _tokenizer.Skip(TokenType.WhiteSpace); - MutableProjectTree? parent = current; - int indent = ReadIndentLevel(out int previousIndentLevel); + indentLevel++; + } - while (indent <= previousIndentLevel) - { - parent = parent?.Parent; - indent++; - } + if (indentLevel > previousIndentLevel + 1) + throw FormatException(ProjectTreeFormatError.IndentTooManyLevels, "Project item has been indented too many levels"); - if (parent is null) - throw FormatException(ProjectTreeFormatError.MultipleRoots, "Encountered another project root, when tree can only have one."); + return _indentLevel = indentLevel; + } - MutableProjectTree tree = ReadProjectItem(); - tree.Parent = parent; - parent.Children.Add(tree); - return tree; + private bool TryReadNewLine() + { + if (_tokenizer.SkipIf(TokenType.CarriageReturn)) + { // If we read '\r', it must be followed by a '\n' + _tokenizer.Skip(TokenType.NewLine); + return true; } - private int ReadIndentLevel(out int previousIndentLevel) - { - // Attempts to read the indent level of the current project item - // - // Root <--- IndentLevel: 0 - // Parent <--- IndentLevel: 1 - // Child <--- IndentLevel: 2 + return _tokenizer.SkipIf(TokenType.NewLine); + } + + private MutableProjectTree ReadProjectItem() + { + var tree = new MutableProjectItemTree(); + ReadProjectItemProperties(tree); - previousIndentLevel = _indentLevel; - int indentLevel = 0; + return tree.BuildProjectTree(); + } - while (_tokenizer.Peek() == TokenType.WhiteSpace) - { - _tokenizer.Skip(TokenType.WhiteSpace); - _tokenizer.Skip(TokenType.WhiteSpace); - _tokenizer.Skip(TokenType.WhiteSpace); - _tokenizer.Skip(TokenType.WhiteSpace); + private void ReadProjectItemProperties(MutableProjectItemTree tree) + { // Parse "Root (visibility: visible, flags: {ProjectRoot}), FilePath: "C:\My Project\MyFile.txt", Icon: {1B5CF1ED-9525-42B4-85F0-2CB50530ECA9 1}, ExpandedIcon: {1B5CF1ED-9525-42B4-85F0-2CB50530ECA9 1} + ReadCaption(tree); + ReadProperties(tree); + ReadFields(tree); + } - indentLevel++; - } + private void ReadCaption(MutableProjectItemTree tree) + { + Tokenizer tokenizer = Tokenizer(Delimiters.Caption); - if (indentLevel > previousIndentLevel + 1) - throw FormatException(ProjectTreeFormatError.IndentTooManyLevels, "Project item has been indented too many levels"); + tree.Caption = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + } - return _indentLevel = indentLevel; - } + private void ReadProperties(MutableProjectItemTree tree) + { // Parses "(visibility: visible, flags: {ProjectRoot})" + // Properties section is optional + if (!_tokenizer.SkipIf(TokenType.LeftParenthesis)) + return; - private bool TryReadNewLine() - { - if (_tokenizer.SkipIf(TokenType.CarriageReturn)) - { // If we read '\r', it must be followed by a '\n' - _tokenizer.Skip(TokenType.NewLine); - return true; - } + // Empty properties + if (_tokenizer.SkipIf(TokenType.RightParenthesis)) + return; - return _tokenizer.SkipIf(TokenType.NewLine); - } + ReadProperty(tree); - private MutableProjectTree ReadProjectItem() + while (_tokenizer.SkipIf(TokenType.Comma)) { - var tree = new MutableProjectItemTree(); - ReadProjectItemProperties(tree); - - return tree.BuildProjectTree(); + _tokenizer.Skip(TokenType.WhiteSpace); + ReadProperty(tree); } - private void ReadProjectItemProperties(MutableProjectItemTree tree) - { // Parse "Root (visibility: visible, flags: {ProjectRoot}), FilePath: "C:\My Project\MyFile.txt", Icon: {1B5CF1ED-9525-42B4-85F0-2CB50530ECA9 1}, ExpandedIcon: {1B5CF1ED-9525-42B4-85F0-2CB50530ECA9 1} - ReadCaption(tree); - ReadProperties(tree); - ReadFields(tree); - } + _tokenizer.Skip(TokenType.RightParenthesis); + } - private void ReadCaption(MutableProjectItemTree tree) - { - Tokenizer tokenizer = Tokenizer(Delimiters.Caption); + private void ReadProperty(MutableProjectItemTree tree) + { + Tokenizer tokenizer = Tokenizer(Delimiters.PropertyName); - tree.Caption = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + string propertyName = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + + switch (propertyName) + { + case "visibility": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadVisibility(tree); + break; + + case "flags": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadCapabilities(tree); + break; + + default: + throw FormatException(ProjectTreeFormatError.UnrecognizedPropertyName, $"Expected 'visibility' or 'flags', but encountered '{propertyName}'."); } + } - private void ReadProperties(MutableProjectItemTree tree) - { // Parses "(visibility: visible, flags: {ProjectRoot})" - // Properties section is optional - if (!_tokenizer.SkipIf(TokenType.LeftParenthesis)) - return; + private void ReadVisibility(MutableProjectItemTree tree) + { // Parse 'visible' in 'visibility:visible' or 'invisible' in 'visibility:invisible" + Tokenizer tokenizer = Tokenizer(Delimiters.PropertyValue); - // Empty properties - if (_tokenizer.SkipIf(TokenType.RightParenthesis)) - return; + string visibility = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - ReadProperty(tree); + tree.Visible = visibility switch + { + "visible" => true, + "invisible" => false, - while (_tokenizer.SkipIf(TokenType.Comma)) - { - _tokenizer.Skip(TokenType.WhiteSpace); - ReadProperty(tree); - } + _ => throw FormatException(ProjectTreeFormatError.UnrecognizedPropertyValue, $"Expected 'visible' or 'invisible', but encountered '{visibility}'."), + }; + } + + private void ReadCapabilities(MutableProjectItemTree tree) + { // Parse '{ProjectRoot Folder}' + Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValueBlock); + tokenizer.Skip(TokenType.LeftBrace); - _tokenizer.Skip(TokenType.RightParenthesis); + // Empty flags + if (tokenizer.SkipIf(TokenType.RightBrace)) + return; + + do + { + ReadFlag(tree); } + while (tokenizer.SkipIf(TokenType.WhiteSpace)); + tokenizer.Skip(TokenType.RightBrace); + } + + private void ReadFlag(MutableProjectItemTree tree) + { // Parses 'AppDesigner' in '{AppDesigner Folder}' + Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValue); - private void ReadProperty(MutableProjectItemTree tree) + string flag = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + tree.AddFlag(flag); + } + + private void ReadFields(MutableProjectItemTree tree) + { // Parses ', FilePath: "C:\Temp\Foo"' + // This section is optional + while (_tokenizer.SkipIf(TokenType.Comma)) { + _tokenizer.Skip(TokenType.WhiteSpace); + Tokenizer tokenizer = Tokenizer(Delimiters.PropertyName); - string propertyName = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + string fieldName = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - switch (propertyName) + switch (fieldName) { - case "visibility": + case "FilePath": tokenizer.Skip(TokenType.Colon); tokenizer.Skip(TokenType.WhiteSpace); - ReadVisibility(tree); + ReadFilePath(tree); break; - case "flags": + case "ItemType": tokenizer.Skip(TokenType.Colon); tokenizer.Skip(TokenType.WhiteSpace); - ReadCapabilities(tree); + ReadItemType(tree); break; - default: - throw FormatException(ProjectTreeFormatError.UnrecognizedPropertyName, $"Expected 'visibility' or 'flags', but encountered '{propertyName}'."); - } - } - - private void ReadVisibility(MutableProjectItemTree tree) - { // Parse 'visible' in 'visibility:visible' or 'invisible' in 'visibility:invisible" - Tokenizer tokenizer = Tokenizer(Delimiters.PropertyValue); - - string visibility = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - - tree.Visible = visibility switch - { - "visible" => true, - "invisible" => false, - - _ => throw FormatException(ProjectTreeFormatError.UnrecognizedPropertyValue, $"Expected 'visible' or 'invisible', but encountered '{visibility}'."), - }; - } - - private void ReadCapabilities(MutableProjectItemTree tree) - { // Parse '{ProjectRoot Folder}' - Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValueBlock); - tokenizer.Skip(TokenType.LeftBrace); + case "SubType": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadSubType(tree); + break; - // Empty flags - if (tokenizer.SkipIf(TokenType.RightBrace)) - return; + case "Icon": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadIcon(tree, expandedIcon: false); + break; - do - { - ReadFlag(tree); - } - while (tokenizer.SkipIf(TokenType.WhiteSpace)); - tokenizer.Skip(TokenType.RightBrace); - } + case "ExpandedIcon": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadIcon(tree, expandedIcon: true); + break; - private void ReadFlag(MutableProjectItemTree tree) - { // Parses 'AppDesigner' in '{AppDesigner Folder}' - Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValue); + case "DisplayOrder": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadDisplayOrder(tree); + break; - string flag = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - tree.AddFlag(flag); - } + case "ItemName": + tokenizer.Skip(TokenType.Colon); + tokenizer.Skip(TokenType.WhiteSpace); + ReadItemName(tree); + break; - private void ReadFields(MutableProjectItemTree tree) - { // Parses ', FilePath: "C:\Temp\Foo"' - // This section is optional - while (_tokenizer.SkipIf(TokenType.Comma)) - { - _tokenizer.Skip(TokenType.WhiteSpace); - - Tokenizer tokenizer = Tokenizer(Delimiters.PropertyName); - - string fieldName = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - - switch (fieldName) - { - case "FilePath": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadFilePath(tree); - break; - - case "ItemType": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadItemType(tree); - break; - - case "SubType": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadSubType(tree); - break; - - case "Icon": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadIcon(tree, expandedIcon: false); - break; - - case "ExpandedIcon": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadIcon(tree, expandedIcon: true); - break; - - case "DisplayOrder": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadDisplayOrder(tree); - break; - - case "ItemName": - tokenizer.Skip(TokenType.Colon); - tokenizer.Skip(TokenType.WhiteSpace); - ReadItemName(tree); - break; - - default: - throw FormatException(ProjectTreeFormatError.UnrecognizedPropertyName, $"Expected 'FilePath', 'Icon' or 'ExpandedIcon', but encountered '{fieldName}'."); - } + default: + throw FormatException(ProjectTreeFormatError.UnrecognizedPropertyName, $"Expected 'FilePath', 'Icon' or 'ExpandedIcon', but encountered '{fieldName}'."); } } + } - private void ReadDisplayOrder(MutableProjectItemTree tree) - { // Parses '1` - Tokenizer tokenizer = Tokenizer(Delimiters.PropertyValue); - - string identifier = tokenizer.ReadIdentifier(IdentifierParseOptions.None); + private void ReadDisplayOrder(MutableProjectItemTree tree) + { // Parses '1` + Tokenizer tokenizer = Tokenizer(Delimiters.PropertyValue); - tree.DisplayOrder = int.Parse(identifier); - } + string identifier = tokenizer.ReadIdentifier(IdentifierParseOptions.None); - private void ReadItemName(MutableProjectItemTree tree) - { // Parses '"test.fs"' - tree.Item.ItemName = ReadQuotedPropertyValue(); - } + tree.DisplayOrder = int.Parse(identifier); + } - private void ReadFilePath(MutableProjectItemTree tree) - { // Parses '"C:\Temp\Foo"' - tree.FilePath = ReadQuotedPropertyValue(); - } + private void ReadItemName(MutableProjectItemTree tree) + { // Parses '"test.fs"' + tree.Item.ItemName = ReadQuotedPropertyValue(); + } - private void ReadItemType(MutableProjectItemTree tree) - { - tree.Item.ItemType = ReadPropertyValue(); - } + private void ReadFilePath(MutableProjectItemTree tree) + { // Parses '"C:\Temp\Foo"' + tree.FilePath = ReadQuotedPropertyValue(); + } - private void ReadSubType(MutableProjectItemTree tree) - { - tree.SubType = ReadPropertyValue(); - } + private void ReadItemType(MutableProjectItemTree tree) + { + tree.Item.ItemType = ReadPropertyValue(); + } - private string ReadPropertyValue() - { - Tokenizer tokenizer = Tokenizer(Delimiters.PropertyValue); + private void ReadSubType(MutableProjectItemTree tree) + { + tree.SubType = ReadPropertyValue(); + } - return tokenizer.ReadIdentifier(IdentifierParseOptions.None); - } + private string ReadPropertyValue() + { + Tokenizer tokenizer = Tokenizer(Delimiters.PropertyValue); - private string ReadQuotedPropertyValue() - { // Parses '"C:\Temp"' - Tokenizer tokenizer = Tokenizer(Delimiters.QuotedPropertyValue); + return tokenizer.ReadIdentifier(IdentifierParseOptions.None); + } - tokenizer.Skip(TokenType.Quote); + private string ReadQuotedPropertyValue() + { // Parses '"C:\Temp"' + Tokenizer tokenizer = Tokenizer(Delimiters.QuotedPropertyValue); - string value = tokenizer.ReadIdentifier(IdentifierParseOptions.None); + tokenizer.Skip(TokenType.Quote); - tokenizer.Skip(TokenType.Quote); + string value = tokenizer.ReadIdentifier(IdentifierParseOptions.None); - return value; - } + tokenizer.Skip(TokenType.Quote); - private void ReadIcon(MutableProjectTree tree, bool expandedIcon) - { // Parses '{1B5CF1ED-9525-42B4-85F0-2CB50530ECA9 1}' - Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValueBlock); - tokenizer.Skip(TokenType.LeftBrace); + return value; + } - // Empty icon - if (tokenizer.SkipIf(TokenType.RightBrace)) - return; + private void ReadIcon(MutableProjectTree tree, bool expandedIcon) + { // Parses '{1B5CF1ED-9525-42B4-85F0-2CB50530ECA9 1}' + Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValueBlock); + tokenizer.Skip(TokenType.LeftBrace); - ProjectImageMoniker moniker = ReadProjectImageMoniker(); + // Empty icon + if (tokenizer.SkipIf(TokenType.RightBrace)) + return; - if (expandedIcon) - { - tree.ExpandedIcon = moniker; - } - else - { - tree.Icon = moniker; - } + ProjectImageMoniker moniker = ReadProjectImageMoniker(); - tokenizer.Skip(TokenType.RightBrace); + if (expandedIcon) + { + tree.ExpandedIcon = moniker; } - - private ProjectImageMoniker ReadProjectImageMoniker() + else { - Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValue); + tree.Icon = moniker; + } - string guidAsString = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + tokenizer.Skip(TokenType.RightBrace); + } - if (!Guid.TryParseExact(guidAsString, "D", out Guid guid)) - throw FormatException(ProjectTreeFormatError.GuidExpected, $"Expected GUID, but encountered '{guidAsString}'"); + private ProjectImageMoniker ReadProjectImageMoniker() + { + Tokenizer tokenizer = Tokenizer(Delimiters.BracedPropertyValue); - tokenizer.Skip(TokenType.WhiteSpace); + string guidAsString = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - string idAsString = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); + if (!Guid.TryParseExact(guidAsString, "D", out Guid guid)) + throw FormatException(ProjectTreeFormatError.GuidExpected, $"Expected GUID, but encountered '{guidAsString}'"); - if (!int.TryParse(idAsString, out int id)) - throw FormatException(ProjectTreeFormatError.IntegerExpected, $"Expected integer, but encountered '{idAsString}'"); + tokenizer.Skip(TokenType.WhiteSpace); - return new ProjectImageMoniker(guid, id); - } + string idAsString = tokenizer.ReadIdentifier(IdentifierParseOptions.Required); - private Tokenizer Tokenizer(ImmutableArray delimiters) - { - return new Tokenizer(_tokenizer.UnderlyingReader, delimiters); - } + if (!int.TryParse(idAsString, out int id)) + throw FormatException(ProjectTreeFormatError.IntegerExpected, $"Expected integer, but encountered '{idAsString}'"); + + return new ProjectImageMoniker(guid, id); + } + + private Tokenizer Tokenizer(ImmutableArray delimiters) + { + return new Tokenizer(_tokenizer.UnderlyingReader, delimiters); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/SimpleStringReader.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/SimpleStringReader.cs index 33457604db..ae5a597306 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/SimpleStringReader.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/SimpleStringReader.cs @@ -1,67 +1,66 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +// Simple, cheap, forward-only string reader +internal class SimpleStringReader { - // Simple, cheap, forward-only string reader - internal class SimpleStringReader - { - private readonly string _input; - private int _position; + private readonly string _input; + private int _position; - public SimpleStringReader(string input) - : this(input, 0) - { - } + public SimpleStringReader(string input) + : this(input, 0) + { + } - private SimpleStringReader(string input, int startIndex) - { - Assumes.NotNull(input); - Assumes.True(input.Length > 0); - Assumes.True(startIndex >= 0); - Assumes.True(startIndex <= input.Length); + private SimpleStringReader(string input, int startIndex) + { + Assumes.NotNull(input); + Assumes.True(input.Length > 0); + Assumes.True(startIndex >= 0); + Assumes.True(startIndex <= input.Length); - _input = input; - _position = startIndex; - } + _input = input; + _position = startIndex; + } - public bool CanRead + public bool CanRead + { + get { - get + if (_position < _input.Length) { - if (_position < _input.Length) - { - // Treat null as end of string - return PeekChar() != '\0'; - } - - return false; + // Treat null as end of string + return PeekChar() != '\0'; } + + return false; } + } - public char Peek() - { - Assumes.True(CanRead); + public char Peek() + { + Assumes.True(CanRead); - return PeekChar(); - } + return PeekChar(); + } - public char Read() - { - char c = Peek(); + public char Read() + { + char c = Peek(); - _position++; + _position++; - return c; - } + return c; + } - private char PeekChar() - { - return _input[_position]; - } + private char PeekChar() + { + return _input[_position]; + } - public SimpleStringReader Clone() - { - return new SimpleStringReader(_input, _position); - } + public SimpleStringReader Clone() + { + return new SimpleStringReader(_input, _position); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.IdentifierParseOptions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.IdentifierParseOptions.cs index ac73da30b2..60366f2820 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.IdentifierParseOptions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.IdentifierParseOptions.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class Tokenizer { - internal partial class Tokenizer + public enum IdentifierParseOptions { - public enum IdentifierParseOptions - { - None, - Required, - } + None, + Required, } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.Token.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.Token.cs index c48253dc87..e0a27678fa 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.Token.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.Token.cs @@ -1,58 +1,57 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class Tokenizer { - internal partial class Tokenizer + // Represents a self-contained unit within a tokenized string + private readonly struct Token { - // Represents a self-contained unit within a tokenized string - private readonly struct Token - { - private readonly char _value; - private readonly bool _isDelimiter; + private readonly char _value; + private readonly bool _isDelimiter; - private Token(char value, bool isDelimiter) - { - Assumes.True(value != '\0'); + private Token(char value, bool isDelimiter) + { + Assumes.True(value != '\0'); - _value = value; - _isDelimiter = isDelimiter; - } + _value = value; + _isDelimiter = isDelimiter; + } - public bool IsDelimiter - { - get { return _isDelimiter; } - } + public bool IsDelimiter + { + get { return _isDelimiter; } + } - public bool IsLiteral - { - get { return !_isDelimiter; } - } + public bool IsLiteral + { + get { return !_isDelimiter; } + } - public char Value - { - get { return _value; } - } + public char Value + { + get { return _value; } + } - public TokenType TokenType + public TokenType TokenType + { + get { - get - { - if (IsDelimiter) - return (TokenType)_value; + if (IsDelimiter) + return (TokenType)_value; - return TokenType.Literal; - } + return TokenType.Literal; } + } - public static Token Literal(char value) - { - return new Token(value, isDelimiter: false); - } + public static Token Literal(char value) + { + return new Token(value, isDelimiter: false); + } - public static Token Delimiter(char value) - { - return new Token(value, isDelimiter: true); - } + public static Token Delimiter(char value) + { + return new Token(value, isDelimiter: true); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.TokenType.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.TokenType.cs index 46100d69ed..e11b378d36 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.TokenType.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.TokenType.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class Tokenizer { - internal partial class Tokenizer + internal enum TokenType { - internal enum TokenType - { - Literal = 0, - LeftParenthesis = '(', - RightParenthesis = ')', - LeftBrace = '{', - RightBrace = '}', - Colon = ':', - Comma = ',', - WhiteSpace = ' ', - Quote = '"', - CarriageReturn = '\r', - NewLine = '\n', - } + Literal = 0, + LeftParenthesis = '(', + RightParenthesis = ')', + LeftBrace = '{', + RightBrace = '}', + Colon = ':', + Comma = ',', + WhiteSpace = ' ', + Quote = '"', + CarriageReturn = '\r', + NewLine = '\n', } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.cs index f2d92c001b..378d56c6da 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeParser/Tokenizer.cs @@ -2,169 +2,168 @@ using System.Text; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class Tokenizer { - internal partial class Tokenizer - { - private readonly SimpleStringReader _reader; - private readonly ImmutableArray _delimiters; + private readonly SimpleStringReader _reader; + private readonly ImmutableArray _delimiters; - public Tokenizer(SimpleStringReader reader, ImmutableArray delimiters) - { - Assumes.NotNull(reader); + public Tokenizer(SimpleStringReader reader, ImmutableArray delimiters) + { + Assumes.NotNull(reader); - _reader = reader; - _delimiters = delimiters; - } + _reader = reader; + _delimiters = delimiters; + } - public SimpleStringReader UnderlyingReader - { - get { return _reader; } - } + public SimpleStringReader UnderlyingReader + { + get { return _reader; } + } - public TokenType? Peek() - { - Token? token = PeekToken(); + public TokenType? Peek() + { + Token? token = PeekToken(); - return token?.TokenType; - } + return token?.TokenType; + } - public void Skip(TokenType expected) + public void Skip(TokenType expected) + { + Token? token = ReadToken(); + if (token == null) { - Token? token = ReadToken(); - if (token == null) - { - throw FormatException(ProjectTreeFormatError.DelimiterExpected_EncounteredEndOfString, $"Expected '{(char)expected}' but encountered end of string."); - } - - Token t = token.Value; - - if (t.TokenType != expected) - { - throw FormatException(ProjectTreeFormatError.DelimiterExpected, $"Expected '{(char)expected}' but encountered '{t.Value}'."); - } + throw FormatException(ProjectTreeFormatError.DelimiterExpected_EncounteredEndOfString, $"Expected '{(char)expected}' but encountered end of string."); } - public bool SkipIf(TokenType expected) - { - if (Peek() != expected) - return false; - - Skip(expected); - return true; - } + Token t = token.Value; - public void Close() + if (t.TokenType != expected) { - Token? token = ReadToken(); - if (token != null) - throw FormatException(ProjectTreeFormatError.EndOfStringExpected, $"Expected end-of-string, but encountered '{token.Value.Value}'."); + throw FormatException(ProjectTreeFormatError.DelimiterExpected, $"Expected '{(char)expected}' but encountered '{t.Value}'."); } + } - public string ReadIdentifier(IdentifierParseOptions options) - { - string identifier = ReadIdentifierCore(); - - CheckIdentifier(identifier, options); + public bool SkipIf(TokenType expected) + { + if (Peek() != expected) + return false; - identifier = identifier.TrimEnd((char)TokenType.WhiteSpace); - CheckIdentifierAfterTrim(identifier, options); + Skip(expected); + return true; + } - return identifier; - } + public void Close() + { + Token? token = ReadToken(); + if (token != null) + throw FormatException(ProjectTreeFormatError.EndOfStringExpected, $"Expected end-of-string, but encountered '{token.Value.Value}'."); + } - private string ReadIdentifierCore() - { - var identifier = new StringBuilder(); + public string ReadIdentifier(IdentifierParseOptions options) + { + string identifier = ReadIdentifierCore(); - Token? token; - while ((token = PeekToken()) != null) - { - Token t = token.Value; + CheckIdentifier(identifier, options); - if (t.IsDelimiter) - break; + identifier = identifier.TrimEnd((char)TokenType.WhiteSpace); + CheckIdentifierAfterTrim(identifier, options); - ReadToken(); - identifier.Append(t.Value); - } + return identifier; + } - return identifier.ToString(); - } + private string ReadIdentifierCore() + { + var identifier = new StringBuilder(); - private void CheckIdentifier(string identifier, IdentifierParseOptions options) + Token? token; + while ((token = PeekToken()) != null) { - if (IsValidIdentifier(identifier, options)) - return; - - // Are we at the end of the string? - Token? token = ReadToken(); // Consume token, so "position" is correct - if (token == null) - { - throw FormatException(ProjectTreeFormatError.IdExpected_EncounteredEndOfString, "Expected identifier, but encountered end-of-string."); - } - - // Otherwise, we must have hit a delimiter as whitespace will have been consumed as part of the identifier - throw FormatException(ProjectTreeFormatError.IdExpected_EncounteredDelimiter, $"Expected identifier, but encountered '{token.Value.Value}'."); - } + Token t = token.Value; - private static void CheckIdentifierAfterTrim(string identifier, IdentifierParseOptions options) - { - if (!IsValidIdentifier(identifier, options)) - throw FormatException(ProjectTreeFormatError.IdExpected_EncounteredOnlyWhiteSpace, "Expected identifier, but encountered only white space."); + if (t.IsDelimiter) + break; + + ReadToken(); + identifier.Append(t.Value); } - private static bool IsValidIdentifier(string identifier, IdentifierParseOptions options) - { - if ((options & IdentifierParseOptions.Required) == IdentifierParseOptions.Required) - { - return identifier.Length != 0; - } + return identifier.ToString(); + } - return true; - } + private void CheckIdentifier(string identifier, IdentifierParseOptions options) + { + if (IsValidIdentifier(identifier, options)) + return; - private Token? PeekToken() + // Are we at the end of the string? + Token? token = ReadToken(); // Consume token, so "position" is correct + if (token == null) { - SimpleStringReader reader = _reader.Clone(); - - return GetTokenFrom(reader); + throw FormatException(ProjectTreeFormatError.IdExpected_EncounteredEndOfString, "Expected identifier, but encountered end-of-string."); } - private Token? ReadToken() + // Otherwise, we must have hit a delimiter as whitespace will have been consumed as part of the identifier + throw FormatException(ProjectTreeFormatError.IdExpected_EncounteredDelimiter, $"Expected identifier, but encountered '{token.Value.Value}'."); + } + + private static void CheckIdentifierAfterTrim(string identifier, IdentifierParseOptions options) + { + if (!IsValidIdentifier(identifier, options)) + throw FormatException(ProjectTreeFormatError.IdExpected_EncounteredOnlyWhiteSpace, "Expected identifier, but encountered only white space."); + } + + private static bool IsValidIdentifier(string identifier, IdentifierParseOptions options) + { + if ((options & IdentifierParseOptions.Required) == IdentifierParseOptions.Required) { - return GetTokenFrom(_reader); + return identifier.Length != 0; } - private Token? GetTokenFrom(SimpleStringReader reader) - { - if (reader.CanRead) - { - return GetToken(reader.Read()); - } + return true; + } - return null; - } + private Token? PeekToken() + { + SimpleStringReader reader = _reader.Clone(); - private Token GetToken(char c) - { - if (IsDelimiter(c)) - { - return Token.Delimiter(c); - } + return GetTokenFrom(reader); + } - // Otherwise, must be a literal - return Token.Literal(c); - } + private Token? ReadToken() + { + return GetTokenFrom(_reader); + } - private bool IsDelimiter(char c) + private Token? GetTokenFrom(SimpleStringReader reader) + { + if (reader.CanRead) { - return _delimiters.Contains((TokenType)c); + return GetToken(reader.Read()); } - internal static FormatException FormatException(ProjectTreeFormatError errorId, string message) + return null; + } + + private Token GetToken(char c) + { + if (IsDelimiter(c)) { - return new ProjectTreeFormatException(message, errorId); + return Token.Delimiter(c); } + + // Otherwise, must be a literal + return Token.Literal(c); + } + + private bool IsDelimiter(char c) + { + return _delimiters.Contains((TokenType)c); + } + + internal static FormatException FormatException(ProjectTreeFormatError errorId, string message) + { + return new ProjectTreeFormatException(message, errorId); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreePropertiesProviderExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreePropertiesProviderExtensions.cs index b836a1c067..fdb1eaa9e6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreePropertiesProviderExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreePropertiesProviderExtensions.cs @@ -1,113 +1,112 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ProjectTreePropertiesProviderExtensions { - internal static class ProjectTreePropertiesProviderExtensions + /// + /// Visits the entire tree, calling + /// for every node. + /// + public static IProjectTree ChangePropertyValuesForEntireTree(this IProjectTreePropertiesProvider propertiesProvider, IProjectTree tree, IImmutableDictionary? projectTreeSettings = null) { - /// - /// Visits the entire tree, calling - /// for every node. - /// - public static IProjectTree ChangePropertyValuesForEntireTree(this IProjectTreePropertiesProvider propertiesProvider, IProjectTree tree, IImmutableDictionary? projectTreeSettings = null) - { - // Cheat here, because the IProjectTree that we get from ProjectTreeParser is mutable, we want to clone it - // so that any properties providers changes don't affect the "original" tree. If we implemented a completely - // immutable tree, then we wouldn't have to do that - but that's currently a lot of work for test-only purposes. - string treeAsString = ProjectTreeWriter.WriteToString(tree); + // Cheat here, because the IProjectTree that we get from ProjectTreeParser is mutable, we want to clone it + // so that any properties providers changes don't affect the "original" tree. If we implemented a completely + // immutable tree, then we wouldn't have to do that - but that's currently a lot of work for test-only purposes. + string treeAsString = ProjectTreeWriter.WriteToString(tree); - return ChangePropertyValuesWalkingTree(propertiesProvider, ProjectTreeParser.Parse(treeAsString), projectTreeSettings); - } + return ChangePropertyValuesWalkingTree(propertiesProvider, ProjectTreeParser.Parse(treeAsString), projectTreeSettings); + } - private static IProjectTree ChangePropertyValuesWalkingTree(IProjectTreePropertiesProvider propertiesProvider, IProjectTree tree, IImmutableDictionary? projectTreeSettings) + private static IProjectTree ChangePropertyValuesWalkingTree(IProjectTreePropertiesProvider propertiesProvider, IProjectTree tree, IImmutableDictionary? projectTreeSettings) + { + tree = ChangePropertyValues(propertiesProvider, tree, projectTreeSettings); + + foreach (IProjectTree child in tree.Children) { - tree = ChangePropertyValues(propertiesProvider, tree, projectTreeSettings); + tree = ChangePropertyValuesWalkingTree(propertiesProvider, child, projectTreeSettings).Parent!; + } - foreach (IProjectTree child in tree.Children) - { - tree = ChangePropertyValuesWalkingTree(propertiesProvider, child, projectTreeSettings).Parent!; - } + return tree; + } - return tree; - } + private static IProjectTree ChangePropertyValues(IProjectTreePropertiesProvider propertiesProvider, IProjectTree child, IImmutableDictionary? projectTreeSettings) + { + var propertyContextAndValues = new ProjectTreeCustomizablePropertyContextAndValues(child, projectTreeSettings); - private static IProjectTree ChangePropertyValues(IProjectTreePropertiesProvider propertiesProvider, IProjectTree child, IImmutableDictionary? projectTreeSettings) - { - var propertyContextAndValues = new ProjectTreeCustomizablePropertyContextAndValues(child, projectTreeSettings); + propertiesProvider.CalculatePropertyValues(propertyContextAndValues, propertyContextAndValues); - propertiesProvider.CalculatePropertyValues(propertyContextAndValues, propertyContextAndValues); + return propertyContextAndValues.Tree; + } - return propertyContextAndValues.Tree; - } + private class ProjectTreeCustomizablePropertyContextAndValues : IProjectTreeCustomizablePropertyValues, IProjectTreeCustomizablePropertyContext + { + private IProjectTree _tree; - private class ProjectTreeCustomizablePropertyContextAndValues : IProjectTreeCustomizablePropertyValues, IProjectTreeCustomizablePropertyContext + public ProjectTreeCustomizablePropertyContextAndValues(IProjectTree tree, IImmutableDictionary? projectTreeSettings) { - private IProjectTree _tree; + _tree = tree; + ProjectTreeSettings = projectTreeSettings ?? ImmutableDictionary.Empty; + } - public ProjectTreeCustomizablePropertyContextAndValues(IProjectTree tree, IImmutableDictionary? projectTreeSettings) - { - _tree = tree; - ProjectTreeSettings = projectTreeSettings ?? ImmutableDictionary.Empty; - } + public IProjectTree Tree + { + get { return _tree; } + } - public IProjectTree Tree - { - get { return _tree; } - } + public ProjectImageMoniker? ExpandedIcon + { + get { return _tree.ExpandedIcon; } + set { _tree = _tree.SetExpandedIcon(value); } + } - public ProjectImageMoniker? ExpandedIcon - { - get { return _tree.ExpandedIcon; } - set { _tree = _tree.SetExpandedIcon(value); } - } + public ProjectTreeFlags Flags + { + get { return _tree.Flags; } + set { _tree = _tree.SetFlags(value); } + } - public ProjectTreeFlags Flags - { - get { return _tree.Flags; } - set { _tree = _tree.SetFlags(value); } - } + public ProjectImageMoniker? Icon + { + get { return _tree.Icon; } + set { _tree = _tree.SetIcon(value); } + } + public bool ExistsOnDisk + { + get { return _tree.Flags.ContainsAny(ProjectTreeFlags.Common.FileOnDisk | ProjectTreeFlags.Common.Folder); } + } - public ProjectImageMoniker? Icon - { - get { return _tree.Icon; } - set { _tree = _tree.SetIcon(value); } - } - public bool ExistsOnDisk - { - get { return _tree.Flags.ContainsAny(ProjectTreeFlags.Common.FileOnDisk | ProjectTreeFlags.Common.Folder); } - } + public string ItemName + { + get { return _tree.Caption; } + } - public string ItemName - { - get { return _tree.Caption; } - } + public string ItemType + { + get { throw new NotImplementedException(); } + } - public string ItemType - { - get { throw new NotImplementedException(); } - } + public IImmutableDictionary Metadata + { + get { throw new NotImplementedException(); } + } - public IImmutableDictionary Metadata + public ProjectTreeFlags ParentNodeFlags + { + get { - get { throw new NotImplementedException(); } - } + IProjectTree? parent = _tree.Parent; + if (parent is null) + return ProjectTreeFlags.Empty; - public ProjectTreeFlags ParentNodeFlags - { - get - { - IProjectTree? parent = _tree.Parent; - if (parent is null) - return ProjectTreeFlags.Empty; - - return parent.Flags; - } + return parent.Flags; } + } - public IImmutableDictionary ProjectTreeSettings { get; } + public IImmutableDictionary ProjectTreeSettings { get; } - public bool IsFolder => _tree.Flags.Contains(ProjectTreeFlags.Common.Folder); + public bool IsFolder => _tree.Flags.Contains(ProjectTreeFlags.Common.Folder); - public bool IsNonFileSystemProjectItem => _tree.Flags.Contains(ProjectTreeFlags.Common.NonFileSystemProjectItem); - } + public bool IsNonFileSystemProjectItem => _tree.Flags.Contains(ProjectTreeFlags.Common.NonFileSystemProjectItem); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeProvider.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeProvider.cs index 766657d7e4..33e88d9526 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeProvider.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeProvider.cs @@ -2,98 +2,97 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal class ProjectTreeProvider : IProjectTreeProvider { - internal class ProjectTreeProvider : IProjectTreeProvider + public IReceivableSourceBlock> Tree { - public IReceivableSourceBlock> Tree - { - get { throw new NotImplementedException(); } - } + get { throw new NotImplementedException(); } + } - public IReceivableSourceBlock> SourceBlock - { - get { throw new NotImplementedException(); } - } + public IReceivableSourceBlock> SourceBlock + { + get { throw new NotImplementedException(); } + } - public NamedIdentity DataSourceKey - { - get { throw new NotImplementedException(); } - } + public NamedIdentity DataSourceKey + { + get { throw new NotImplementedException(); } + } - public IComparable DataSourceVersion - { - get { throw new NotImplementedException(); } - } + public IComparable DataSourceVersion + { + get { throw new NotImplementedException(); } + } - ISourceBlock> IProjectValueDataSource.SourceBlock - { - get { throw new NotImplementedException(); } - } + ISourceBlock> IProjectValueDataSource.SourceBlock + { + get { throw new NotImplementedException(); } + } - public bool CanCopy(IImmutableSet nodes, IProjectTree? receiver, bool deleteOriginal = false) - { - throw new NotImplementedException(); - } + public bool CanCopy(IImmutableSet nodes, IProjectTree? receiver, bool deleteOriginal = false) + { + throw new NotImplementedException(); + } - public bool CanRemove(IImmutableSet nodes, DeleteOptions deleteOptions = DeleteOptions.None) - { - throw new NotImplementedException(); - } + public bool CanRemove(IImmutableSet nodes, DeleteOptions deleteOptions = DeleteOptions.None) + { + throw new NotImplementedException(); + } - public Task CanRenameAsync(IProjectTree node) - { - throw new NotImplementedException(); - } + public Task CanRenameAsync(IProjectTree node) + { + throw new NotImplementedException(); + } - public IProjectTree? FindByPath(IProjectTree root, string path) + public IProjectTree? FindByPath(IProjectTree root, string path) + { + Requires.NotNull(root); + Requires.NotNullOrEmpty(path); + + foreach (IProjectTree child in root.GetSelfAndDescendentsBreadthFirst()) { - Requires.NotNull(root); - Requires.NotNullOrEmpty(path); + if (StringComparer.CurrentCultureIgnoreCase.Equals(child.FilePath, path)) + return child; + } - foreach (IProjectTree child in root.GetSelfAndDescendentsBreadthFirst()) - { - if (StringComparer.CurrentCultureIgnoreCase.Equals(child.FilePath, path)) - return child; - } + return null; + } + public string? GetAddNewItemDirectory(IProjectTree target) + { + if (target.Flags.Contains(ProjectTreeFlags.Common.DisableAddItemFolder)) return null; - } - public string? GetAddNewItemDirectory(IProjectTree target) - { - if (target.Flags.Contains(ProjectTreeFlags.Common.DisableAddItemFolder)) - return null; + if (target.IsRoot()) + return string.Empty; - if (target.IsRoot()) - return string.Empty; + return Path.Combine(GetAddNewItemDirectory(target.Parent!), target.Caption); + } - return Path.Combine(GetAddNewItemDirectory(target.Parent!), target.Caption); - } + public string? GetPath(IProjectTree node) + { + return node.FilePath; + } - public string? GetPath(IProjectTree node) - { - return node.FilePath; - } + public IDisposable Join() + { + throw new NotImplementedException(); + } - public IDisposable Join() + public Task RemoveAsync(IImmutableSet nodes, DeleteOptions deleteOptions = DeleteOptions.None) + { + foreach (IProjectTree node in nodes) { - throw new NotImplementedException(); + node.Parent?.Remove(node); } - public Task RemoveAsync(IImmutableSet nodes, DeleteOptions deleteOptions = DeleteOptions.None) - { - foreach (IProjectTree node in nodes) - { - node.Parent?.Remove(node); - } - - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task RenameAsync(IProjectTree node, string value) - { - throw new NotImplementedException(); - } + public Task RenameAsync(IProjectTree node, string value) + { + throw new NotImplementedException(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriter.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriter.cs index 1138bc93dc..58cba215c2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriter.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriter.cs @@ -2,230 +2,229 @@ using System.Text; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal class ProjectTreeWriter { - internal class ProjectTreeWriter + private readonly StringBuilder _builder = new(); + private readonly ProjectTreeWriterOptions _options; + private readonly IProjectTree _parent; + + public ProjectTreeWriter(IProjectTree tree, ProjectTreeWriterOptions options) { - private readonly StringBuilder _builder = new(); - private readonly ProjectTreeWriterOptions _options; - private readonly IProjectTree _parent; + Requires.NotNull(tree); - public ProjectTreeWriter(IProjectTree tree, ProjectTreeWriterOptions options) - { - Requires.NotNull(tree); + _parent = tree; + _options = options; + } - _parent = tree; - _options = options; - } + private bool TagElements + { + get { return (_options & ProjectTreeWriterOptions.Tags) == ProjectTreeWriterOptions.Tags; } + } - private bool TagElements - { - get { return (_options & ProjectTreeWriterOptions.Tags) == ProjectTreeWriterOptions.Tags; } - } + public static string WriteToString(IProjectTree tree) + { + var writer = new ProjectTreeWriter(tree, ProjectTreeWriterOptions.AllProperties); + return writer.WriteToString(); + } - public static string WriteToString(IProjectTree tree) - { - var writer = new ProjectTreeWriter(tree, ProjectTreeWriterOptions.AllProperties); - return writer.WriteToString(); - } + public string WriteToString() + { + _builder.Clear(); - public string WriteToString() - { - _builder.Clear(); + WriteProjectItem(_parent); - WriteProjectItem(_parent); + return _builder.ToString(); + } - return _builder.ToString(); - } + private void WriteProjectItem(IProjectTree tree, int indentLevel = 0) + { + WriteIndentLevel(indentLevel); + WriteCaption(tree); + WriteProperties(tree); + WriteFilePath(tree); + WriteItemType(tree); + WriteSubType(tree); + WriteIcons(tree); + WriteChildren(tree, indentLevel); + } - private void WriteProjectItem(IProjectTree tree, int indentLevel = 0) + private void WriteChildren(IProjectTree tree, int indentLevel) + { + foreach (IProjectTree child in tree.Children) { - WriteIndentLevel(indentLevel); - WriteCaption(tree); - WriteProperties(tree); - WriteFilePath(tree); - WriteItemType(tree); - WriteSubType(tree); - WriteIcons(tree); - WriteChildren(tree, indentLevel); + _builder.AppendLine(); + WriteProjectItem(child, indentLevel + 1); } + } - private void WriteChildren(IProjectTree tree, int indentLevel) + private void WriteIndentLevel(int indentLevel) + { + if (TagElements) { - foreach (IProjectTree child in tree.Children) + for (int i = 0; i < indentLevel; i++) { - _builder.AppendLine(); - WriteProjectItem(child, indentLevel + 1); + _builder.Append("[indent]"); } } - - private void WriteIndentLevel(int indentLevel) + else { - if (TagElements) - { - for (int i = 0; i < indentLevel; i++) - { - _builder.Append("[indent]"); - } - } - else - { - _builder.Append(' ', indentLevel * 4); - } + _builder.Append(' ', indentLevel * 4); } + } - private void WriteCaption(IProjectTree tree) - { - _builder.Append(tree.Caption); + private void WriteCaption(IProjectTree tree) + { + _builder.Append(tree.Caption); - if (TagElements) - { - _builder.Append("[caption]"); - } + if (TagElements) + { + _builder.Append("[caption]"); } + } - private void WriteProperties(IProjectTree tree) - { - bool visibility = _options.HasFlag(ProjectTreeWriterOptions.Visibility); - bool flags = _options.HasFlag(ProjectTreeWriterOptions.Flags); + private void WriteProperties(IProjectTree tree) + { + bool visibility = _options.HasFlag(ProjectTreeWriterOptions.Visibility); + bool flags = _options.HasFlag(ProjectTreeWriterOptions.Flags); - if (!visibility && !flags) - return; + if (!visibility && !flags) + return; - _builder.Append(' '); - _builder.Append('('); + _builder.Append(' '); + _builder.Append('('); + if (visibility) + { + WriteVisibility(tree); + } + + if (flags) + { if (visibility) { - WriteVisibility(tree); + _builder.Append(", "); } - if (flags) - { - if (visibility) - { - _builder.Append(", "); - } + WriteFlags(tree); + } - WriteFlags(tree); - } + _builder.Append(')'); + } - _builder.Append(')'); - } + private void WriteFilePath(IProjectTree tree) + { + if (!_options.HasFlag(ProjectTreeWriterOptions.FilePath)) + return; + + _builder.Append(", FilePath: "); + _builder.Append('"'); + _builder.Append(tree.FilePath); - private void WriteFilePath(IProjectTree tree) + if (TagElements) { - if (!_options.HasFlag(ProjectTreeWriterOptions.FilePath)) - return; + _builder.Append("[filepath]"); + } - _builder.Append(", FilePath: "); - _builder.Append('"'); - _builder.Append(tree.FilePath); + _builder.Append('"'); + } + + private void WriteItemType(IProjectTree tree) + { + if (!_options.HasFlag(ProjectTreeWriterOptions.ItemType)) + return; + + if (tree is IProjectItemTree item) + { + _builder.Append(", ItemType: "); + _builder.Append(item.Item?.ItemType); if (TagElements) { - _builder.Append("[filepath]"); + _builder.Append("[itemtype]"); } + } + } - _builder.Append('"'); + private void WriteSubType(IProjectTree tree) + { + if (!_options.HasFlag(ProjectTreeWriterOptions.SubType)) + return; + + if (tree.BrowseObjectProperties is not null) + { + _builder.Append(", SubType: "); + _builder.Append(tree.BrowseObjectProperties.GetPropertyValueAsync("SubType").Result); } - private void WriteItemType(IProjectTree tree) + if (TagElements) { - if (!_options.HasFlag(ProjectTreeWriterOptions.ItemType)) - return; + _builder.Append("[subtype]"); + } + } - if (tree is IProjectItemTree item) - { - _builder.Append(", ItemType: "); - _builder.Append(item.Item?.ItemType); + private void WriteVisibility(IProjectTree tree) + { + _builder.Append("visibility: "); - if (TagElements) - { - _builder.Append("[itemtype]"); - } - } + if (tree.Visible) + { + _builder.Append("visible"); } - - private void WriteSubType(IProjectTree tree) + else { - if (!_options.HasFlag(ProjectTreeWriterOptions.SubType)) - return; + _builder.Append("invisible"); + } + } - if (tree.BrowseObjectProperties is not null) - { - _builder.Append(", SubType: "); - _builder.Append(tree.BrowseObjectProperties.GetPropertyValueAsync("SubType").Result); - } + private void WriteFlags(IProjectTree tree) + { + _builder.Append("flags: "); + _builder.Append('{'); - if (TagElements) - { - _builder.Append("[subtype]"); - } - } + bool writtenCapability = false; - private void WriteVisibility(IProjectTree tree) + foreach (string capability in tree.Flags.OrderBy(c => c, StringComparer.InvariantCultureIgnoreCase)) { - _builder.Append("visibility: "); + if (writtenCapability) + _builder.Append(' '); - if (tree.Visible) - { - _builder.Append("visible"); - } - else + writtenCapability = true; + _builder.Append(capability); + + if (TagElements) { - _builder.Append("invisible"); + _builder.Append("[capability]"); } } - private void WriteFlags(IProjectTree tree) - { - _builder.Append("flags: "); - _builder.Append('{'); - - bool writtenCapability = false; - - foreach (string capability in tree.Flags.OrderBy(c => c, StringComparer.InvariantCultureIgnoreCase)) - { - if (writtenCapability) - _builder.Append(' '); + _builder.Append('}'); + } - writtenCapability = true; - _builder.Append(capability); + private void WriteIcons(IProjectTree tree) + { + if (!_options.HasFlag(ProjectTreeWriterOptions.Icons)) + return; - if (TagElements) - { - _builder.Append("[capability]"); - } - } + WriteIcon("Icon", tree.Icon); + WriteIcon("ExpandedIcon", tree.ExpandedIcon); + } - _builder.Append('}'); - } + private void WriteIcon(string name, ProjectImageMoniker? icon) + { + _builder.AppendFormat(", {0}: {{", name); - private void WriteIcons(IProjectTree tree) + if (icon is not null) { - if (!_options.HasFlag(ProjectTreeWriterOptions.Icons)) - return; - - WriteIcon("Icon", tree.Icon); - WriteIcon("ExpandedIcon", tree.ExpandedIcon); + _builder.AppendFormat("{1} {2}", name, icon.Guid.ToString("D").ToUpperInvariant(), icon.Id); } - private void WriteIcon(string name, ProjectImageMoniker? icon) + if (TagElements) { - _builder.AppendFormat(", {0}: {{", name); - - if (icon is not null) - { - _builder.AppendFormat("{1} {2}", name, icon.Guid.ToString("D").ToUpperInvariant(), icon.Id); - } - - if (TagElements) - { - _builder.Append("[icon]"); - } - - _builder.Append('}'); + _builder.Append("[icon]"); } + + _builder.Append('}'); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriterOptions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriterOptions.cs index 65bf72974b..14f2222f9f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriterOptions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/ProjectSystem/ProjectTreeWriterOptions.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +[Flags] +internal enum ProjectTreeWriterOptions { - [Flags] - internal enum ProjectTreeWriterOptions - { - None = 0, - Tags = 1, - FilePath = 2, - Flags = 4, - Visibility = 8, - Icons = 16, - ItemType = 32, - SubType = 64, - AllProperties = FilePath | Visibility | Flags | Icons | ItemType | SubType - } + None = 0, + Tags = 1, + FilePath = 2, + Flags = 4, + Visibility = 8, + Icons = 16, + ItemType = 32, + SubType = 64, + AllProperties = FilePath | Visibility | Flags | Icons | ItemType | SubType } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Threading/Tasks/TaskTimeoutExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Threading/Tasks/TaskTimeoutExtensions.cs index f284e4717f..a113a8372d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Threading/Tasks/TaskTimeoutExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Threading/Tasks/TaskTimeoutExtensions.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace System.Threading.Tasks +namespace System.Threading.Tasks; + +public static class TaskTimeoutExtensions { - public static class TaskTimeoutExtensions + public static async Task TimeoutAfter(this Task task, TimeSpan timeout) { - public static async Task TimeoutAfter(this Task task, TimeSpan timeout) - { - using var cts = new CancellationTokenSource(); - var timeoutTask = Task.Delay(timeout, cts.Token); - Task completedTask = await Task.WhenAny(task, timeoutTask); + using var cts = new CancellationTokenSource(); + var timeoutTask = Task.Delay(timeout, cts.Token); + Task completedTask = await Task.WhenAny(task, timeoutTask); - if (timeoutTask == completedTask) - throw new TimeoutException($"Task timed out after {timeout.TotalMilliseconds} ms"); + if (timeoutTask == completedTask) + throw new TimeoutException($"Task timed out after {timeout.TotalMilliseconds} ms"); - cts.Cancel(); - await task; - } + cts.Cancel(); + await task; + } - public static async Task TimeoutAfter(this Task task, TimeSpan timeout) - { - using var cts = new CancellationTokenSource(); - var timeoutTask = Task.Delay(timeout, cts.Token); - Task completedTask = await Task.WhenAny(task, timeoutTask); + public static async Task TimeoutAfter(this Task task, TimeSpan timeout) + { + using var cts = new CancellationTokenSource(); + var timeoutTask = Task.Delay(timeout, cts.Token); + Task completedTask = await Task.WhenAny(task, timeoutTask); - if (timeoutTask == completedTask) - throw new TimeoutException($"Task timed out after {timeout.TotalMilliseconds} ms"); + if (timeoutTask == completedTask) + throw new TimeoutException($"Task timed out after {timeout.TotalMilliseconds} ms"); - cts.Cancel(); - return await task; - } + cts.Cancel(); + return await task; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Workspaces/WorkspaceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Workspaces/WorkspaceFactory.cs index 8f985a68f6..e64c5af229 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Workspaces/WorkspaceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.TestServices/Workspaces/WorkspaceFactory.cs @@ -3,26 +3,25 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.VisualStudio.Workspaces +namespace Microsoft.VisualStudio.Workspaces; + +public static class WorkspaceFactory { - public static class WorkspaceFactory - { - private static readonly MetadataReference s_corlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); - private static readonly MetadataReference s_systemReference = MetadataReference.CreateFromFile(typeof(System.Diagnostics.Debug).Assembly.Location); - private static readonly MetadataReference s_systemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); + private static readonly MetadataReference s_corlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); + private static readonly MetadataReference s_systemReference = MetadataReference.CreateFromFile(typeof(System.Diagnostics.Debug).Assembly.Location); + private static readonly MetadataReference s_systemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); - public static Workspace Create(string text, string language = LanguageNames.CSharp) - { - var workspace = new AdhocWorkspace(); + public static Workspace Create(string text, string language = LanguageNames.CSharp) + { + var workspace = new AdhocWorkspace(); - var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "TestProject", "TestProject", language, - filePath: "D:\\Test.proj", - metadataReferences: new[] { s_corlibReference, s_systemCoreReference, s_systemReference }); - Project project = workspace.AddProject(projectInfo); + var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "TestProject", "TestProject", language, + filePath: "D:\\Test.proj", + metadataReferences: new[] { s_corlibReference, s_systemCoreReference, s_systemReference }); + Project project = workspace.AddProject(projectInfo); - workspace.AddDocument(project.Id, "TestDocument", SourceText.From(text)); + workspace.AddDocument(project.Id, "TestDocument", SourceText.From(text)); - return workspace; - } + return workspace; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Build/BuildUtilitiesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Build/BuildUtilitiesTests.cs index 3b555b4893..458108219d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Build/BuildUtilitiesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Build/BuildUtilitiesTests.cs @@ -2,298 +2,297 @@ using Microsoft.Build.Construction; -namespace Microsoft.VisualStudio.Build +namespace Microsoft.VisualStudio.Build; + +public class BuildUtilitiesTests { - public class BuildUtilitiesTests + [Fact] + public void GetProperty_MissingProperty() + { + string projectXml = + """ + + + MyPropertyValue + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + var property = BuildUtilities.GetProperty(project, "NonExistentProperty"); + Assert.Null(property); + } + + [Fact] + public void GetProperty_ExistentProperty() + { + string projectXml = + """ + + + MyPropertyValue + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + } + + [Fact] + public void GetPropertyValues_SingleValue() + { + var values = BuildUtilities.GetPropertyValues("MyPropertyValue"); + Assert.Collection(values, firstValue => Assert.Equal("MyPropertyValue", firstValue)); + } + + [Fact] + public void GetPropertyValues_MultipleValues() + { + var values = BuildUtilities.GetPropertyValues("1;2"); + Assert.Collection(values, + firstValue => Assert.Equal("1", firstValue), + secondValue => Assert.Equal("2", secondValue)); + } + + [Fact] + public void GetPropertyValues_EmptyValues() + { + var values = BuildUtilities.GetPropertyValues("1; ;;;2"); + Assert.Collection(values, + firstValue => Assert.Equal("1", firstValue), + secondValue => Assert.Equal("2", secondValue)); + } + + [Fact] + public void GetPropertyValues_WhiteSpace() + { + var values = BuildUtilities.GetPropertyValues(" 1; ; ; ; 2 "); + Assert.Collection(values, + firstValue => Assert.Equal("1", firstValue), + secondValue => Assert.Equal("2", secondValue)); + } + + [Fact] + public void GetPropertyValues_Duplicates() + { + var values = BuildUtilities.GetPropertyValues("1;2;1;1;2;2;2;1"); + Assert.Collection(values, + firstValue => Assert.Equal("1", firstValue), + secondValue => Assert.Equal("2", secondValue)); + } + + [Fact] + public void GetOrAddProperty_NoGroups() + { + var project = ProjectRootElementFactory.Create(); + BuildUtilities.GetOrAddProperty(project, "MyProperty"); + Assert.Single(project.Properties); + Assert.Collection(project.PropertyGroups, + group => Assert.Collection(group.Properties, + firstProperty => Assert.Equal(string.Empty, firstProperty.Value))); + } + + [Fact] + public void GetOrAddProperty_FirstGroup() + { + string projectXml = + """ + + + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.GetOrAddProperty(project, "MyProperty"); + Assert.Single(project.Properties); + AssertEx.CollectionLength(project.PropertyGroups, 2); + + var group = project.PropertyGroups.First(); + Assert.Single(group.Properties); + + var property = group.Properties.First(); + Assert.Equal(string.Empty, property.Value); + } + + [Fact] + public void GetOrAddProperty_ExistingProperty() + { + string projectXml = + """ + + + 1 + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.GetOrAddProperty(project, "MyProperty"); + Assert.Single(project.Properties); + Assert.Single(project.PropertyGroups); + + var group = project.PropertyGroups.First(); + Assert.Single(group.Properties); + + var property = group.Properties.First(); + Assert.Equal("1", property.Value); + } + + [Fact] + public void AppendPropertyValue_DefaultDelimiter() + { + string projectXml = + """ + + + 1;2 + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.AppendPropertyValue(project, "1;2", "MyProperty", "3"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("1;2;3", property.Value); + } + + [Fact] + public void AppendPropertyValue_EmptyProperty() + { + string projectXml = + """ + + + + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.AppendPropertyValue(project, "", "MyProperty", "1"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("1", property.Value); + } + + [Fact] + public void AppendPropertyValue_InheritedValue() + { + var project = ProjectRootElementFactory.Create(); + BuildUtilities.AppendPropertyValue(project, "1;2", "MyProperty", "3"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("1;2;3", property.Value); + } + + [Fact] + public void AppendPropertyValue_MissingProperty() + { + var project = ProjectRootElementFactory.Create(); + BuildUtilities.AppendPropertyValue(project, "", "MyProperty", "1"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("1", property.Value); + } + + [Fact] + public void RemovePropertyValue_DefaultDelimiter() + { + string projectXml = + """ + + + 1;2 + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.RemovePropertyValue(project, "1;2", "MyProperty", "2"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("1", property.Value); + } + + [Fact] + public void RemovePropertyValue_EmptyAfterRemove() + { + string projectXml = + """ + + + 1 + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.RemovePropertyValue(project, "1", "MyProperty", "1"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal(string.Empty, property.Value); + } + + [Fact] + public void RemovePropertyValue_InheritedValue() + { + var project = ProjectRootElementFactory.Create(); + BuildUtilities.RemovePropertyValue(project, "1;2", "MyProperty", "1"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("2", property.Value); + } + + [Fact] + public void RemovePropertyValue_MissingProperty() + { + var project = ProjectRootElementFactory.Create(); + Assert.Throws("valueToRemove", () => BuildUtilities.RemovePropertyValue(project, "", "MyProperty", "1")); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal(string.Empty, property.Value); + } + + [Fact] + public void RenamePropertyValue_DefaultDelimiter() + { + string projectXml = + """ + + + 1;2 + + + """; + + var project = ProjectRootElementFactory.Create(projectXml); + BuildUtilities.RenamePropertyValue(project, "1;2", "MyProperty", "2", "5"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("1;5", property.Value); + } + + [Fact] + public void RenamePropertyValue_InheritedValue() + { + var project = ProjectRootElementFactory.Create(); + BuildUtilities.RenamePropertyValue(project, "1;2", "MyProperty", "1", "3"); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal("3;2", property.Value); + } + + [Fact] + public void RenamePropertyValue_MissingProperty() { - [Fact] - public void GetProperty_MissingProperty() - { - string projectXml = - """ - - - MyPropertyValue - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - var property = BuildUtilities.GetProperty(project, "NonExistentProperty"); - Assert.Null(property); - } - - [Fact] - public void GetProperty_ExistentProperty() - { - string projectXml = - """ - - - MyPropertyValue - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - } - - [Fact] - public void GetPropertyValues_SingleValue() - { - var values = BuildUtilities.GetPropertyValues("MyPropertyValue"); - Assert.Collection(values, firstValue => Assert.Equal("MyPropertyValue", firstValue)); - } - - [Fact] - public void GetPropertyValues_MultipleValues() - { - var values = BuildUtilities.GetPropertyValues("1;2"); - Assert.Collection(values, - firstValue => Assert.Equal("1", firstValue), - secondValue => Assert.Equal("2", secondValue)); - } - - [Fact] - public void GetPropertyValues_EmptyValues() - { - var values = BuildUtilities.GetPropertyValues("1; ;;;2"); - Assert.Collection(values, - firstValue => Assert.Equal("1", firstValue), - secondValue => Assert.Equal("2", secondValue)); - } - - [Fact] - public void GetPropertyValues_WhiteSpace() - { - var values = BuildUtilities.GetPropertyValues(" 1; ; ; ; 2 "); - Assert.Collection(values, - firstValue => Assert.Equal("1", firstValue), - secondValue => Assert.Equal("2", secondValue)); - } - - [Fact] - public void GetPropertyValues_Duplicates() - { - var values = BuildUtilities.GetPropertyValues("1;2;1;1;2;2;2;1"); - Assert.Collection(values, - firstValue => Assert.Equal("1", firstValue), - secondValue => Assert.Equal("2", secondValue)); - } - - [Fact] - public void GetOrAddProperty_NoGroups() - { - var project = ProjectRootElementFactory.Create(); - BuildUtilities.GetOrAddProperty(project, "MyProperty"); - Assert.Single(project.Properties); - Assert.Collection(project.PropertyGroups, - group => Assert.Collection(group.Properties, - firstProperty => Assert.Equal(string.Empty, firstProperty.Value))); - } - - [Fact] - public void GetOrAddProperty_FirstGroup() - { - string projectXml = - """ - - - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.GetOrAddProperty(project, "MyProperty"); - Assert.Single(project.Properties); - AssertEx.CollectionLength(project.PropertyGroups, 2); - - var group = project.PropertyGroups.First(); - Assert.Single(group.Properties); - - var property = group.Properties.First(); - Assert.Equal(string.Empty, property.Value); - } - - [Fact] - public void GetOrAddProperty_ExistingProperty() - { - string projectXml = - """ - - - 1 - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.GetOrAddProperty(project, "MyProperty"); - Assert.Single(project.Properties); - Assert.Single(project.PropertyGroups); - - var group = project.PropertyGroups.First(); - Assert.Single(group.Properties); - - var property = group.Properties.First(); - Assert.Equal("1", property.Value); - } - - [Fact] - public void AppendPropertyValue_DefaultDelimiter() - { - string projectXml = - """ - - - 1;2 - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.AppendPropertyValue(project, "1;2", "MyProperty", "3"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("1;2;3", property.Value); - } - - [Fact] - public void AppendPropertyValue_EmptyProperty() - { - string projectXml = - """ - - - - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.AppendPropertyValue(project, "", "MyProperty", "1"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("1", property.Value); - } - - [Fact] - public void AppendPropertyValue_InheritedValue() - { - var project = ProjectRootElementFactory.Create(); - BuildUtilities.AppendPropertyValue(project, "1;2", "MyProperty", "3"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("1;2;3", property.Value); - } - - [Fact] - public void AppendPropertyValue_MissingProperty() - { - var project = ProjectRootElementFactory.Create(); - BuildUtilities.AppendPropertyValue(project, "", "MyProperty", "1"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("1", property.Value); - } - - [Fact] - public void RemovePropertyValue_DefaultDelimiter() - { - string projectXml = - """ - - - 1;2 - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.RemovePropertyValue(project, "1;2", "MyProperty", "2"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("1", property.Value); - } - - [Fact] - public void RemovePropertyValue_EmptyAfterRemove() - { - string projectXml = - """ - - - 1 - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.RemovePropertyValue(project, "1", "MyProperty", "1"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal(string.Empty, property.Value); - } - - [Fact] - public void RemovePropertyValue_InheritedValue() - { - var project = ProjectRootElementFactory.Create(); - BuildUtilities.RemovePropertyValue(project, "1;2", "MyProperty", "1"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("2", property.Value); - } - - [Fact] - public void RemovePropertyValue_MissingProperty() - { - var project = ProjectRootElementFactory.Create(); - Assert.Throws("valueToRemove", () => BuildUtilities.RemovePropertyValue(project, "", "MyProperty", "1")); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal(string.Empty, property.Value); - } - - [Fact] - public void RenamePropertyValue_DefaultDelimiter() - { - string projectXml = - """ - - - 1;2 - - - """; - - var project = ProjectRootElementFactory.Create(projectXml); - BuildUtilities.RenamePropertyValue(project, "1;2", "MyProperty", "2", "5"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("1;5", property.Value); - } - - [Fact] - public void RenamePropertyValue_InheritedValue() - { - var project = ProjectRootElementFactory.Create(); - BuildUtilities.RenamePropertyValue(project, "1;2", "MyProperty", "1", "3"); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal("3;2", property.Value); - } - - [Fact] - public void RenamePropertyValue_MissingProperty() - { - var project = ProjectRootElementFactory.Create(); - Assert.Throws("oldValue", () => BuildUtilities.RenamePropertyValue(project, "", "MyProperty", "1", "2")); - var property = BuildUtilities.GetProperty(project, "MyProperty"); - Assert.NotNull(property); - Assert.Equal(string.Empty, property.Value); - } + var project = ProjectRootElementFactory.Create(); + Assert.Throws("oldValue", () => BuildUtilities.RenamePropertyValue(project, "", "MyProperty", "1", "2")); + var property = BuildUtilities.GetProperty(project, "MyProperty"); + Assert.NotNull(property); + Assert.Equal(string.Empty, property.Value); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/IO/Win32FileSystemTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/IO/Win32FileSystemTests.cs index cb67a89945..2025a5718a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/IO/Win32FileSystemTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/IO/Win32FileSystemTests.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +public class Win32FileSystemTests { - public class Win32FileSystemTests + [Fact] + public void GetLastFileWriteTimeOrMinValueUtc_WhenPathDoesNotExist_ReturnsDateTimeMinValue() { - [Fact] - public void GetLastFileWriteTimeOrMinValueUtc_WhenPathDoesNotExist_ReturnsDateTimeMinValue() - { - var fileSystem = CreateInstance(); + var fileSystem = CreateInstance(); - DateTime result = fileSystem.GetLastFileWriteTimeOrMinValueUtc("This path does not exist!"); + DateTime result = fileSystem.GetLastFileWriteTimeOrMinValueUtc("This path does not exist!"); - Assert.Equal(result, DateTime.MinValue); - } + Assert.Equal(result, DateTime.MinValue); + } - [Fact] - public void TryGetLastFileWriteTimeUtc_WhenPathDoesNotExist_ReturnsFalse() - { - var fileSystem = CreateInstance(); + [Fact] + public void TryGetLastFileWriteTimeUtc_WhenPathDoesNotExist_ReturnsFalse() + { + var fileSystem = CreateInstance(); - bool value = fileSystem.TryGetLastFileWriteTimeUtc("This path does not exist!", out DateTime? result); + bool value = fileSystem.TryGetLastFileWriteTimeUtc("This path does not exist!", out DateTime? result); - Assert.False(value); - Assert.Null(result); - } + Assert.False(value); + Assert.Null(result); + } - private Win32FileSystem CreateInstance() - { - return new Win32FileSystem(); - } + private Win32FileSystem CreateInstance() + { + return new Win32FileSystem(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/AbstractMultiLifetimeComponentFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/AbstractMultiLifetimeComponentFactory.cs index 14cf7198ec..719e471bb6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/AbstractMultiLifetimeComponentFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/AbstractMultiLifetimeComponentFactory.cs @@ -2,58 +2,57 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class AbstractMultiLifetimeComponentFactory { - internal static class AbstractMultiLifetimeComponentFactory + public static MultiLifetimeComponent Create() + { + var joinableTaskContextNode = JoinableTaskContextNodeFactory.Create(); + + return new MultiLifetimeComponent(joinableTaskContextNode); + } + + public class MultiLifetimeComponent : AbstractMultiLifetimeComponent { - public static MultiLifetimeComponent Create() + public MultiLifetimeComponent(JoinableTaskContextNode joinableTaskContextNode) + : base(joinableTaskContextNode) { - var joinableTaskContextNode = JoinableTaskContextNodeFactory.Create(); + } - return new MultiLifetimeComponent(joinableTaskContextNode); + protected override MultiLifetimeInstance CreateInstance() + { + return new MultiLifetimeInstance(); } - public class MultiLifetimeComponent : AbstractMultiLifetimeComponent + public new bool IsInitialized { - public MultiLifetimeComponent(JoinableTaskContextNode joinableTaskContextNode) - : base(joinableTaskContextNode) - { - } + get { return base.IsInitialized; } + } - protected override MultiLifetimeInstance CreateInstance() - { - return new MultiLifetimeInstance(); - } + public new Task WaitForLoadedAsync(CancellationToken cancellationToken = default) + { + return base.WaitForLoadedAsync(cancellationToken); + } - public new bool IsInitialized - { - get { return base.IsInitialized; } - } + public class MultiLifetimeInstance : IMultiLifetimeInstance + { + public bool IsInitialized { get; private set; } - public new Task WaitForLoadedAsync(CancellationToken cancellationToken = default) - { - return base.WaitForLoadedAsync(cancellationToken); - } + public bool IsDisposed { get; private set; } - public class MultiLifetimeInstance : IMultiLifetimeInstance + public Task InitializeAsync() { - public bool IsInitialized { get; private set; } - - public bool IsDisposed { get; private set; } - - public Task InitializeAsync() - { - IsInitialized = true; + IsInitialized = true; - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task DisposeAsync() - { - IsDisposed = true; + public Task DisposeAsync() + { + IsDisposed = true; - return Task.CompletedTask; - } + return Task.CompletedTask; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectFactory.cs index 02116643ce..96f4304b6e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectFactory.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ConfiguredProjectFactory { - internal static class ConfiguredProjectFactory + public static ConfiguredProject Create(IProjectCapabilitiesScope? capabilities = null, ProjectConfiguration? projectConfiguration = null, ConfiguredProjectServices? services = null, UnconfiguredProject? unconfiguredProject = null) { - public static ConfiguredProject Create(IProjectCapabilitiesScope? capabilities = null, ProjectConfiguration? projectConfiguration = null, ConfiguredProjectServices? services = null, UnconfiguredProject? unconfiguredProject = null) - { - var mock = new Mock(); - mock.Setup(c => c.Capabilities).Returns(capabilities!); - mock.Setup(c => c.ProjectConfiguration).Returns(projectConfiguration!); - mock.Setup(c => c.Services).Returns(services!); - mock.SetupGet(c => c.UnconfiguredProject).Returns(unconfiguredProject ?? UnconfiguredProjectFactory.Create()); - return mock.Object; - } + var mock = new Mock(); + mock.Setup(c => c.Capabilities).Returns(capabilities!); + mock.Setup(c => c.ProjectConfiguration).Returns(projectConfiguration!); + mock.Setup(c => c.Services).Returns(services!); + mock.SetupGet(c => c.UnconfiguredProject).Returns(unconfiguredProject ?? UnconfiguredProjectFactory.Create()); + return mock.Object; + } - public static ConfiguredProject ImplementProjectConfiguration(string configuration) - { - return ImplementProjectConfiguration(ProjectConfigurationFactory.Create(configuration)); - } + public static ConfiguredProject ImplementProjectConfiguration(string configuration) + { + return ImplementProjectConfiguration(ProjectConfigurationFactory.Create(configuration)); + } - public static ConfiguredProject ImplementProjectConfiguration(ProjectConfiguration projectConfiguration) - { - return Create(projectConfiguration: projectConfiguration); - } + public static ConfiguredProject ImplementProjectConfiguration(ProjectConfiguration projectConfiguration) + { + return Create(projectConfiguration: projectConfiguration); + } - public static ConfiguredProject ImplementUnconfiguredProject(UnconfiguredProject project) - { - var mock = new Mock(); - mock.SetupGet(p => p.UnconfiguredProject) - .Returns(project); + public static ConfiguredProject ImplementUnconfiguredProject(UnconfiguredProject project) + { + var mock = new Mock(); + mock.SetupGet(p => p.UnconfiguredProject) + .Returns(project); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectServicesFactory.cs index 13e32202f2..3aae6b8a29 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ConfiguredProjectServicesFactory.cs @@ -3,33 +3,32 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.References; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ConfiguredProjectServicesFactory { - internal static class ConfiguredProjectServicesFactory + public static ConfiguredProjectServices Create(IPropertyPagesCatalogProvider? propertyPagesCatalogProvider = null, + IAdditionalRuleDefinitionsService? ruleService = null, + IProjectSubscriptionService? projectSubscriptionService = null, + IProjectPropertiesProvider? projectPropertiesProvider = null, + IProjectService? projectService = null, + IProjectThreadingService? threadingService = null, + IProjectAsynchronousTasksService? projectAsynchronousTasksService = null, + IBuildDependencyProjectReferencesService? projectReferences = null, + IPackageReferencesService? packageReferences = null, + IAssemblyReferencesService? assemblyReferences = null) { - public static ConfiguredProjectServices Create(IPropertyPagesCatalogProvider? propertyPagesCatalogProvider = null, - IAdditionalRuleDefinitionsService? ruleService = null, - IProjectSubscriptionService? projectSubscriptionService = null, - IProjectPropertiesProvider? projectPropertiesProvider = null, - IProjectService? projectService = null, - IProjectThreadingService? threadingService = null, - IProjectAsynchronousTasksService? projectAsynchronousTasksService = null, - IBuildDependencyProjectReferencesService? projectReferences = null, - IPackageReferencesService? packageReferences = null, - IAssemblyReferencesService? assemblyReferences = null) - { - var mock = new Mock(); - mock.Setup(c => c.PropertyPagesCatalog).Returns(propertyPagesCatalogProvider!); - mock.Setup(c => c.AdditionalRuleDefinitions).Returns(ruleService!); - mock.Setup(c => c.ProjectSubscription).Returns(projectSubscriptionService!); - mock.Setup(c => c.ProjectPropertiesProvider).Returns(projectPropertiesProvider!); - mock.Setup(c => c.ThreadingPolicy).Returns(threadingService!); - mock.Setup(c => c.ProjectAsynchronousTasks).Returns(projectAsynchronousTasksService!); - mock.SetupGet(s => s.ProjectService).Returns(projectService!); - mock.SetupGet(c => c.ProjectReferences).Returns(projectReferences!); - mock.SetupGet(c => c.PackageReferences).Returns(packageReferences!); - mock.SetupGet(c => c.AssemblyReferences).Returns(assemblyReferences!); - return mock.Object; - } + var mock = new Mock(); + mock.Setup(c => c.PropertyPagesCatalog).Returns(propertyPagesCatalogProvider!); + mock.Setup(c => c.AdditionalRuleDefinitions).Returns(ruleService!); + mock.Setup(c => c.ProjectSubscription).Returns(projectSubscriptionService!); + mock.Setup(c => c.ProjectPropertiesProvider).Returns(projectPropertiesProvider!); + mock.Setup(c => c.ThreadingPolicy).Returns(threadingService!); + mock.Setup(c => c.ProjectAsynchronousTasks).Returns(projectAsynchronousTasksService!); + mock.SetupGet(s => s.ProjectService).Returns(projectService!); + mock.SetupGet(c => c.ProjectReferences).Returns(projectReferences!); + mock.SetupGet(c => c.PackageReferences).Returns(packageReferences!); + mock.SetupGet(c => c.AssemblyReferences).Returns(assemblyReferences!); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ExportFactoryFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ExportFactoryFactory.cs index 232fe9bb5f..79a02a8689 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ExportFactoryFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ExportFactoryFactory.cs @@ -1,45 +1,44 @@ // 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.md file in the project root for more information. -namespace System.ComponentModel.Composition +namespace System.ComponentModel.Composition; + +internal static class ExportFactoryFactory { - internal static class ExportFactoryFactory + public static ExportFactory Implement(Func factory, Action? disposeAction = null) { - public static ExportFactory Implement(Func factory, Action? disposeAction = null) + return new ExportFactory(() => { - return new ExportFactory(() => - { - T value = factory(); + T value = factory(); - return Tuple.Create(value, disposeAction ?? delegate { }); - }); - } + return Tuple.Create(value, disposeAction ?? delegate { }); + }); + } - public static ExportFactory ImplementCreateValueWithAutoDispose(Func factory) + public static ExportFactory ImplementCreateValueWithAutoDispose(Func factory) + { + return new ExportFactory(() => { - return new ExportFactory(() => - { - T value = factory(); + T value = factory(); - return Tuple.Create(value, () => - { - if (value is IDisposable disposable) - disposable.Dispose(); - }); + return Tuple.Create(value, () => + { + if (value is IDisposable disposable) + disposable.Dispose(); }); - } + }); + } - public static ExportFactory ImplementCreateValueWithAutoDispose(Func factory, TMetadata metadata) + public static ExportFactory ImplementCreateValueWithAutoDispose(Func factory, TMetadata metadata) + { + return new ExportFactory(() => { - return new ExportFactory(() => - { - T value = factory(); + T value = factory(); - return Tuple.Create(value, () => - { - if (value is IDisposable disposable) - disposable.Dispose(); - }); - }, metadata); - } + return Tuple.Create(value, () => + { + if (value is IDisposable disposable) + disposable.Dispose(); + }); + }, metadata); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfigurationGroupServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfigurationGroupServiceFactory.cs index 9d8c9ad384..8921e0d7da 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfigurationGroupServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfigurationGroupServiceFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IActiveConfigurationGroupServiceFactory { - internal static class IActiveConfigurationGroupServiceFactory + public static IActiveConfigurationGroupService Implement(IProjectValueDataSource> source) { - public static IActiveConfigurationGroupService Implement(IProjectValueDataSource> source) - { - var mock = new Mock(); - mock.SetupGet(s => s.ActiveConfigurationGroupSource) - .Returns(source); + var mock = new Mock(); + mock.SetupGet(s => s.ActiveConfigurationGroupSource) + .Returns(source); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectFactory.cs index cf8e605575..2d8a9b7f96 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectFactory.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IActiveConfiguredValueFactory { - internal static class IActiveConfiguredValueFactory + public static IActiveConfiguredValue ImplementValue(Func action) + where T : class? { - public static IActiveConfiguredValue ImplementValue(Func action) - where T : class? - { - var mock = new Mock>(); + var mock = new Mock>(); - mock.SetupGet(p => p.Value) - .Returns(action); + mock.SetupGet(p => p.Value) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectProviderFactory.cs index f0060c44ef..e6dff19df0 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectProviderFactory.cs @@ -1,39 +1,38 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IActiveConfiguredProjectProviderFactory { - internal static class IActiveConfiguredProjectProviderFactory + public static IActiveConfiguredProjectProvider ImplementActiveProjectConfiguration(Func action) + { + var mock = new Mock(); + mock.SetupGet(p => p.ActiveProjectConfiguration) + .Returns(action); + + return mock.Object; + } + + public static IActiveConfiguredProjectProvider Create() { - public static IActiveConfiguredProjectProvider ImplementActiveProjectConfiguration(Func action) - { - var mock = new Mock(); - mock.SetupGet(p => p.ActiveProjectConfiguration) - .Returns(action); - - return mock.Object; - } - - public static IActiveConfiguredProjectProvider Create() - { - var mock = new Mock(); - mock.SetupGet(p => p.ActiveConfiguredProjectBlock) - .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); - - return mock.Object; - } - - public static IActiveConfiguredProjectProvider Create( - Func? getActiveProjectConfiguration = null, - Func? getActiveConfiguredProject = null) - { - var mock = new Mock(); - mock.SetupGet(p => p.ActiveProjectConfiguration) - .Returns(getActiveProjectConfiguration); - - mock.SetupGet(p => p.ActiveConfiguredProject) - .Returns(getActiveConfiguredProject); - - return mock.Object; - } + var mock = new Mock(); + mock.SetupGet(p => p.ActiveConfiguredProjectBlock) + .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); + + return mock.Object; + } + + public static IActiveConfiguredProjectProvider Create( + Func? getActiveProjectConfiguration = null, + Func? getActiveConfiguredProject = null) + { + var mock = new Mock(); + mock.SetupGet(p => p.ActiveProjectConfiguration) + .Returns(getActiveProjectConfiguration); + + mock.SetupGet(p => p.ActiveConfiguredProject) + .Returns(getActiveConfiguredProject); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsDimensionProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsDimensionProviderFactory.cs index 5bd23a3484..b44773215d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsDimensionProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsDimensionProviderFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +internal static class IActiveConfiguredProjectsDimensionProviderFactory { - internal static class IActiveConfiguredProjectsDimensionProviderFactory + public static IActiveConfiguredProjectsDimensionProvider ImplementDimensionName(string value) { - public static IActiveConfiguredProjectsDimensionProvider ImplementDimensionName(string value) - { - var mock = new Mock(); - mock.SetupGet(t => t.DimensionName) - .Returns(value); + var mock = new Mock(); + mock.SetupGet(t => t.DimensionName) + .Returns(value); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsProviderFactory.cs index f2f4ce36f2..e870eae457 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredProjectsProviderFactory.cs @@ -1,44 +1,43 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal class IActiveConfiguredProjectsProviderFactory { - internal class IActiveConfiguredProjectsProviderFactory - { - private readonly Mock _mock; + private readonly Mock _mock; - public IActiveConfiguredProjectsProviderFactory(MockBehavior mockBehavior = MockBehavior.Strict) - { - _mock = new Mock(mockBehavior); - } + public IActiveConfiguredProjectsProviderFactory(MockBehavior mockBehavior = MockBehavior.Strict) + { + _mock = new Mock(mockBehavior); + } - public IActiveConfiguredProjectsProvider Object => _mock.Object; + public IActiveConfiguredProjectsProvider Object => _mock.Object; - public void Verify() - { - _mock.Verify(); - } + public void Verify() + { + _mock.Verify(); + } - public IActiveConfiguredProjectsProviderFactory ImplementGetActiveConfiguredProjectsMapAsync(ImmutableDictionary configuredProjects) - { + public IActiveConfiguredProjectsProviderFactory ImplementGetActiveConfiguredProjectsMapAsync(ImmutableDictionary configuredProjects) + { #pragma warning disable CS0618 // Type or member is obsolete - _mock.Setup(x => x.GetActiveConfiguredProjectsMapAsync()) + _mock.Setup(x => x.GetActiveConfiguredProjectsMapAsync()) #pragma warning restore CS0618 // Type or member is obsolete - .ReturnsAsync(configuredProjects); - return this; - } - - public IActiveConfiguredProjectsProviderFactory ImplementGetActiveConfiguredProjectsAsync(ActiveConfiguredObjects configuredProjects) - { - _mock.Setup(x => x.GetActiveConfiguredProjectsAsync()) - .ReturnsAsync(configuredProjects); - return this; - } - - public IActiveConfiguredProjectsProviderFactory ImplementGetProjectFrameworksAsync(ActiveConfiguredObjects projectConfigurations) - { - _mock.Setup(x => x.GetActiveProjectConfigurationsAsync()) - .ReturnsAsync(projectConfigurations); - return this; - } + .ReturnsAsync(configuredProjects); + return this; + } + + public IActiveConfiguredProjectsProviderFactory ImplementGetActiveConfiguredProjectsAsync(ActiveConfiguredObjects configuredProjects) + { + _mock.Setup(x => x.GetActiveConfiguredProjectsAsync()) + .ReturnsAsync(configuredProjects); + return this; + } + + public IActiveConfiguredProjectsProviderFactory ImplementGetProjectFrameworksAsync(ActiveConfiguredObjects projectConfigurations) + { + _mock.Setup(x => x.GetActiveProjectConfigurationsAsync()) + .ReturnsAsync(projectConfigurations); + return this; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredValuesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredValuesFactory.cs index fc6a9ed4c7..4b691d05c2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredValuesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveConfiguredValuesFactory.cs @@ -1,18 +1,17 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IActiveConfiguredValuesFactory { - internal static class IActiveConfiguredValuesFactory + public static IActiveConfiguredValues ImplementValues(Func> action) + where T : class { - public static IActiveConfiguredValues ImplementValues(Func> action) - where T : class - { - var mock = new Mock>(); + var mock = new Mock>(); - mock.SetupGet(p => p.Values) - .Returns(action); + mock.SetupGet(p => p.Values) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesFactory.cs index b0ae19b1bd..cdbb7e4942 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesFactory.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal static class IActiveDebugFrameworkServicesFactory { - internal static class IActiveDebugFrameworkServicesFactory + public static IActiveDebugFrameworkServices ImplementGetConfiguredProjectForActiveFrameworkAsync(ConfiguredProject? project) { - public static IActiveDebugFrameworkServices ImplementGetConfiguredProjectForActiveFrameworkAsync(ConfiguredProject? project) - { - var service = new IActiveDebugFrameworkServicesMock(); - service.ImplementGetConfiguredProjectForActiveFrameworkAsync(project); + var service = new IActiveDebugFrameworkServicesMock(); + service.ImplementGetConfiguredProjectForActiveFrameworkAsync(project); - return service.Object; - } + return service.Object; + } - public static IActiveDebugFrameworkServices ImplementGetActiveDebuggingFrameworkPropertyAsync(string? activeDebugFramework) - { - var service = new IActiveDebugFrameworkServicesMock(); - service.ImplementGetActiveDebuggingFrameworkPropertyAsync(activeDebugFramework); + public static IActiveDebugFrameworkServices ImplementGetActiveDebuggingFrameworkPropertyAsync(string? activeDebugFramework) + { + var service = new IActiveDebugFrameworkServicesMock(); + service.ImplementGetActiveDebuggingFrameworkPropertyAsync(activeDebugFramework); - return service.Object; - } + return service.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesMock.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesMock.cs index b593ffd0c7..857f5ba5ff 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesMock.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveDebugFrameworkServicesMock.cs @@ -1,44 +1,43 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal class IActiveDebugFrameworkServicesMock : AbstractMock { - internal class IActiveDebugFrameworkServicesMock : AbstractMock + public IActiveDebugFrameworkServicesMock() + : base(MockBehavior.Strict) { - public IActiveDebugFrameworkServicesMock() - : base(MockBehavior.Strict) - { - } - - public IActiveDebugFrameworkServicesMock ImplementGetActiveDebuggingFrameworkPropertyAsync(string? activeFramework) - { - Setup(x => x.GetActiveDebuggingFrameworkPropertyAsync()) - .ReturnsAsync(activeFramework); - - return this; - } - - public IActiveDebugFrameworkServicesMock ImplementGetConfiguredProjectForActiveFrameworkAsync(ConfiguredProject? project) - { - Setup(x => x.GetConfiguredProjectForActiveFrameworkAsync()) - .ReturnsAsync(project); - - return this; - } - - public IActiveDebugFrameworkServicesMock ImplementGetProjectFrameworksAsync(List? frameworks) - { - Setup(x => x.GetProjectFrameworksAsync()) - .ReturnsAsync(frameworks); - - return this; - } - - public IActiveDebugFrameworkServicesMock ImplementSetActiveDebuggingFrameworkPropertyAsync(string activeFramework) - { - Setup(x => x.SetActiveDebuggingFrameworkPropertyAsync(activeFramework)) - .ReturnsAsync(() => { }); - - return this; - } + } + + public IActiveDebugFrameworkServicesMock ImplementGetActiveDebuggingFrameworkPropertyAsync(string? activeFramework) + { + Setup(x => x.GetActiveDebuggingFrameworkPropertyAsync()) + .ReturnsAsync(activeFramework); + + return this; + } + + public IActiveDebugFrameworkServicesMock ImplementGetConfiguredProjectForActiveFrameworkAsync(ConfiguredProject? project) + { + Setup(x => x.GetConfiguredProjectForActiveFrameworkAsync()) + .ReturnsAsync(project); + + return this; + } + + public IActiveDebugFrameworkServicesMock ImplementGetProjectFrameworksAsync(List? frameworks) + { + Setup(x => x.GetProjectFrameworksAsync()) + .ReturnsAsync(frameworks); + + return this; + } + + public IActiveDebugFrameworkServicesMock ImplementSetActiveDebuggingFrameworkPropertyAsync(string activeFramework) + { + Setup(x => x.SetActiveDebuggingFrameworkPropertyAsync(activeFramework)) + .ReturnsAsync(() => { }); + + return this; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveEditorContextTrackerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveEditorContextTrackerFactory.cs index 88fada426e..e02436e066 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveEditorContextTrackerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IActiveEditorContextTrackerFactory.cs @@ -2,34 +2,33 @@ using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal static class IActiveEditorContextTrackerFactory { - internal static class IActiveEditorContextTrackerFactory + public static IActiveEditorContextTracker Create() { - public static IActiveEditorContextTracker Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IActiveEditorContextTracker ImplementIsActiveEditorContext(Func action) - { - var mock = new Mock(); + public static IActiveEditorContextTracker ImplementIsActiveEditorContext(Func action) + { + var mock = new Mock(); - mock.Setup(t => t.IsActiveEditorContext(It.IsAny())) - .Returns(action); + mock.Setup(t => t.IsActiveEditorContext(It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IActiveEditorContextTracker ImplementRegisterContext(Action action, IDisposable? lifetime = null) - { - var mock = new Mock(); + public static IActiveEditorContextTracker ImplementRegisterContext(Action action, IDisposable? lifetime = null) + { + var mock = new Mock(); - mock.Setup(t => t.RegisterContext(It.IsAny())) - .Callback(action) - .Returns(lifetime ?? EmptyDisposable.Instance); + mock.Setup(t => t.RegisterContext(It.IsAny())) + .Callback(action) + .Returns(lifetime ?? EmptyDisposable.Instance); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IAppDesignerFolderSpecialFileProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IAppDesignerFolderSpecialFileProviderFactory.cs index d6ef408be8..f45cbbfbf9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IAppDesignerFolderSpecialFileProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IAppDesignerFolderSpecialFileProviderFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +internal static class IAppDesignerFolderSpecialFileProviderFactory { - internal static class IAppDesignerFolderSpecialFileProviderFactory + public static IAppDesignerFolderSpecialFileProvider ImplementGetFile(string? result) { - public static IAppDesignerFolderSpecialFileProvider ImplementGetFile(string? result) - { - var mock = new Mock(); - mock.Setup(m => m.GetFileAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(result); + var mock = new Mock(); + mock.Setup(m => m.GetFileAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(result); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICommandLineParserServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICommandLineParserServiceFactory.cs index 720ec3c3d7..6a78e94c17 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICommandLineParserServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICommandLineParserServiceFactory.cs @@ -2,27 +2,26 @@ using Microsoft.CodeAnalysis.CSharp; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal static class ICommandLineParserServiceFactory { - internal static class ICommandLineParserServiceFactory + public static ICommandLineParserService ImplementParse(Func, string, BuildOptions> action) { - public static ICommandLineParserService ImplementParse(Func, string, BuildOptions> action) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(s => s.Parse(It.IsAny>(), It.IsAny())) - .Returns(action); + mock.Setup(s => s.Parse(It.IsAny>(), It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static ICommandLineParserService CreateCSharp() + public static ICommandLineParserService CreateCSharp() + { + return ImplementParse((arguments, baseDirectory) => { - return ImplementParse((arguments, baseDirectory) => - { - return BuildOptions.FromCommandLineArguments( - CSharpCommandLineParser.Default.Parse(arguments, baseDirectory, sdkDirectory: null, additionalReferenceDirectories: null)); - }); - } + return BuildOptions.FromCommandLineArguments( + CSharpCommandLineParser.Default.Parse(arguments, baseDirectory, sdkDirectory: null, additionalReferenceDirectories: null)); + }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationDimensionDescriptionMetadataViewFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationDimensionDescriptionMetadataViewFactory.cs index 0432e6f262..a3b3bb0db1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationDimensionDescriptionMetadataViewFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationDimensionDescriptionMetadataViewFactory.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +internal static class IConfigurationDimensionDescriptionMetadataViewFactory { - internal static class IConfigurationDimensionDescriptionMetadataViewFactory + public static IConfigurationDimensionDescriptionMetadataView Create(string[] dimensionNames, bool[] isVariantDimension) { - public static IConfigurationDimensionDescriptionMetadataView Create(string[] dimensionNames, bool[] isVariantDimension) - { - var mock = new Mock(); - mock.SetupGet(v => v.DimensionName) - .Returns(dimensionNames); + var mock = new Mock(); + mock.SetupGet(v => v.DimensionName) + .Returns(dimensionNames); - mock.SetupGet(v => v.IsVariantDimension) - .Returns(isVariantDimension); + mock.SetupGet(v => v.IsVariantDimension) + .Returns(isVariantDimension); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationGroupFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationGroupFactory.cs index 4d6211e02f..4793821da3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationGroupFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IConfigurationGroupFactory.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IConfigurationGroupFactory { - internal static class IConfigurationGroupFactory + public static IConfigurationGroup CreateFromConfigurationNames(params string[] configurationNames) { - public static IConfigurationGroup CreateFromConfigurationNames(params string[] configurationNames) - { - IEnumerable configurations = configurationNames.Select(ProjectConfigurationFactory.Create); + IEnumerable configurations = configurationNames.Select(ProjectConfigurationFactory.Create); - return Create(configurations); - } + return Create(configurations); + } - public static IConfigurationGroup Create(IEnumerable values) - { - var group = new ConfigurationGroup(); + public static IConfigurationGroup Create(IEnumerable values) + { + var group = new ConfigurationGroup(); - group.AddRange(values); + group.AddRange(values); - return group; - } + return group; + } - private class ConfigurationGroup : List, IConfigurationGroup - { - public IReadOnlyCollection VariantDimensionNames => throw new NotImplementedException(); - } + private class ConfigurationGroup : List, IConfigurationGroup + { + public IReadOnlyCollection VariantDimensionNames => throw new NotImplementedException(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICreateFileFromTemplateServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICreateFileFromTemplateServiceFactory.cs index 9b1f00490b..22fedb0026 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICreateFileFromTemplateServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ICreateFileFromTemplateServiceFactory.cs @@ -2,21 +2,20 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ICreateFileFromTemplateServiceFactory { - internal static class ICreateFileFromTemplateServiceFactory + public static ICreateFileFromTemplateService Create() { - public static ICreateFileFromTemplateService Create() - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(s => s.CreateFileAsync(It.IsAny(), It.IsAny())) - .Returns((templateFile, path) => - { - return TaskResult.True; - }); + mock.Setup(s => s.CreateFileAsync(It.IsAny(), It.IsAny())) + .Returns((templateFile, path) => + { + return TaskResult.True; + }); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceFactory.cs index 51101718e0..b437656465 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceFactory.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IDataProgressTrackerServiceFactory { - internal static class IDataProgressTrackerServiceFactory + public static IDataProgressTrackerService Create() { - public static IDataProgressTrackerService Create() - { - var mock = new Mock(); - mock.Setup(s => s.RegisterOutputDataSource(It.IsAny())) - .Returns(IDataProgressTrackerServiceRegistrationFactory.Create()); + var mock = new Mock(); + mock.Setup(s => s.RegisterOutputDataSource(It.IsAny())) + .Returns(IDataProgressTrackerServiceRegistrationFactory.Create()); - return mock.Object; - } + return mock.Object; + } - public static IDataProgressTrackerService ImplementNotifyOutputDataCalculated(Action> action) - { - var registration = IDataProgressTrackerServiceRegistrationFactory.ImplementNotifyOutputDataCalculated(action); + public static IDataProgressTrackerService ImplementNotifyOutputDataCalculated(Action> action) + { + var registration = IDataProgressTrackerServiceRegistrationFactory.ImplementNotifyOutputDataCalculated(action); - var mock = new Mock(); - mock.Setup(s => s.RegisterOutputDataSource(It.IsAny())) - .Returns(registration); + var mock = new Mock(); + mock.Setup(s => s.RegisterOutputDataSource(It.IsAny())) + .Returns(registration); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceRegistrationFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceRegistrationFactory.cs index dfba2f8605..35c41c5a55 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceRegistrationFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IDataProgressTrackerServiceRegistrationFactory.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IDataProgressTrackerServiceRegistrationFactory { - internal static class IDataProgressTrackerServiceRegistrationFactory + public static IDataProgressTrackerServiceRegistration Create() { - public static IDataProgressTrackerServiceRegistration Create() - { - return ImplementNotifyOutputDataCalculated(_ => { }); - } + return ImplementNotifyOutputDataCalculated(_ => { }); + } - public static IDataProgressTrackerServiceRegistration ImplementNotifyOutputDataCalculated(Action> action) - { - var mock = new Mock(); - mock.Setup(s => s.NotifyOutputDataCalculated(It.IsAny>())) - .Callback(action); + public static IDataProgressTrackerServiceRegistration ImplementNotifyOutputDataCalculated(Action> action) + { + var mock = new Mock(); + mock.Setup(s => s.NotifyOutputDataCalculated(It.IsAny>())) + .Callback(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnumValueFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnumValueFactory.cs index 08762ba391..03e365f063 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnumValueFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnumValueFactory.cs @@ -2,25 +2,24 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IEnumValueFactory { - internal static class IEnumValueFactory + public static IEnumValue Create(string? displayName = null, string? name = null) { - public static IEnumValue Create(string? displayName = null, string? name = null) - { - var mock = new Mock(); + var mock = new Mock(); - if (displayName is not null) - { - mock.SetupGet(m => m.DisplayName).Returns(displayName); - } - - if (name is not null) - { - mock.SetupGet(m => m.Name).Returns(name); - } + if (displayName is not null) + { + mock.SetupGet(m => m.DisplayName).Returns(displayName); + } - return mock.Object; + if (name is not null) + { + mock.SetupGet(m => m.Name).Returns(name); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnvironmentHelperFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnvironmentHelperFactory.cs index 72d1f8a592..49447043ee 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnvironmentHelperFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IEnvironmentHelperFactory.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.Utilities; + +internal static class IEnvironmentHelperFactory { - internal static class IEnvironmentHelperFactory + public static IEnvironmentHelper ImplementGetEnvironmentVariable(string? result) { - public static IEnvironmentHelper ImplementGetEnvironmentVariable(string? result) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(s => s.GetEnvironmentVariable(It.IsAny())) - .Returns(() => result); + mock.Setup(s => s.GetEnvironmentVariable(It.IsAny())) + .Returns(() => result); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemFactory.cs index 618dfa9b7e..fd83531bd4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemFactory.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +internal static class IFileSystemFactory { - internal static class IFileSystemFactory + public static IFileSystem ImplementTryGetLastFileWriteTimeUtc(FuncWithOut action) { - public static IFileSystem ImplementTryGetLastFileWriteTimeUtc(FuncWithOut action) - { - DateTime? result; - var mock = new Mock(); - mock.Setup(f => f.TryGetLastFileWriteTimeUtc(It.IsAny(), out result)) - .Returns(action); - - return mock.Object; - } + DateTime? result; + var mock = new Mock(); + mock.Setup(f => f.TryGetLastFileWriteTimeUtc(It.IsAny(), out result)) + .Returns(action); - public static IFileSystem Create() - { - return Mock.Of(); - } + return mock.Object; + } - public static IFileSystem ImplementCreate(Action action) - { - var mock = new Mock(); - mock.Setup(f => f.Create(It.IsAny())) - .Callback(action); + public static IFileSystem Create() + { + return Mock.Of(); + } - return mock.Object; - } + public static IFileSystem ImplementCreate(Action action) + { + var mock = new Mock(); + mock.Setup(f => f.Create(It.IsAny())) + .Callback(action); - public static IFileSystem ImplementCreateDirectory(Action action) - { - var mock = new Mock(); - mock.Setup(f => f.CreateDirectory(It.IsAny())) - .Callback(action); + return mock.Object; + } - return mock.Object; - } + public static IFileSystem ImplementCreateDirectory(Action action) + { + var mock = new Mock(); + mock.Setup(f => f.CreateDirectory(It.IsAny())) + .Callback(action); - public static IFileSystem Create( - Func existsFunc, - Func? readAllTextFunc = null) - { - var mock = new Mock(); + return mock.Object; + } - mock.Setup(f => f.FileExists(It.IsAny())).Returns(existsFunc); + public static IFileSystem Create( + Func existsFunc, + Func? readAllTextFunc = null) + { + var mock = new Mock(); - if (readAllTextFunc is not null) - { - mock.Setup(f => f.ReadAllTextAsync(It.IsAny())) - .Returns((Func>)(path => Task.FromResult(readAllTextFunc(path)))); - } + mock.Setup(f => f.FileExists(It.IsAny())).Returns(existsFunc); - return mock.Object; + if (readAllTextFunc is not null) + { + mock.Setup(f => f.ReadAllTextAsync(It.IsAny())) + .Returns((Func>)(path => Task.FromResult(readAllTextFunc(path)))); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemMock.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemMock.cs index 84099c6086..6f5318f598 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemMock.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFileSystemMock.cs @@ -3,211 +3,210 @@ using System.Diagnostics.CodeAnalysis; using System.Text; -namespace Microsoft.VisualStudio.IO +namespace Microsoft.VisualStudio.IO; + +/// +/// A useful helper moq for IFileSystem. +/// Use AddFile, AddFolder method to add file paths and folders paths to the file system. +/// Maintaining the integrity of file system is the responsibility of caller. (Like creating +/// files and folders in a proper way) +/// +internal class IFileSystemMock : IFileSystem { - /// - /// A useful helper moq for IFileSystem. - /// Use AddFile, AddFolder method to add file paths and folders paths to the file system. - /// Maintaining the integrity of file system is the responsibility of caller. (Like creating - /// files and folders in a proper way) - /// - internal class IFileSystemMock : IFileSystem - { - internal class FileData - { - public string? FileContents; - public DateTime LastWriteTimeUtc = DateTime.MaxValue; + internal class FileData + { + public string? FileContents; + public DateTime LastWriteTimeUtc = DateTime.MaxValue; - public void SetLastWriteTime() + public void SetLastWriteTime() + { + // Every write should increase in time and just using DateTime.UtcNow can cause issues where + // two very fast writes return the same value. The following better simulates writes in the real world + if (LastWriteTimeUtc == DateTime.MaxValue) { - // Every write should increase in time and just using DateTime.UtcNow can cause issues where - // two very fast writes return the same value. The following better simulates writes in the real world - if (LastWriteTimeUtc == DateTime.MaxValue) - { - LastWriteTimeUtc = DateTime.UtcNow; - } - else if (LastWriteTimeUtc == DateTime.UtcNow) - { - LastWriteTimeUtc = DateTime.UtcNow.AddMilliseconds(new Random().NextDouble() * 10000); - } - else - { - LastWriteTimeUtc = LastWriteTimeUtc.AddMilliseconds(new Random().NextDouble() * 10000); - } + LastWriteTimeUtc = DateTime.UtcNow; + } + else if (LastWriteTimeUtc == DateTime.UtcNow) + { + LastWriteTimeUtc = DateTime.UtcNow.AddMilliseconds(new Random().NextDouble() * 10000); + } + else + { + LastWriteTimeUtc = LastWriteTimeUtc.AddMilliseconds(new Random().NextDouble() * 10000); } } + } - private readonly HashSet _folders = new(StringComparer.OrdinalIgnoreCase); + private readonly HashSet _folders = new(StringComparer.OrdinalIgnoreCase); - private string? _currentDirectory; + private string? _currentDirectory; - public Dictionary Files { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + public Dictionary Files { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - public void Create(string path) - { - _ = WriteAllTextAsync(path, ""); - } + public void Create(string path) + { + _ = WriteAllTextAsync(path, ""); + } - public void AddFile(string path, DateTime? lastWriteTime = null) + public void AddFile(string path, DateTime? lastWriteTime = null) + { + Files[path] = new FileData { - Files[path] = new FileData - { - FileContents = "", - LastWriteTimeUtc = lastWriteTime ?? DateTime.UtcNow - }; - } + FileContents = "", + LastWriteTimeUtc = lastWriteTime ?? DateTime.UtcNow + }; + } - public void AddFolder(string path) - { - _folders.Add(path); - } + public void AddFolder(string path) + { + _folders.Add(path); + } - public bool FileExists(string path) - { - return Files.ContainsKey(path); - } + public bool FileExists(string path) + { + return Files.ContainsKey(path); + } - public bool DirectoryExists(string path) - { - return _folders.Contains(path); - } + public bool DirectoryExists(string path) + { + return _folders.Contains(path); + } - public void CreateDirectory(string path) + public void CreateDirectory(string path) + { + if (!DirectoryExists(path)) { - if (!DirectoryExists(path)) - { - _folders.Add(path); - } + _folders.Add(path); } + } - public void SetCurrentDirectory(string directory) - { - CreateDirectory(directory); - _currentDirectory = directory; - } + public void SetCurrentDirectory(string directory) + { + CreateDirectory(directory); + _currentDirectory = directory; + } - public string GetFullPath(string path) + public string GetFullPath(string path) + { + if (_currentDirectory is not null) { - if (_currentDirectory is not null) + var pathRoot = Path.GetPathRoot(path); + if (pathRoot == @"\") { - var pathRoot = Path.GetPathRoot(path); - if (pathRoot == @"\") - { - return Path.GetPathRoot(_currentDirectory) + path.Substring(1); - } - else if (!Path.IsPathRooted(path)) - { - return Path.Combine(_currentDirectory, path); - } + return Path.GetPathRoot(_currentDirectory) + path.Substring(1); } - - return Path.GetFullPath(path); - } - - public void RemoveFile(string path) - { - if (!FileExists(path)) + else if (!Path.IsPathRooted(path)) { - throw new FileNotFoundException(); + return Path.Combine(_currentDirectory, path); } - Files.Remove(path); } - public void CopyFile(string source, string destination, bool overwrite, bool clearReadOnly) - { - if (!FileExists(destination)) - { - Files.Add(destination, Files[source]); - } - } + return Path.GetFullPath(path); + } - public Task ReadAllTextAsync(string path) + public void RemoveFile(string path) + { + if (!FileExists(path)) { - return Task.FromResult(GetFileData(path).FileContents!); + throw new FileNotFoundException(); } + Files.Remove(path); + } - public Stream OpenTextStream(string path) + public void CopyFile(string source, string destination, bool overwrite, bool clearReadOnly) + { + if (!FileExists(destination)) { - return new MemoryStream(Encoding.UTF8.GetBytes(GetFileData(path).FileContents!)); + Files.Add(destination, Files[source]); } + } - public Task WriteAllTextAsync(string path, string content) - { - if (Files.TryGetValue(path, out FileData data)) - { - // This makes sure each write to the file increases the timestamp - data.FileContents = content; - data.SetLastWriteTime(); - } - else - { - Files[path] = new FileData - { - FileContents = content, - LastWriteTimeUtc = DateTime.UtcNow - }; - } + public Task ReadAllTextAsync(string path) + { + return Task.FromResult(GetFileData(path).FileContents!); + } - return Task.CompletedTask; - } + public Stream OpenTextStream(string path) + { + return new MemoryStream(Encoding.UTF8.GetBytes(GetFileData(path).FileContents!)); + } - public DateTime GetLastFileWriteTimeOrMinValueUtc(string path) + public Task WriteAllTextAsync(string path, string content) + { + if (Files.TryGetValue(path, out FileData data)) { - if (TryGetLastFileWriteTimeUtc(path, out DateTime? result)) - { - return result.Value; - } - - return DateTime.MinValue; + // This makes sure each write to the file increases the timestamp + data.FileContents = content; + data.SetLastWriteTime(); } - - public bool TryGetLastFileWriteTimeUtc(string path, [NotNullWhen(true)]out DateTime? result) + else { - if (Files.TryGetValue(path, out FileData value)) + Files[path] = new FileData { - result = value.LastWriteTimeUtc; - return true; - } - - result = null; - return false; + FileContents = content, + LastWriteTimeUtc = DateTime.UtcNow + }; } - public bool TryGetFileSizeBytes(string path, out long result) - { - if (Files.TryGetValue(path, out FileData value)) - { - result = value.FileContents?.Length ?? 0; - return true; - } + return Task.CompletedTask; + } - result = default; - return false; + public DateTime GetLastFileWriteTimeOrMinValueUtc(string path) + { + if (TryGetLastFileWriteTimeUtc(path, out DateTime? result)) + { + return result.Value; } - public bool PathExists(string path) + return DateTime.MinValue; + } + + public bool TryGetLastFileWriteTimeUtc(string path, [NotNullWhen(true)]out DateTime? result) + { + if (Files.TryGetValue(path, out FileData value)) { - throw new NotImplementedException(); + result = value.LastWriteTimeUtc; + return true; } - public (long SizeBytes, DateTime WriteTimeUtc)? GetFileSizeAndWriteTimeUtc(string path) - { - if (Files.TryGetValue(path, out FileData value)) - { - return (value.FileContents?.Length ?? 0, value.LastWriteTimeUtc); - } + result = null; + return false; + } - return null; + public bool TryGetFileSizeBytes(string path, out long result) + { + if (Files.TryGetValue(path, out FileData value)) + { + result = value.FileContents?.Length ?? 0; + return true; } - private FileData GetFileData(string path) + result = default; + return false; + } + + public bool PathExists(string path) + { + throw new NotImplementedException(); + } + + public (long SizeBytes, DateTime WriteTimeUtc)? GetFileSizeAndWriteTimeUtc(string path) + { + if (Files.TryGetValue(path, out FileData value)) { - if (!Files.TryGetValue(path, out FileData fileData)) - { - throw new FileNotFoundException(); - } + return (value.FileContents?.Length ?? 0, value.LastWriteTimeUtc); + } + + return null; + } - return fileData; + private FileData GetFileData(string path) + { + if (!Files.TryGetValue(path, out FileData fileData)) + { + throw new FileNotFoundException(); } + + return fileData; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFolderManagerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFolderManagerFactory.cs index 3a25a272b2..6a96065c98 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFolderManagerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IFolderManagerFactory.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IFolderManagerFactory { - internal static class IFolderManagerFactory + public static IFolderManager Create() { - public static IFolderManager Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IFolderManager IncludeFolderInProjectAsync(Action action) - { - var mock = new Mock(); + public static IFolderManager IncludeFolderInProjectAsync(Action action) + { + var mock = new Mock(); - mock.Setup(m => m.IncludeFolderInProjectAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(action); + mock.Setup(m => m.IncludeFolderInProjectAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; + } - public static IFolderManager IncludeFolderInProjectAsync(Func action) - { - var mock = new Mock(); + public static IFolderManager IncludeFolderInProjectAsync(Func action) + { + var mock = new Mock(); - mock.Setup(m => m.IncludeFolderInProjectAsync(It.IsAny(), It.IsAny())) - .Returns(action); + mock.Setup(m => m.IncludeFolderInProjectAsync(It.IsAny(), It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IGlobalSettingExtensionValueProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IGlobalSettingExtensionValueProviderFactory.cs index cde71e9ae5..5095173972 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IGlobalSettingExtensionValueProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IGlobalSettingExtensionValueProviderFactory.cs @@ -3,29 +3,28 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.Mocks +namespace Microsoft.VisualStudio.Mocks; + +internal static class IGlobalSettingExtensionValueProviderFactory { - internal static class IGlobalSettingExtensionValueProviderFactory + public static IGlobalSettingExtensionValueProvider Create( + Func, Rule?, string>? onGetPropertyValue = null, + Func, Rule?, ImmutableDictionary>? onSetPropertyValue = null) { - public static IGlobalSettingExtensionValueProvider Create( - Func, Rule?, string>? onGetPropertyValue = null, - Func, Rule?, ImmutableDictionary>? onSetPropertyValue = null) - { - var providerMock = new Mock(); + var providerMock = new Mock(); - if (onGetPropertyValue is not null) - { - providerMock.Setup(t => t.OnGetPropertyValue(It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(onGetPropertyValue); - } - - if (onSetPropertyValue is not null) - { - providerMock.Setup(t => t.OnSetPropertyValue(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(onSetPropertyValue); - } + if (onGetPropertyValue is not null) + { + providerMock.Setup(t => t.OnGetPropertyValue(It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(onGetPropertyValue); + } - return providerMock.Object; + if (onSetPropertyValue is not null) + { + providerMock.Setup(t => t.OnSetPropertyValue(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(onSetPropertyValue); } + + return providerMock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyActiveDimensionProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyActiveDimensionProviderFactory.cs index 21334c2f85..7f3e6c79a9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyActiveDimensionProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyActiveDimensionProviderFactory.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +internal static class IImplicitlyActiveDimensionProviderFactory { - internal static class IImplicitlyActiveDimensionProviderFactory + public static IImplicitlyActiveDimensionProvider Create() { - public static IImplicitlyActiveDimensionProvider Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IImplicitlyActiveDimensionProvider ImplementGetImplicitlyActiveDimensions(Func, IEnumerable> action) - { - var mock = new Mock(); - mock.Setup(p => p.GetImplicitlyActiveDimensions(It.IsAny>())) - .Returns(action); + public static IImplicitlyActiveDimensionProvider ImplementGetImplicitlyActiveDimensions(Func, IEnumerable> action) + { + var mock = new Mock(); + mock.Setup(p => p.GetImplicitlyActiveDimensions(It.IsAny>())) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyTriggeredBuildManagerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyTriggeredBuildManagerFactory.cs index 6a6af4f874..2710ed8586 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyTriggeredBuildManagerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicitlyTriggeredBuildManagerFactory.cs @@ -2,36 +2,35 @@ #pragma warning disable CS0618 // Type or member is obsolete - IImplicitlyTriggeredBuildManager is marked obsolete as it may eventually be replaced with a different API. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +internal static class IImplicitlyTriggeredBuildManagerFactory { - internal static class IImplicitlyTriggeredBuildManagerFactory + public static IImplicitlyTriggeredBuildManager Create( + Action? onImplicitBuildStart = null, + Action? onImplicitBuildEndOrCancel = null, + Action>? onImplictBuildStartWithStartupPaths = null) { - public static IImplicitlyTriggeredBuildManager Create( - Action? onImplicitBuildStart = null, - Action? onImplicitBuildEndOrCancel = null, - Action>? onImplictBuildStartWithStartupPaths = null) - { - var mock = new Mock(); - - if (onImplicitBuildStart is not null) - { - mock.Setup(t => t.OnBuildStart()) - .Callback(onImplicitBuildStart); - } + var mock = new Mock(); - if (onImplicitBuildEndOrCancel is not null) - { - mock.Setup(t => t.OnBuildEndOrCancel()) - .Callback(onImplicitBuildEndOrCancel); - } + if (onImplicitBuildStart is not null) + { + mock.Setup(t => t.OnBuildStart()) + .Callback(onImplicitBuildStart); + } - if (onImplictBuildStartWithStartupPaths is not null) - { - mock.Setup(t => t.OnBuildStart(It.IsAny>())) - .Callback(onImplictBuildStartWithStartupPaths); - } + if (onImplicitBuildEndOrCancel is not null) + { + mock.Setup(t => t.OnBuildEndOrCancel()) + .Callback(onImplicitBuildEndOrCancel); + } - return mock.Object; + if (onImplictBuildStartWithStartupPaths is not null) + { + mock.Setup(t => t.OnBuildStart(It.IsAny>())) + .Callback(onImplictBuildStartWithStartupPaths); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicityTriggeredBuildStateFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicityTriggeredBuildStateFactory.cs index 1a81506684..b77e884186 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicityTriggeredBuildStateFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IImplicityTriggeredBuildStateFactory.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build +namespace Microsoft.VisualStudio.ProjectSystem.Managed.Build; + +internal class IImplicityTriggeredBuildStateFactory { - internal class IImplicityTriggeredBuildStateFactory + public static IImplicitlyTriggeredBuildState Create(bool implicitBuild, IEnumerable? startupProjects = null) { - public static IImplicitlyTriggeredBuildState Create(bool implicitBuild, IEnumerable? startupProjects = null) - { - var mock = new Mock(); + var mock = new Mock(); - mock.SetupGet(state => state.IsImplicitlyTriggeredBuild) - .Returns(implicitBuild); + mock.SetupGet(state => state.IsImplicitlyTriggeredBuild) + .Returns(implicitBuild); - mock.SetupGet(state => state.StartupProjectFullPaths) - .Returns(startupProjects?.ToImmutableArray() ?? ImmutableArray.Empty); + mock.SetupGet(state => state.StartupProjectFullPaths) + .Returns(startupProjects?.ToImmutableArray() ?? ImmutableArray.Empty); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderFactory.cs index 595fc6177b..bc51dd77b4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderFactory.cs @@ -2,57 +2,56 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IInterceptingPropertyValueProviderFactory { - internal static class IInterceptingPropertyValueProviderFactory + public static IInterceptingPropertyValueProvider Create( + Func? onGetEvaluatedPropertyValue = null, + Func? onGetUnevaluatedPropertyValue = null, + Func, string?>? onSetPropertyValue = null) { - public static IInterceptingPropertyValueProvider Create( - Func? onGetEvaluatedPropertyValue = null, - Func? onGetUnevaluatedPropertyValue = null, - Func, string?>? onSetPropertyValue = null) + var mock = new Mock(); + + if (onGetEvaluatedPropertyValue is not null) + { + mock.Setup(t => t.OnGetEvaluatedPropertyValueAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((n, u, p) => Task.FromResult(onGetEvaluatedPropertyValue(u, p))); + } + + if (onGetUnevaluatedPropertyValue is not null) { - var mock = new Mock(); - - if (onGetEvaluatedPropertyValue is not null) - { - mock.Setup(t => t.OnGetEvaluatedPropertyValueAsync( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((n, u, p) => Task.FromResult(onGetEvaluatedPropertyValue(u, p))); - } - - if (onGetUnevaluatedPropertyValue is not null) - { - mock.Setup(t => t.OnGetUnevaluatedPropertyValueAsync( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns((n, u, p) => Task.FromResult(onGetUnevaluatedPropertyValue(u, p))); - } - - if (onSetPropertyValue is not null) - { - mock.Setup(t => t.OnSetPropertyValueAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>())) - .Returns>((n, u, p, d) => Task.FromResult(onSetPropertyValue(u, p, d))); - } - - return mock.Object; + mock.Setup(t => t.OnGetUnevaluatedPropertyValueAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns((n, u, p) => Task.FromResult(onGetUnevaluatedPropertyValue(u, p))); } - public static Lazy Create( - string propertyName, - Func? onGetEvaluatedPropertyValue = null, - Func? onGetUnevaluatedPropertyValue = null, - Func, string?>? onSetPropertyValue = null) + if (onSetPropertyValue is not null) { - var mockMetadata = IInterceptingPropertyValueProviderMetadataFactory.Create(propertyName); - var mockProvider = Create(onGetEvaluatedPropertyValue, onGetUnevaluatedPropertyValue, onSetPropertyValue); - return new Lazy(() => mockProvider, mockMetadata); + mock.Setup(t => t.OnSetPropertyValueAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>())) + .Returns>((n, u, p, d) => Task.FromResult(onSetPropertyValue(u, p, d))); } + + return mock.Object; + } + + public static Lazy Create( + string propertyName, + Func? onGetEvaluatedPropertyValue = null, + Func? onGetUnevaluatedPropertyValue = null, + Func, string?>? onSetPropertyValue = null) + { + var mockMetadata = IInterceptingPropertyValueProviderMetadataFactory.Create(propertyName); + var mockProvider = Create(onGetEvaluatedPropertyValue, onGetUnevaluatedPropertyValue, onSetPropertyValue); + return new Lazy(() => mockProvider, mockMetadata); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderMetadataFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderMetadataFactory.cs index da65eb3821..3679138e48 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderMetadataFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IInterceptingPropertyValueProviderMetadataFactory.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IInterceptingPropertyValueProviderMetadataFactory { - internal static class IInterceptingPropertyValueProviderMetadataFactory + public static IInterceptingPropertyValueProviderMetadata2 Create(string propertyName) { - public static IInterceptingPropertyValueProviderMetadata2 Create(string propertyName) - { - var mock = new Mock(); + var mock = new Mock(); - mock.SetupGet(s => s.PropertyNames) - .Returns(new[] { propertyName }); + mock.SetupGet(s => s.PropertyNames) + .Returns(new[] { propertyName }); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IJsonModel.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IJsonModel.cs index f577068196..c0891805f4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IJsonModel.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IJsonModel.cs @@ -2,23 +2,22 @@ using Newtonsoft.Json.Linq; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal interface IJsonModel { - internal interface IJsonModel - { - T ToActualModel(); - } + T ToActualModel(); +} - internal abstract class JsonModel : IJsonModel +internal abstract class JsonModel : IJsonModel +{ + public T FromJson(string jsonString) { - public T FromJson(string jsonString) - { - var json = JObject.Parse(jsonString); - var data = (IJsonModel?)json.ToObject(GetType()); - Assumes.NotNull(data); - return data.ToActualModel(); - } - - public abstract T ToActualModel(); + var json = JObject.Parse(jsonString); + var data = (IJsonModel?)json.ToObject(GetType()); + Assumes.NotNull(data); + return data.ToActualModel(); } + + public abstract T ToActualModel(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderFactory.cs index a15490d78b..0e7e10d3bf 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderFactory.cs @@ -4,29 +4,28 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.Mocks +namespace Microsoft.VisualStudio.Mocks; + +internal static class ILaunchProfileExtensionValueProviderFactory { - internal static class ILaunchProfileExtensionValueProviderFactory + public static ILaunchProfileExtensionValueProvider Create( + Func, Rule?, string>? onGetPropertyValue = null, + Action, Rule?>? onSetPropertyValue = null) { - public static ILaunchProfileExtensionValueProvider Create( - Func, Rule?, string>? onGetPropertyValue = null, - Action, Rule?>? onSetPropertyValue = null) - { - var providerMock = new Mock(); + var providerMock = new Mock(); - if (onGetPropertyValue is not null) - { - providerMock.Setup(t => t.OnGetPropertyValue(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(onGetPropertyValue); - } - - if (onSetPropertyValue is not null) - { - providerMock.Setup(t => t.OnSetPropertyValue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) - .Callback(onSetPropertyValue); - } + if (onGetPropertyValue is not null) + { + providerMock.Setup(t => t.OnGetPropertyValue(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(onGetPropertyValue); + } - return providerMock.Object; + if (onSetPropertyValue is not null) + { + providerMock.Setup(t => t.OnSetPropertyValue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Callback(onSetPropertyValue); } + + return providerMock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderMetadataFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderMetadataFactory.cs index 8e0efb38ed..1013ee9b94 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderMetadataFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchProfileExtensionValueProviderMetadataFactory.cs @@ -2,25 +2,24 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.Mocks +namespace Microsoft.VisualStudio.Mocks; + +internal static class ILaunchProfileExtensionValueProviderMetadataFactory { - internal static class ILaunchProfileExtensionValueProviderMetadataFactory + public static ILaunchProfileExtensionValueProviderMetadata Create(string[]? propertyNames = null) { - public static ILaunchProfileExtensionValueProviderMetadata Create(string[]? propertyNames = null) - { - var metadataMock = new Mock(); - - if (propertyNames is not null) - { - metadataMock.Setup(t => t.PropertyNames).Returns(propertyNames); - } - - return metadataMock.Object; - } + var metadataMock = new Mock(); - public static ILaunchProfileExtensionValueProviderMetadata Create(string propertyName) + if (propertyNames is not null) { - return Create(new[] { propertyName }); + metadataMock.Setup(t => t.PropertyNames).Returns(propertyNames); } + + return metadataMock.Object; + } + + public static ILaunchProfileExtensionValueProviderMetadata Create(string propertyName) + { + return Create(new[] { propertyName }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchSettingsProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchSettingsProviderFactory.cs index ba5e1d00a0..8c215985f3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchSettingsProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILaunchSettingsProviderFactory.cs @@ -3,151 +3,150 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ILaunchSettingsProviderFactory { - internal static class ILaunchSettingsProviderFactory + /// + /// Creates a mock for testing purposes. + /// + /// The name of the active profile in the new object. + /// The initial set of launch profiles to expose through the provider. + /// An optional method to call when the active profile is set. + /// An optional method to call when the set of launch settings is updated + /// + /// A optional method to call when the set of profile is queried. Given the initial set of as an argument. + /// + /// An optional method to call when a profile is added or updated. + /// An optional methods to call when a profile is removed. + /// An optional method to call when a profile is updated. + /// An optional method to call when a global setting is updated. + /// The initial set of global settings to expose through the provider. + public static ILaunchSettingsProvider3 Create( + string? activeProfileName = null, + IEnumerable? launchProfiles = null, + Action? setActiveProfileCallback = null, + Action? updateLaunchSettingsCallback = null, + Func, ImmutableList>? getProfilesCallback = null, + Action? addOrUpdateProfileCallback = null, + Action? removeProfileCallback = null, + Action>? tryUpdateProfileCallback = null, + Func, ImmutableDictionary>? updateGlobalSettingsCallback = null, + ImmutableDictionary? globalSettings = null) { - /// - /// Creates a mock for testing purposes. - /// - /// The name of the active profile in the new object. - /// The initial set of launch profiles to expose through the provider. - /// An optional method to call when the active profile is set. - /// An optional method to call when the set of launch settings is updated - /// - /// A optional method to call when the set of profile is queried. Given the initial set of as an argument. - /// - /// An optional method to call when a profile is added or updated. - /// An optional methods to call when a profile is removed. - /// An optional method to call when a profile is updated. - /// An optional method to call when a global setting is updated. - /// The initial set of global settings to expose through the provider. - public static ILaunchSettingsProvider3 Create( - string? activeProfileName = null, - IEnumerable? launchProfiles = null, - Action? setActiveProfileCallback = null, - Action? updateLaunchSettingsCallback = null, - Func, ImmutableList>? getProfilesCallback = null, - Action? addOrUpdateProfileCallback = null, - Action? removeProfileCallback = null, - Action>? tryUpdateProfileCallback = null, - Func, ImmutableDictionary>? updateGlobalSettingsCallback = null, - ImmutableDictionary? globalSettings = null) + var launchSettingsMock = new Mock(); + + var initialLaunchProfiles = launchProfiles is not null + ? launchProfiles.ToImmutableList() + : ImmutableList.Empty; + + var initialGlobalSettings = globalSettings ?? ImmutableDictionary.Empty; + + if (getProfilesCallback is not null) + { + launchSettingsMock.Setup(t => t.Profiles).Returns(() => getProfilesCallback(initialLaunchProfiles)); + } + else + { + launchSettingsMock.Setup(t => t.Profiles).Returns(initialLaunchProfiles); + } + + if (initialGlobalSettings is not null) + { + launchSettingsMock.Setup(t => t.GlobalSettings).Returns(initialGlobalSettings); + } + + if (activeProfileName is not null) + { + var activeLaunchProfile = launchProfiles?.FirstOrDefault(p => p.Name == activeProfileName) + ?? new LaunchProfile(name: activeProfileName, commandName: null); + launchSettingsMock.Setup(t => t.ActiveProfile).Returns(activeLaunchProfile); + } + + var launchSettings = launchSettingsMock.Object; + + var settingsProviderMock = new Mock(); + settingsProviderMock.Setup(t => t.WaitForFirstSnapshot(It.IsAny())).Returns(Task.FromResult(launchSettings)); + settingsProviderMock.SetupGet(t => t.CurrentSnapshot).Returns(launchSettings); + + if (setActiveProfileCallback is not null) + { + settingsProviderMock.Setup(t => t.SetActiveProfileAsync(It.IsAny())) + .Returns(v => + { + setActiveProfileCallback(v); + return Task.CompletedTask; + }); + } + + if (updateLaunchSettingsCallback is not null) + { + settingsProviderMock.Setup(t => t.UpdateAndSaveSettingsAsync(It.IsAny())) + .Returns(v => + { + updateLaunchSettingsCallback(v); + return Task.CompletedTask; + }); + } + + if (addOrUpdateProfileCallback is not null) + { + settingsProviderMock.Setup(t => t.AddOrUpdateProfileAsync(It.IsAny(), It.IsAny())) + .Returns((profile, addToFront) => + { + addOrUpdateProfileCallback(profile, addToFront); + return Task.CompletedTask; + }); + } + + if (removeProfileCallback is not null) + { + settingsProviderMock.Setup(t => t.RemoveProfileAsync(It.IsAny())) + .Returns(name => + { + removeProfileCallback(name); + return Task.CompletedTask; + }); + } + + var settingsProvider3Mock = settingsProviderMock.As(); + if (tryUpdateProfileCallback is not null) { - var launchSettingsMock = new Mock(); - - var initialLaunchProfiles = launchProfiles is not null - ? launchProfiles.ToImmutableList() - : ImmutableList.Empty; - - var initialGlobalSettings = globalSettings ?? ImmutableDictionary.Empty; - - if (getProfilesCallback is not null) - { - launchSettingsMock.Setup(t => t.Profiles).Returns(() => getProfilesCallback(initialLaunchProfiles)); - } - else - { - launchSettingsMock.Setup(t => t.Profiles).Returns(initialLaunchProfiles); - } - - if (initialGlobalSettings is not null) - { - launchSettingsMock.Setup(t => t.GlobalSettings).Returns(initialGlobalSettings); - } - - if (activeProfileName is not null) - { - var activeLaunchProfile = launchProfiles?.FirstOrDefault(p => p.Name == activeProfileName) - ?? new LaunchProfile(name: activeProfileName, commandName: null); - launchSettingsMock.Setup(t => t.ActiveProfile).Returns(activeLaunchProfile); - } - - var launchSettings = launchSettingsMock.Object; - - var settingsProviderMock = new Mock(); - settingsProviderMock.Setup(t => t.WaitForFirstSnapshot(It.IsAny())).Returns(Task.FromResult(launchSettings)); - settingsProviderMock.SetupGet(t => t.CurrentSnapshot).Returns(launchSettings); - - if (setActiveProfileCallback is not null) - { - settingsProviderMock.Setup(t => t.SetActiveProfileAsync(It.IsAny())) - .Returns(v => - { - setActiveProfileCallback(v); - return Task.CompletedTask; - }); - } - - if (updateLaunchSettingsCallback is not null) - { - settingsProviderMock.Setup(t => t.UpdateAndSaveSettingsAsync(It.IsAny())) - .Returns(v => - { - updateLaunchSettingsCallback(v); - return Task.CompletedTask; - }); - } - - if (addOrUpdateProfileCallback is not null) - { - settingsProviderMock.Setup(t => t.AddOrUpdateProfileAsync(It.IsAny(), It.IsAny())) - .Returns((profile, addToFront) => - { - addOrUpdateProfileCallback(profile, addToFront); - return Task.CompletedTask; - }); - } - - if (removeProfileCallback is not null) - { - settingsProviderMock.Setup(t => t.RemoveProfileAsync(It.IsAny())) - .Returns(name => - { - removeProfileCallback(name); - return Task.CompletedTask; - }); - } - - var settingsProvider3Mock = settingsProviderMock.As(); - if (tryUpdateProfileCallback is not null) - { - settingsProvider3Mock.Setup(t => t.TryUpdateProfileAsync(It.IsAny(), It.IsAny>())) - .Returns>((name, action) => - { - tryUpdateProfileCallback(name, action); - return TaskResult.True; - }); - } - else - { - settingsProvider3Mock.Setup(t => t.TryUpdateProfileAsync(It.IsAny(), It.IsAny>())) - .Returns>((name, action) => - { - var profile = new WritableLaunchProfile { Name = name }; - action(profile); - return TaskResult.True; - }); - - } - - if (updateGlobalSettingsCallback is not null) - { - settingsProvider3Mock.Setup(t => t.UpdateGlobalSettingsAsync(It.IsAny, ImmutableDictionary>>())) - .Returns(updateGlobalSettingsCallback); - } - else - { - settingsProvider3Mock.Setup(t => t.UpdateGlobalSettingsAsync(It.IsAny, ImmutableDictionary>>())) - .Returns, ImmutableDictionary>>(updateFunc => - { - _ = updateFunc(ImmutableDictionary.Empty); - return Task.CompletedTask; - }); - } - - var settingsProvider = settingsProviderMock.Object; - return settingsProvider; + settingsProvider3Mock.Setup(t => t.TryUpdateProfileAsync(It.IsAny(), It.IsAny>())) + .Returns>((name, action) => + { + tryUpdateProfileCallback(name, action); + return TaskResult.True; + }); } + else + { + settingsProvider3Mock.Setup(t => t.TryUpdateProfileAsync(It.IsAny(), It.IsAny>())) + .Returns>((name, action) => + { + var profile = new WritableLaunchProfile { Name = name }; + action(profile); + return TaskResult.True; + }); + + } + + if (updateGlobalSettingsCallback is not null) + { + settingsProvider3Mock.Setup(t => t.UpdateGlobalSettingsAsync(It.IsAny, ImmutableDictionary>>())) + .Returns(updateGlobalSettingsCallback); + } + else + { + settingsProvider3Mock.Setup(t => t.UpdateGlobalSettingsAsync(It.IsAny, ImmutableDictionary>>())) + .Returns, ImmutableDictionary>>(updateFunc => + { + _ = updateFunc(ImmutableDictionary.Empty); + return Task.CompletedTask; + }); + } + + var settingsProvider = settingsProviderMock.Object; + return settingsProvider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILoadedInHostListenerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILoadedInHostListenerFactory.cs index 91e809ab38..b2e9ea63f5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILoadedInHostListenerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ILoadedInHostListenerFactory.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ILoadedInHostListenerFactory { - internal static class ILoadedInHostListenerFactory + public static ILoadedInHostListener Create() { - public static ILoadedInHostListener Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static ILoadedInHostListener ImplementStartListeningAsync(Action action) - { - var mock = new Mock(); - mock.Setup(t => t.StartListeningAsync()) - .ReturnsAsync(action); + public static ILoadedInHostListener ImplementStartListeningAsync(Action action) + { + var mock = new Mock(); + mock.Setup(t => t.StartListeningAsync()) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IOrderPrecedenceMetadataViewFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IOrderPrecedenceMetadataViewFactory.cs index f9f02104d9..cda403af70 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IOrderPrecedenceMetadataViewFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IOrderPrecedenceMetadataViewFactory.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IOrderPrecedenceMetadataViewFactory { - internal static class IOrderPrecedenceMetadataViewFactory + public static IOrderPrecedenceMetadataView Create(string? appliesTo = null, int orderPrecedence = 0) { - public static IOrderPrecedenceMetadataView Create(string? appliesTo = null, int orderPrecedence = 0) - { - var mock = new Mock(); + var mock = new Mock(); - mock.SetupGet(v => v.AppliesTo) - .Returns(appliesTo ?? ""); + mock.SetupGet(v => v.AppliesTo) + .Returns(appliesTo ?? ""); - mock.SetupGet(v => v.OrderPrecedence) - .Returns(orderPrecedence); + mock.SetupGet(v => v.OrderPrecedence) + .Returns(orderPrecedence); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreServiceFactory.cs index e212ab6093..5660538fb7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreServiceFactory.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static class IPackageRestoreServiceFactory { - internal static class IPackageRestoreServiceFactory + public static IPackageRestoreDataSource Create() { - public static IPackageRestoreDataSource Create() - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(s => s.SourceBlock) - .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); + mock.Setup(s => s.SourceBlock) + .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreUnconfiguredInputDataSourceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreUnconfiguredInputDataSourceFactory.cs index e0dc1b1d39..bdd5b107da 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreUnconfiguredInputDataSourceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPackageRestoreUnconfiguredInputDataSourceFactory.cs @@ -2,40 +2,39 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static class IPackageRestoreUnconfiguredInputDataSourceFactory { - internal static class IPackageRestoreUnconfiguredInputDataSourceFactory + public static IPackageRestoreUnconfiguredInputDataSource Create() { - public static IPackageRestoreUnconfiguredInputDataSource Create() - { - var sourceBlock = Mock.Of>>(); + var sourceBlock = Mock.Of>>(); - // Moq gets really confused with mocking IProjectValueDataSource.SourceBlock - // because of the generic/non-generic version of it. Avoid it. - return new PackageRestoreUnconfiguredDataSource(sourceBlock); - } + // Moq gets really confused with mocking IProjectValueDataSource.SourceBlock + // because of the generic/non-generic version of it. Avoid it. + return new PackageRestoreUnconfiguredDataSource(sourceBlock); + } - private class PackageRestoreUnconfiguredDataSource : IPackageRestoreUnconfiguredInputDataSource + private class PackageRestoreUnconfiguredDataSource : IPackageRestoreUnconfiguredInputDataSource + { + public PackageRestoreUnconfiguredDataSource(IReceivableSourceBlock> sourceBlock) { - public PackageRestoreUnconfiguredDataSource(IReceivableSourceBlock> sourceBlock) - { - SourceBlock = sourceBlock; - DataSourceKey = new NamedIdentity(); - DataSourceVersion = 1; - } + SourceBlock = sourceBlock; + DataSourceKey = new NamedIdentity(); + DataSourceVersion = 1; + } - public IReceivableSourceBlock> SourceBlock { get; } + public IReceivableSourceBlock> SourceBlock { get; } - public NamedIdentity DataSourceKey { get; } + public NamedIdentity DataSourceKey { get; } - public IComparable DataSourceVersion { get; } + public IComparable DataSourceVersion { get; } - ISourceBlock> IProjectValueDataSource.SourceBlock => SourceBlock; + ISourceBlock> IProjectValueDataSource.SourceBlock => SourceBlock; - public IDisposable? Join() - { - return null; - } + public IDisposable? Join() + { + return null; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeFactory.cs index 683eb6f336..d9d3ebd23e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeFactory.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IPhysicalProjectTreeFactory { - internal static class IPhysicalProjectTreeFactory + public static IPhysicalProjectTree Create(IProjectTreeProvider? provider = null, IProjectTree? currentTree = null, IProjectTreeService? service = null, IPhysicalProjectTreeStorage? storage = null) { - public static IPhysicalProjectTree Create(IProjectTreeProvider? provider = null, IProjectTree? currentTree = null, IProjectTreeService? service = null, IPhysicalProjectTreeStorage? storage = null) - { - currentTree ??= ProjectTreeParser.Parse("Project"); - provider ??= new ProjectTreeProvider(); - storage ??= IPhysicalProjectTreeStorageFactory.Create(); - service ??= IProjectTreeServiceFactory.Create(currentTree, provider); + currentTree ??= ProjectTreeParser.Parse("Project"); + provider ??= new ProjectTreeProvider(); + storage ??= IPhysicalProjectTreeStorageFactory.Create(); + service ??= IProjectTreeServiceFactory.Create(currentTree, provider); - var mock = new Mock(); - mock.Setup(t => t.TreeProvider) - .Returns(provider); + var mock = new Mock(); + mock.Setup(t => t.TreeProvider) + .Returns(provider); - mock.Setup(t => t.CurrentTree) - .Returns(currentTree); + mock.Setup(t => t.CurrentTree) + .Returns(currentTree); - mock.Setup(t => t.TreeService) - .Returns(service); + mock.Setup(t => t.TreeService) + .Returns(service); - mock.Setup(t => t.TreeStorage) - .Returns(storage); + mock.Setup(t => t.TreeStorage) + .Returns(storage); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeStorageFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeStorageFactory.cs index d4b278615f..c90d344a63 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeStorageFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPhysicalProjectTreeStorageFactory.cs @@ -1,48 +1,47 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IPhysicalProjectTreeStorageFactory { - internal static class IPhysicalProjectTreeStorageFactory + public static IPhysicalProjectTreeStorage Create() { - public static IPhysicalProjectTreeStorage Create() - { - return Mock.Of(); - } - - public static IPhysicalProjectTreeStorage ImplementCreateEmptyFileAsync(Action action) - { - var mock = new Mock(); - mock.Setup(p => p.CreateEmptyFileAsync(It.IsAny())) - .ReturnsAsync(action); - - return mock.Object; - } - - public static IPhysicalProjectTreeStorage ImplementAddFileAsync(Action action) - { - var mock = new Mock(); - mock.Setup(p => p.AddFileAsync(It.IsAny())) - .ReturnsAsync(action); - - return mock.Object; - } - - public static IPhysicalProjectTreeStorage ImplementAddFolderAsync(Action action) - { - var mock = new Mock(); - mock.Setup(p => p.AddFolderAsync(It.IsAny())) - .ReturnsAsync(action); - - return mock.Object; - } - - public static IPhysicalProjectTreeStorage ImplementCreateFolderAsync(Action action) - { - var mock = new Mock(); - mock.Setup(p => p.CreateFolderAsync(It.IsAny())) - .ReturnsAsync(action); - - return mock.Object; - } + return Mock.Of(); + } + + public static IPhysicalProjectTreeStorage ImplementCreateEmptyFileAsync(Action action) + { + var mock = new Mock(); + mock.Setup(p => p.CreateEmptyFileAsync(It.IsAny())) + .ReturnsAsync(action); + + return mock.Object; + } + + public static IPhysicalProjectTreeStorage ImplementAddFileAsync(Action action) + { + var mock = new Mock(); + mock.Setup(p => p.AddFileAsync(It.IsAny())) + .ReturnsAsync(action); + + return mock.Object; + } + + public static IPhysicalProjectTreeStorage ImplementAddFolderAsync(Action action) + { + var mock = new Mock(); + mock.Setup(p => p.AddFolderAsync(It.IsAny())) + .ReturnsAsync(action); + + return mock.Object; + } + + public static IPhysicalProjectTreeStorage ImplementCreateFolderAsync(Action action) + { + var mock = new Mock(); + mock.Setup(p => p.CreateFolderAsync(It.IsAny())) + .ReturnsAsync(action); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAccessorFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAccessorFactory.cs index 43578b525e..ba460cbc63 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAccessorFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAccessorFactory.cs @@ -3,79 +3,78 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectAccessorFactory { - internal static class IProjectAccessorFactory + public static IProjectAccessor Create(string? xml = null) { - public static IProjectAccessor Create(string? xml = null) - { - var rootElement = ProjectRootElementFactory.Create(xml); + var rootElement = ProjectRootElementFactory.Create(xml); - return Create(rootElement); - } + return Create(rootElement); + } - public static IProjectAccessor Create(ProjectRootElement rootElement) - { - var evaluationProject = ProjectFactory.Create(rootElement); + public static IProjectAccessor Create(ProjectRootElement rootElement) + { + var evaluationProject = ProjectFactory.Create(rootElement); - return new ProjectAccessor(rootElement, evaluationProject); - } + return new ProjectAccessor(rootElement, evaluationProject); + } - private class ProjectAccessor : IProjectAccessor - { - private readonly ProjectRootElement _rootElement; - private readonly Project _evaluationProject; + private class ProjectAccessor : IProjectAccessor + { + private readonly ProjectRootElement _rootElement; + private readonly Project _evaluationProject; - public ProjectAccessor(ProjectRootElement element, Project project) - { - _rootElement = element; - _evaluationProject = project; - } + public ProjectAccessor(ProjectRootElement element, Project project) + { + _rootElement = element; + _evaluationProject = project; + } - public Task EnterWriteLockAsync(Func action, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } + public Task EnterWriteLockAsync(Func action, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } - public Task OpenProjectForReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) - { - var result = action(_evaluationProject); + public Task OpenProjectForReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + var result = action(_evaluationProject); - return Task.FromResult(result); - } + return Task.FromResult(result); + } - public Task OpenProjectXmlForReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) - { - var result = action(_rootElement); + public Task OpenProjectXmlForReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + var result = action(_rootElement); - return Task.FromResult(result); - } + return Task.FromResult(result); + } - public Task OpenProjectXmlForWriteAsync(UnconfiguredProject project, Action action, CancellationToken cancellationToken = default) - { - action(_rootElement); + public Task OpenProjectXmlForWriteAsync(UnconfiguredProject project, Action action, CancellationToken cancellationToken = default) + { + action(_rootElement); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task OpenProjectForWriteAsync(ConfiguredProject project, Action action, ProjectCheckoutOption option, CancellationToken cancellationToken = default) - { - action(_evaluationProject); + public Task OpenProjectForWriteAsync(ConfiguredProject project, Action action, ProjectCheckoutOption option, CancellationToken cancellationToken = default) + { + action(_evaluationProject); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task OpenProjectXmlForUpgradeableReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) - { - return action(_rootElement, cancellationToken); - } + public Task OpenProjectXmlForUpgradeableReadAsync(UnconfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + return action(_rootElement, cancellationToken); + } - public Task OpenProjectForUpgradeableReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) - { - var result = action(_evaluationProject); + public Task OpenProjectForUpgradeableReadAsync(ConfiguredProject project, Func action, CancellationToken cancellationToken = default) + { + var result = action(_evaluationProject); - return Task.FromResult(result); - } + return Task.FromResult(result); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAsynchronousTasksServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAsynchronousTasksServiceFactory.cs index d8aa0b1bf2..a32e1e7020 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAsynchronousTasksServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectAsynchronousTasksServiceFactory.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectAsynchronousTasksServiceFactory { - internal static class IProjectAsynchronousTasksServiceFactory + public static IProjectAsynchronousTasksService Create() { - public static IProjectAsynchronousTasksService Create() - { - return ImplementUnloadCancellationToken(CancellationToken.None); - } + return ImplementUnloadCancellationToken(CancellationToken.None); + } - public static IProjectAsynchronousTasksService ImplementUnloadCancellationToken(CancellationToken cancellationToken) - { - var mock = new Mock(); + public static IProjectAsynchronousTasksService ImplementUnloadCancellationToken(CancellationToken cancellationToken) + { + var mock = new Mock(); - mock.Setup(s => s.UnloadCancellationToken) - .Returns(cancellationToken); + mock.Setup(s => s.UnloadCancellationToken) + .Returns(cancellationToken); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCapabilitiesServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCapabilitiesServiceFactory.cs index 4fac162b92..a7071e2f67 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCapabilitiesServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCapabilitiesServiceFactory.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectCapabilitiesServiceFactory { - internal static class IProjectCapabilitiesServiceFactory + public static IProjectCapabilitiesService Create() { - public static IProjectCapabilitiesService Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectCapabilitiesService ImplementsContains(Func action) - { - var mock = new Mock(); + public static IProjectCapabilitiesService ImplementsContains(Func action) + { + var mock = new Mock(); - mock.Setup(s => s.Contains(It.IsAny())) - .Returns(action); + mock.Setup(s => s.Contains(It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCatalogSnapshotFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCatalogSnapshotFactory.cs index e700061406..37615ad3c7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCatalogSnapshotFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCatalogSnapshotFactory.cs @@ -3,34 +3,33 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectCatalogSnapshotFactory { - internal static class IProjectCatalogSnapshotFactory + public static IProjectCatalogSnapshot Create() { - public static IProjectCatalogSnapshot Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectCatalogSnapshot CreateWithDefaultMapping(IImmutableList itemTypes) - { - var projectCatalogSnapshot = new Mock(); + public static IProjectCatalogSnapshot CreateWithDefaultMapping(IImmutableList itemTypes) + { + var projectCatalogSnapshot = new Mock(); - var ruleNameToRule = new Dictionary(); - foreach (IItemType itemType in itemTypes) - { - ruleNameToRule.Add(itemType.Name, new Rule { DataSource = new DataSource { ItemType = itemType.Name } }); - } + var ruleNameToRule = new Dictionary(); + foreach (IItemType itemType in itemTypes) + { + ruleNameToRule.Add(itemType.Name, new Rule { DataSource = new DataSource { ItemType = itemType.Name } }); + } - var propertyPageCatalog = new Mock(); - propertyPageCatalog.Setup(o => o.GetSchema(It.IsAny())) - .Returns(ruleName => ruleNameToRule.TryGetValue(ruleName, out var rule) ? rule : new Rule { DataSource = new DataSource { ItemType = ruleName } }); + var propertyPageCatalog = new Mock(); + propertyPageCatalog.Setup(o => o.GetSchema(It.IsAny())) + .Returns(ruleName => ruleNameToRule.TryGetValue(ruleName, out var rule) ? rule : new Rule { DataSource = new DataSource { ItemType = ruleName } }); - var namedCatalogs = ImmutableDictionary.Empty.Add("File", propertyPageCatalog.Object); + var namedCatalogs = ImmutableDictionary.Empty.Add("File", propertyPageCatalog.Object); - projectCatalogSnapshot.SetupGet(o => o.NamedCatalogs).Returns(namedCatalogs); + projectCatalogSnapshot.SetupGet(o => o.NamedCatalogs).Returns(namedCatalogs); - return projectCatalogSnapshot.Object; - } + return projectCatalogSnapshot.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDescriptionFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDescriptionFactory.cs index 0f3d5af1db..bc9ea19bb5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDescriptionFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDescriptionFactory.cs @@ -2,69 +2,68 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectChangeDescriptionFactory { - internal static class IProjectChangeDescriptionFactory + public static IProjectChangeDescription Create() { - public static IProjectChangeDescription Create() - { - return FromJson( - """ - { - "Difference": { - "AnyChanges": false, - "AddedItems": [], - "ChangedItems": [], - "ChangedProperties": [], - "RemovedItems": [], - "RenamedItems": {} - }, - "Before": { - "RuleName": "SomeRule", - "EvaluationSucceeded": true, - "Properties" : {}, - "Items" : {} - }, - "After": { - "RuleName": "SomeRule", - "EvaluationSucceeded": true, - "Properties" : {}, - "Items" : {} - } + return FromJson( + """ + { + "Difference": { + "AnyChanges": false, + "AddedItems": [], + "ChangedItems": [], + "ChangedProperties": [], + "RemovedItems": [], + "RenamedItems": {} + }, + "Before": { + "RuleName": "SomeRule", + "EvaluationSucceeded": true, + "Properties" : {}, + "Items" : {} + }, + "After": { + "RuleName": "SomeRule", + "EvaluationSucceeded": true, + "Properties" : {}, + "Items" : {} } - """); - } + } + """); + } - public static IProjectChangeDescription FromJson(string jsonString) - { - var model = new IProjectChangeDescriptionModel(); - return model.FromJson(jsonString); - } + public static IProjectChangeDescription FromJson(string jsonString) + { + var model = new IProjectChangeDescriptionModel(); + return model.FromJson(jsonString); } +} - internal class IProjectChangeDescriptionModel : JsonModel +internal class IProjectChangeDescriptionModel : JsonModel +{ + public IProjectRuleSnapshotModel After { get; set; } = new IProjectRuleSnapshotModel(); + public IProjectRuleSnapshotModel Before { get; set; } = new IProjectRuleSnapshotModel(); + public IProjectChangeDiffModel Difference { get; set; } = new IProjectChangeDiffModel(); + + public override IProjectChangeDescription ToActualModel() { - public IProjectRuleSnapshotModel After { get; set; } = new IProjectRuleSnapshotModel(); - public IProjectRuleSnapshotModel Before { get; set; } = new IProjectRuleSnapshotModel(); - public IProjectChangeDiffModel Difference { get; set; } = new IProjectChangeDiffModel(); + return new ActualModel(After.ToActualModel(), Before.ToActualModel(), Difference); + } - public override IProjectChangeDescription ToActualModel() - { - return new ActualModel(After.ToActualModel(), Before.ToActualModel(), Difference); - } + private sealed class ActualModel : IProjectChangeDescription + { + public IProjectRuleSnapshot After { get; } + public IProjectRuleSnapshot Before { get; } + public IProjectChangeDiff Difference { get; } - private sealed class ActualModel : IProjectChangeDescription + public ActualModel(IProjectRuleSnapshot after, IProjectRuleSnapshot before, IProjectChangeDiff difference) { - public IProjectRuleSnapshot After { get; } - public IProjectRuleSnapshot Before { get; } - public IProjectChangeDiff Difference { get; } - - public ActualModel(IProjectRuleSnapshot after, IProjectRuleSnapshot before, IProjectChangeDiff difference) - { - After = after; - Before = before; - Difference = difference; - } + After = after; + Before = before; + Difference = difference; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDiffFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDiffFactory.cs index 703f6576d6..049f3621e9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDiffFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectChangeDiffFactory.cs @@ -1,84 +1,83 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectChangeDiffFactory { - internal static class IProjectChangeDiffFactory + public static IProjectChangeDiff WithAddedItems(string semiColonSeparatedItems) { - public static IProjectChangeDiff WithAddedItems(string semiColonSeparatedItems) - { - if (semiColonSeparatedItems.Length == 0) - return WithNoChanges(); - - return WithAddedItems(semiColonSeparatedItems.Split(';')); - } + if (semiColonSeparatedItems.Length == 0) + return WithNoChanges(); - public static IProjectChangeDiff WithAddedItems(params string[] addedItems) - { - return Create(addedItems: ImmutableHashSet.Create(StringComparers.Paths, addedItems)); - } - - public static IProjectChangeDiff WithRemovedItems(string semiColonSeparatedItems) - { - if (semiColonSeparatedItems.Length == 0) - return WithNoChanges(); + return WithAddedItems(semiColonSeparatedItems.Split(';')); + } - return WithRemovedItems(semiColonSeparatedItems.Split(';')); - } + public static IProjectChangeDiff WithAddedItems(params string[] addedItems) + { + return Create(addedItems: ImmutableHashSet.Create(StringComparers.Paths, addedItems)); + } - public static IProjectChangeDiff WithRemovedItems(params string[] removedItems) - { - return Create(removedItems: ImmutableHashSet.Create(StringComparers.Paths, removedItems)); - } + public static IProjectChangeDiff WithRemovedItems(string semiColonSeparatedItems) + { + if (semiColonSeparatedItems.Length == 0) + return WithNoChanges(); - public static IProjectChangeDiff WithRenamedItems(string semiColonSeparatedOriginalNames, string semiColonSeparatedNewNames) - { - string[] originalNames = semiColonSeparatedOriginalNames.Split(';'); - string[] newNames = semiColonSeparatedNewNames.Split(';'); + return WithRemovedItems(semiColonSeparatedItems.Split(';')); + } - var builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + public static IProjectChangeDiff WithRemovedItems(params string[] removedItems) + { + return Create(removedItems: ImmutableHashSet.Create(StringComparers.Paths, removedItems)); + } - for (int i = 0; i < originalNames.Length; i++) - { - builder.Add(originalNames[i], newNames[i]); - } + public static IProjectChangeDiff WithRenamedItems(string semiColonSeparatedOriginalNames, string semiColonSeparatedNewNames) + { + string[] originalNames = semiColonSeparatedOriginalNames.Split(';'); + string[] newNames = semiColonSeparatedNewNames.Split(';'); - return Create(renamedItems: builder.ToImmutable()); - } + var builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); - public static IProjectChangeDiff WithChangedItems(params string[] changedItems) + for (int i = 0; i < originalNames.Length; i++) { - return Create(changedItems: ImmutableHashSet.Create(StringComparers.Paths, changedItems)); + builder.Add(originalNames[i], newNames[i]); } - public static IProjectChangeDiff WithNoChanges() - { - return new ProjectChangeDiff(); - } + return Create(renamedItems: builder.ToImmutable()); + } - public static IProjectChangeDiff Create(IImmutableSet? addedItems = null, IImmutableSet? removedItems = null, IImmutableSet? changedItems = null, IImmutableDictionary? renamedItems = null) - { - return new ProjectChangeDiff(addedItems, removedItems, changedItems, renamedItems); - } + public static IProjectChangeDiff WithChangedItems(params string[] changedItems) + { + return Create(changedItems: ImmutableHashSet.Create(StringComparers.Paths, changedItems)); + } - public static IProjectChangeDiff FromJson(string jsonString) - { - var model = new IProjectChangeDiffModel(); - return model.FromJson(jsonString); - } + public static IProjectChangeDiff WithNoChanges() + { + return new ProjectChangeDiff(); } - internal class IProjectChangeDiffModel : JsonModel, IProjectChangeDiff + public static IProjectChangeDiff Create(IImmutableSet? addedItems = null, IImmutableSet? removedItems = null, IImmutableSet? changedItems = null, IImmutableDictionary? renamedItems = null) { - public IImmutableSet AddedItems { get; set; } = ImmutableHashSet.Empty; - public bool AnyChanges { get; set; } - public IImmutableSet ChangedItems { get; set; } = ImmutableHashSet.Empty; - public IImmutableSet ChangedProperties { get; set; } = ImmutableHashSet.Empty; - public IImmutableSet RemovedItems { get; set; } = ImmutableHashSet.Empty; - public IImmutableDictionary RenamedItems { get; set; } = ImmutableDictionary.Empty; - - public override IProjectChangeDiff ToActualModel() - { - return this; - } + return new ProjectChangeDiff(addedItems, removedItems, changedItems, renamedItems); + } + + public static IProjectChangeDiff FromJson(string jsonString) + { + var model = new IProjectChangeDiffModel(); + return model.FromJson(jsonString); + } +} + +internal class IProjectChangeDiffModel : JsonModel, IProjectChangeDiff +{ + public IImmutableSet AddedItems { get; set; } = ImmutableHashSet.Empty; + public bool AnyChanges { get; set; } + public IImmutableSet ChangedItems { get; set; } = ImmutableHashSet.Empty; + public IImmutableSet ChangedProperties { get; set; } = ImmutableHashSet.Empty; + public IImmutableSet RemovedItems { get; set; } = ImmutableHashSet.Empty; + public IImmutableDictionary RenamedItems { get; set; } = ImmutableDictionary.Empty; + + public override IProjectChangeDiff ToActualModel() + { + return this; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCommonServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCommonServicesFactory.cs index 584041a635..d49abaf6fc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCommonServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectCommonServicesFactory.cs @@ -1,27 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectCommonServicesFactory { - internal static class IProjectCommonServicesFactory + public static IProjectCommonServices CreateWithDefaultThreadingPolicy() { - public static IProjectCommonServices CreateWithDefaultThreadingPolicy() - { - return Create(null); - } + return Create(null); + } - public static IProjectCommonServices Create(IProjectThreadingService? threadingService = null, IProjectLockService? projectLockService = null) - { - threadingService ??= IProjectThreadingServiceFactory.Create(); + public static IProjectCommonServices Create(IProjectThreadingService? threadingService = null, IProjectLockService? projectLockService = null) + { + threadingService ??= IProjectThreadingServiceFactory.Create(); - var services = ProjectServicesFactory.Create(threadingService: threadingService, projectLockService: projectLockService); - var projectService = IProjectServiceFactory.Create(services); + var services = ProjectServicesFactory.Create(threadingService: threadingService, projectLockService: projectLockService); + var projectService = IProjectServiceFactory.Create(services); - var mock = new Mock(); + var mock = new Mock(); - mock.SetupGet(s => s.ProjectService) - .Returns(projectService); + mock.SetupGet(s => s.ProjectService) + .Returns(projectService); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectConfigurationsServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectConfigurationsServiceFactory.cs index 370e3ec048..4befaba36b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectConfigurationsServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectConfigurationsServiceFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectConfigurationsServiceFactory { - internal static class IProjectConfigurationsServiceFactory + public static IProjectConfigurationsService ImplementGetKnownProjectConfigurationsAsync(IImmutableSet action) { - public static IProjectConfigurationsService ImplementGetKnownProjectConfigurationsAsync(IImmutableSet action) - { - var mock = new Mock(); - mock.Setup(p => p.GetKnownProjectConfigurationsAsync()) - .ReturnsAsync(action); + var mock = new Mock(); + mock.Setup(p => p.GetKnownProjectConfigurationsAsync()) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDependenciesSubTreeProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDependenciesSubTreeProviderFactory.cs index 2a02c3e5ee..fd1d790a07 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDependenciesSubTreeProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDependenciesSubTreeProviderFactory.cs @@ -2,28 +2,27 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectDependenciesSubTreeProviderFactory { - internal static class IProjectDependenciesSubTreeProviderFactory + public static IProjectDependenciesSubTreeProvider Implement( + string? providerType = null, + IDependencyModel? createRootDependencyNode = null, + MockBehavior mockBehavior = MockBehavior.Strict) { - public static IProjectDependenciesSubTreeProvider Implement( - string? providerType = null, - IDependencyModel? createRootDependencyNode = null, - MockBehavior mockBehavior = MockBehavior.Strict) - { - var mock = new Mock(mockBehavior); + var mock = new Mock(mockBehavior); - if (providerType is not null) - { - mock.Setup(x => x.ProviderType).Returns(providerType); - } - - if (createRootDependencyNode is not null) - { - mock.Setup(x => x.CreateRootDependencyNode()).Returns(createRootDependencyNode); - } + if (providerType is not null) + { + mock.Setup(x => x.ProviderType).Returns(providerType); + } - return mock.Object; + if (createRootDependencyNode is not null) + { + mock.Setup(x => x.CreateRootDependencyNode()).Returns(createRootDependencyNode); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDesignerServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDesignerServiceFactory.cs index 52a52dda69..35115a7346 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDesignerServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectDesignerServiceFactory.cs @@ -1,31 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static class IProjectDesignerServiceFactory { - internal static class IProjectDesignerServiceFactory + public static IProjectDesignerService Create() { - public static IProjectDesignerService Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectDesignerService ImplementSupportsProjectDesigner(Func action) - { - var mock = new Mock(); + public static IProjectDesignerService ImplementSupportsProjectDesigner(Func action) + { + var mock = new Mock(); - mock.SetupGet(f => f.SupportsProjectDesigner) - .Returns(action); + mock.SetupGet(f => f.SupportsProjectDesigner) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectDesignerService ImplementShowProjectDesignerAsync(Action action) - { - var mock = new Mock(); - mock.Setup(s => s.ShowProjectDesignerAsync()) - .ReturnsAsync(action); + public static IProjectDesignerService ImplementShowProjectDesignerAsync(Action action) + { + var mock = new Mock(); + mock.Setup(s => s.ShowProjectDesignerAsync()) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectFaultHandlerServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectFaultHandlerServiceFactory.cs index 1391c50421..84c8aef475 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectFaultHandlerServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectFaultHandlerServiceFactory.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectFaultHandlerServiceFactory { - internal static class IProjectFaultHandlerServiceFactory + public static IProjectFaultHandlerService Create() { - public static IProjectFaultHandlerService Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectFaultHandlerService ImplementHandleFaultAsync(Action action) - { - var mock = new Mock(); - mock.Setup(s => s.HandleFaultAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(action); + public static IProjectFaultHandlerService ImplementHandleFaultAsync(Action action) + { + var mock = new Mock(); + mock.Setup(s => s.HandleFaultAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectFaultHandlerService ImplementForget(Action action) - { - var mock = new Mock(); - mock.Setup(s => s.RegisterFaultHandler(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(action); + public static IProjectFaultHandlerService ImplementForget(Action action) + { + var mock = new Mock(); + mock.Setup(s => s.RegisterFaultHandler(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectImageProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectImageProviderFactory.cs index 9f40e28c68..6190113ec8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectImageProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectImageProviderFactory.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +internal static class IProjectImageProviderFactory { - internal static class IProjectImageProviderFactory + public static IProjectImageProvider Create() { - public static IProjectImageProvider Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectImageProvider ImplementGetProjectImage(Func action) - { - var mock = new Mock(); - mock.Setup(p => p.GetProjectImage(It.IsAny())) - .Returns(action); + public static IProjectImageProvider ImplementGetProjectImage(Func action) + { + var mock = new Mock(); + mock.Setup(p => p.GetProjectImage(It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectImageProvider ImplementGetProjectImage(string key, ProjectImageMoniker? moniker) - { - return ImplementGetProjectImage(k => k == key ? moniker : null); - } + public static IProjectImageProvider ImplementGetProjectImage(string key, ProjectImageMoniker? moniker) + { + return ImplementGetProjectImage(k => k == key ? moniker : null); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectInstancePropertiesProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectInstancePropertiesProviderFactory.cs index dd9a03af1c..0ff04bc0dd 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectInstancePropertiesProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectInstancePropertiesProviderFactory.cs @@ -3,31 +3,30 @@ using Microsoft.Build.Execution; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectInstancePropertiesProviderFactory { - internal static class IProjectInstancePropertiesProviderFactory - { - public static IProjectInstancePropertiesProvider Create() - => Mock.Of(); + public static IProjectInstancePropertiesProvider Create() + => Mock.Of(); - public static IProjectInstancePropertiesProvider ImplementsGetItemTypeProperties(IProjectProperties? projectProperties = null) - { - var mock = new Mock(); + public static IProjectInstancePropertiesProvider ImplementsGetItemTypeProperties(IProjectProperties? projectProperties = null) + { + var mock = new Mock(); - mock.Setup(d => d.GetItemTypeProperties(It.IsAny(), It.IsAny())) - .Returns(() => projectProperties ?? Mock.Of()); + mock.Setup(d => d.GetItemTypeProperties(It.IsAny(), It.IsAny())) + .Returns(() => projectProperties ?? Mock.Of()); - return mock.Object; - } + return mock.Object; + } - public static IProjectInstancePropertiesProvider ImplementsGetCommonProperties(IProjectProperties? projectProperties = null) - { - var mock = new Mock(); + public static IProjectInstancePropertiesProvider ImplementsGetCommonProperties(IProjectProperties? projectProperties = null) + { + var mock = new Mock(); - mock.Setup(d => d.GetCommonProperties(It.IsAny())) - .Returns(() => projectProperties ?? Mock.Of()); + mock.Setup(d => d.GetCommonProperties(It.IsAny())) + .Returns(() => projectProperties ?? Mock.Of()); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemFactory.cs index 03d3f5e385..f2e1a38163 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemFactory.cs @@ -2,28 +2,27 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectItemFactory { - internal static class IProjectItemFactory + public static IProjectItem Create(string evaluatedInclude, IProjectProperties properties, bool isProjectFile = true) { - public static IProjectItem Create(string evaluatedInclude, IProjectProperties properties, bool isProjectFile = true) - { - var projectItem = new Mock(); + var projectItem = new Mock(); - projectItem.SetupGet(o => o.EvaluatedInclude) - .Returns(evaluatedInclude); - projectItem.SetupGet(o => o.Metadata) - .Returns(properties); + projectItem.SetupGet(o => o.EvaluatedInclude) + .Returns(evaluatedInclude); + projectItem.SetupGet(o => o.Metadata) + .Returns(properties); - projectItem.Setup(o => o.SetUnevaluatedIncludeAsync(It.IsAny())) - .Returns(Task.CompletedTask); - projectItem.Setup(o => o.RemoveAsync(It.IsAny())) - .Returns(Task.CompletedTask); - var propertiesContext = IProjectPropertiesContextFactory.Create(isProjectFile); - projectItem.SetupGet(o => o.PropertiesContext) - .Returns(propertiesContext); + projectItem.Setup(o => o.SetUnevaluatedIncludeAsync(It.IsAny())) + .Returns(Task.CompletedTask); + projectItem.Setup(o => o.RemoveAsync(It.IsAny())) + .Returns(Task.CompletedTask); + var propertiesContext = IProjectPropertiesContextFactory.Create(isProjectFile); + projectItem.SetupGet(o => o.PropertiesContext) + .Returns(propertiesContext); - return projectItem.Object; - } + return projectItem.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemProviderFactory.cs index b6f8ba7739..ee3593e9bc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemProviderFactory.cs @@ -2,67 +2,66 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectItemProviderFactory { - internal static class IProjectItemProviderFactory + public static IProjectItemProvider Create() { - public static IProjectItemProvider Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectItemProvider AddItemAsync(Func action) - { - var mock = new Mock(); - mock.Setup(p => p.AddAsync(It.IsAny())) - .ReturnsAsync(action); + public static IProjectItemProvider AddItemAsync(Func action) + { + var mock = new Mock(); + mock.Setup(p => p.AddAsync(It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectItemProvider CreateWithAdd(IProjectTree inputTree) - { - var mock = new Mock(); + public static IProjectItemProvider CreateWithAdd(IProjectTree inputTree) + { + var mock = new Mock(); - mock.Setup(a => a.AddAsync(It.IsAny())) - .Returns(path => - { - var fileName = Path.GetFileName(path); - var parentFolder = Path.GetDirectoryName(path); - var newSubTree = ProjectTreeParser.Parse($@"{fileName}, FilePath: ""{path}"""); + mock.Setup(a => a.AddAsync(It.IsAny())) + .Returns(path => + { + var fileName = Path.GetFileName(path); + var parentFolder = Path.GetDirectoryName(path); + var newSubTree = ProjectTreeParser.Parse($@"{fileName}, FilePath: ""{path}"""); - // Find the node that has the parent folder and add the new node as a child. - foreach (var node in inputTree.GetSelfAndDescendentsBreadthFirst()) + // Find the node that has the parent folder and add the new node as a child. + foreach (var node in inputTree.GetSelfAndDescendentsBreadthFirst()) + { + string? nodeFolderPath = node.IsFolder ? node.FilePath : Path.GetDirectoryName(node.FilePath); + if (nodeFolderPath?.TrimEnd(Path.DirectorySeparatorChar) == parentFolder) { - string? nodeFolderPath = node.IsFolder ? node.FilePath : Path.GetDirectoryName(node.FilePath); - if (nodeFolderPath?.TrimEnd(Path.DirectorySeparatorChar) == parentFolder) + if (node.TryFindImmediateChild(fileName, out IProjectTree? child) && !child.Flags.IsIncludedInProject()) + { + var newFlags = child.Flags.Remove(ProjectTreeFlags.Common.IncludeInProjectCandidate); + child.SetProperties(flags: newFlags); + } + else { - if (node.TryFindImmediateChild(fileName, out IProjectTree? child) && !child.Flags.IsIncludedInProject()) - { - var newFlags = child.Flags.Remove(ProjectTreeFlags.Common.IncludeInProjectCandidate); - child.SetProperties(flags: newFlags); - } - else - { - node.Add(newSubTree); - } - return Task.FromResult(Mock.Of()); + node.Add(newSubTree); } + return Task.FromResult(Mock.Of()); } + } - return TaskResult.Null()!; // TODO remove ! when CPS annotations updated - }); + return TaskResult.Null()!; // TODO remove ! when CPS annotations updated + }); - return mock.Object; - } + return mock.Object; + } - public static IProjectItemProvider GetItemsAsync(Func> action) - { - var mock = new Mock(); - mock.Setup(p => p.GetItemsAsync(It.IsAny())) - .ReturnsAsync(action); + public static IProjectItemProvider GetItemsAsync(Func> action) + { + var mock = new Mock(); + mock.Setup(p => p.GetItemsAsync(It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaFactory.cs index 9eb86d4cde..ce55c893ab 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaFactory.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectItemSchemaFactory { - internal static class IProjectItemSchemaFactory + public static IProjectItemSchema Create(IImmutableList itemTypes) { - public static IProjectItemSchema Create(IImmutableList itemTypes) - { - var projectItemSchema = new Mock(); + var projectItemSchema = new Mock(); - projectItemSchema.Setup(o => o.GetKnownItemTypes()) - .Returns(() => ImmutableHashSet.Empty.Union(itemTypes.Select(i => i.Name))); + projectItemSchema.Setup(o => o.GetKnownItemTypes()) + .Returns(() => ImmutableHashSet.Empty.Union(itemTypes.Select(i => i.Name))); - projectItemSchema.Setup(o => o.GetItemType(It.IsAny())) - .Returns((string itemTypeName) => itemTypes.FirstOrDefault(i => i.Name == itemTypeName)); + projectItemSchema.Setup(o => o.GetItemType(It.IsAny())) + .Returns((string itemTypeName) => itemTypes.FirstOrDefault(i => i.Name == itemTypeName)); - return projectItemSchema.Object; - } + return projectItemSchema.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaServiceFactory.cs index 63581c1ce6..9c0e4b2f75 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectItemSchemaServiceFactory.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectItemSchemaServiceFactory { - internal static class IProjectItemSchemaServiceFactory + public static IProjectItemSchemaService Create() { - public static IProjectItemSchemaService Create() - { - var projectItemSchemaService = new Mock(); + var projectItemSchemaService = new Mock(); - projectItemSchemaService.SetupGet(o => o.SourceBlock) - .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); + projectItemSchemaService.SetupGet(o => o.SourceBlock) + .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); - return projectItemSchemaService.Object; - } + return projectItemSchemaService.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLockServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLockServiceFactory.cs index eebf8fe2f4..9aa546dc18 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLockServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLockServiceFactory.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectLockServiceFactory { - internal static class IProjectLockServiceFactory + public static IProjectLockService Create() { - public static IProjectLockService Create() - { - return Mock.Of(); - } + return Mock.Of(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLoggerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLoggerFactory.cs index 2fe8c99394..6200d0e5ea 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLoggerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectLoggerFactory.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IManagedProjectDiagnosticOutputServiceFactory { - internal static class IManagedProjectDiagnosticOutputServiceFactory + public static IManagedProjectDiagnosticOutputService Create() { - public static IManagedProjectDiagnosticOutputService Create() - { - return Mock.Of(); - } + return Mock.Of(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesContextFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesContextFactory.cs index 5640b16561..e5653857af 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesContextFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesContextFactory.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectPropertiesContextFactory { - internal static class IProjectPropertiesContextFactory + public static IProjectPropertiesContext Create(bool isProjectFile) { - public static IProjectPropertiesContext Create(bool isProjectFile) - { - var projectItem = new Mock(); + var projectItem = new Mock(); - projectItem.SetupGet(o => o.IsProjectFile) - .Returns(isProjectFile); + projectItem.SetupGet(o => o.IsProjectFile) + .Returns(isProjectFile); - return projectItem.Object; - } + return projectItem.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesFactory.cs index 0893f4253a..391a75d582 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesFactory.cs @@ -2,80 +2,79 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectPropertiesFactory { - internal static class IProjectPropertiesFactory + public static Mock MockWithProperty(string propertyName) { - public static Mock MockWithProperty(string propertyName) - { - return MockWithProperties(ImmutableArray.Create(propertyName)); - } + return MockWithProperties(ImmutableArray.Create(propertyName)); + } - public static Mock MockWithProperties(IEnumerable propertyNames) - { - var mock = new Mock(); + public static Mock MockWithProperties(IEnumerable propertyNames) + { + var mock = new Mock(); - mock.Setup(t => t.GetPropertyNamesAsync()) - .ReturnsAsync(propertyNames); + mock.Setup(t => t.GetPropertyNamesAsync()) + .ReturnsAsync(propertyNames); - return mock; - } + return mock; + } - public static Mock MockWithPropertyAndValue(string propertyName, string setValue) - { - return MockWithPropertiesAndValues(new Dictionary() { { propertyName, setValue } }); - } + public static Mock MockWithPropertyAndValue(string propertyName, string setValue) + { + return MockWithPropertiesAndValues(new Dictionary() { { propertyName, setValue } }); + } - public static Mock MockWithPropertiesAndValues(IDictionary propertyNameAndValues, HashSet? inheritedPropertyNames = null) + public static Mock MockWithPropertiesAndValues(IDictionary propertyNameAndValues, HashSet? inheritedPropertyNames = null) + { + var mock = MockWithProperties(propertyNameAndValues.Keys); + + // evaluated properties are never null + mock.Setup(t => t.GetEvaluatedPropertyValueAsync(It.IsAny())) + .Returns(k => Task.FromResult(propertyNameAndValues.TryGetValue(k, out var v) ? v ?? "" : "")); + + mock.Setup(t => t.GetUnevaluatedPropertyValueAsync( + It.IsIn(propertyNameAndValues.Keys))) + .Returns(k => Task.FromResult(propertyNameAndValues[k])); + + mock.Setup(t => t.SetPropertyValueAsync( + It.IsIn(propertyNameAndValues.Keys), + It.IsAny(), null)) + .Returns>((k, v, d) => + { + propertyNameAndValues[k] = v; + + inheritedPropertyNames?.Remove(k); + + return Task.CompletedTask; + }); + + mock.Setup(t => t.DeletePropertyAsync( + It.IsIn(propertyNameAndValues.Keys), + null)) + .Returns>((propName, d) => + { + propertyNameAndValues[propName] = null; + return Task.CompletedTask; + }); + + if (inheritedPropertyNames is not null) { - var mock = MockWithProperties(propertyNameAndValues.Keys); - - // evaluated properties are never null - mock.Setup(t => t.GetEvaluatedPropertyValueAsync(It.IsAny())) - .Returns(k => Task.FromResult(propertyNameAndValues.TryGetValue(k, out var v) ? v ?? "" : "")); - - mock.Setup(t => t.GetUnevaluatedPropertyValueAsync( + mock.Setup(t => t.IsValueInheritedAsync( It.IsIn(propertyNameAndValues.Keys))) - .Returns(k => Task.FromResult(propertyNameAndValues[k])); - - mock.Setup(t => t.SetPropertyValueAsync( - It.IsIn(propertyNameAndValues.Keys), - It.IsAny(), null)) - .Returns>((k, v, d) => - { - propertyNameAndValues[k] = v; - - inheritedPropertyNames?.Remove(k); - - return Task.CompletedTask; - }); - - mock.Setup(t => t.DeletePropertyAsync( - It.IsIn(propertyNameAndValues.Keys), - null)) - .Returns>((propName, d) => - { - propertyNameAndValues[propName] = null; - return Task.CompletedTask; - }); - - if (inheritedPropertyNames is not null) - { - mock.Setup(t => t.IsValueInheritedAsync( - It.IsIn(propertyNameAndValues.Keys))) - .Returns(k => Task.FromResult(inheritedPropertyNames.Contains(k))); - } - - return mock; + .Returns(k => Task.FromResult(inheritedPropertyNames.Contains(k))); } - public static IProjectProperties CreateWithProperty(string propertyName) - => MockWithProperty(propertyName).Object; + return mock; + } + + public static IProjectProperties CreateWithProperty(string propertyName) + => MockWithProperty(propertyName).Object; - public static IProjectProperties CreateWithPropertyAndValue(string propertyName, string setValue) - => MockWithPropertyAndValue(propertyName, setValue).Object; + public static IProjectProperties CreateWithPropertyAndValue(string propertyName, string setValue) + => MockWithPropertyAndValue(propertyName, setValue).Object; - public static IProjectProperties CreateWithPropertiesAndValues(IDictionary propertyNameAndValues, HashSet? inheritedPropertyNames = null) - => MockWithPropertiesAndValues(propertyNameAndValues, inheritedPropertyNames).Object; - } + public static IProjectProperties CreateWithPropertiesAndValues(IDictionary propertyNameAndValues, HashSet? inheritedPropertyNames = null) + => MockWithPropertiesAndValues(propertyNameAndValues, inheritedPropertyNames).Object; } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesProviderFactory.cs index fc503e2630..006ac0eb52 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectPropertiesProviderFactory.cs @@ -2,26 +2,25 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectPropertiesProviderFactory { - internal static class IProjectPropertiesProviderFactory + public static IProjectPropertiesProvider Create(IProjectProperties? props = null, IProjectProperties? commonProps = null) { - public static IProjectPropertiesProvider Create(IProjectProperties? props = null, IProjectProperties? commonProps = null) - { - var mock = new Mock(); + var mock = new Mock(); - if (props is not null) - { - mock.Setup(t => t.GetProperties(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(props); - } - - if (commonProps is not null) - { - mock.Setup(t => t.GetCommonProperties()).Returns(commonProps); - } + if (props is not null) + { + mock.Setup(t => t.GetProperties(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(props); + } - return mock.Object; + if (commonProps is not null) + { + mock.Setup(t => t.GetCommonProperties()).Returns(commonProps); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotFactory.cs index df31e7f40d..dd55104c26 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotFactory.cs @@ -1,77 +1,76 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static class IProjectRuleSnapshotFactory { - internal static class IProjectRuleSnapshotFactory + public static IProjectRuleSnapshot Create(string ruleName, string propertyName, string propertyValue, IImmutableDictionary>? items = null) { - public static IProjectRuleSnapshot Create(string ruleName, string propertyName, string propertyValue, IImmutableDictionary>? items = null) - { - var mock = new Mock(); - mock.SetupGet(r => r.RuleName) - .Returns(ruleName); - - var dictionary = ImmutableStringDictionary.EmptyOrdinal.Add(propertyName, propertyValue); + var mock = new Mock(); + mock.SetupGet(r => r.RuleName) + .Returns(ruleName); - mock.SetupGet(r => r.Properties) - .Returns(dictionary); + var dictionary = ImmutableStringDictionary.EmptyOrdinal.Add(propertyName, propertyValue); - if (items is not null) - { - mock.SetupGet(c => c.Items).Returns(items); - } + mock.SetupGet(r => r.Properties) + .Returns(dictionary); - return mock.Object; + if (items is not null) + { + mock.SetupGet(c => c.Items).Returns(items); } - public static IProjectRuleSnapshot Add(this IProjectRuleSnapshot snapshot, string propertyName, string propertyValue) - { - var mock = Mock.Get(snapshot); + return mock.Object; + } - var dictionary = snapshot.Properties.Add(propertyName, propertyValue); + public static IProjectRuleSnapshot Add(this IProjectRuleSnapshot snapshot, string propertyName, string propertyValue) + { + var mock = Mock.Get(snapshot); - mock.SetupGet(r => r.Properties) - .Returns(dictionary); + var dictionary = snapshot.Properties.Add(propertyName, propertyValue); - return mock.Object; - } + mock.SetupGet(r => r.Properties) + .Returns(dictionary); - public static IProjectRuleSnapshot FromJson(string jsonString) - { - var model = new IProjectRuleSnapshotModel(); - return model.FromJson(jsonString); - } + return mock.Object; } - internal class IProjectRuleSnapshotModel : JsonModel + public static IProjectRuleSnapshot FromJson(string jsonString) { - public Dictionary> Items { get; set; } = new(); - public Dictionary Properties { get; set; } = new(); - public string RuleName { get; set; } = ""; - public bool EvaluationSucceeded { get; set; } = true; + var model = new IProjectRuleSnapshotModel(); + return model.FromJson(jsonString); + } +} - public override IProjectRuleSnapshot ToActualModel() - { - return new ActualModel( - new ImmutableOrderedDictionary>(Items), - new ImmutableOrderedDictionary(Properties), - RuleName, - EvaluationSucceeded); - } +internal class IProjectRuleSnapshotModel : JsonModel +{ + public Dictionary> Items { get; set; } = new(); + public Dictionary Properties { get; set; } = new(); + public string RuleName { get; set; } = ""; + public bool EvaluationSucceeded { get; set; } = true; - private sealed class ActualModel : IProjectRuleSnapshot, IProjectRuleSnapshotEvaluationStatus - { - public IImmutableDictionary> Items { get; } - public IImmutableDictionary Properties { get; } - public string RuleName { get; } - public bool EvaluationSucceeded { get; } + public override IProjectRuleSnapshot ToActualModel() + { + return new ActualModel( + new ImmutableOrderedDictionary>(Items), + new ImmutableOrderedDictionary(Properties), + RuleName, + EvaluationSucceeded); + } - public ActualModel(IImmutableDictionary> items, IImmutableDictionary properties, string ruleName, bool evaluationSucceeded) - { - Items = items; - Properties = properties; - RuleName = ruleName; - EvaluationSucceeded = evaluationSucceeded; - } + private sealed class ActualModel : IProjectRuleSnapshot, IProjectRuleSnapshotEvaluationStatus + { + public IImmutableDictionary> Items { get; } + public IImmutableDictionary Properties { get; } + public string RuleName { get; } + public bool EvaluationSucceeded { get; } + + public ActualModel(IImmutableDictionary> items, IImmutableDictionary properties, string ruleName, bool evaluationSucceeded) + { + Items = items; + Properties = properties; + RuleName = ruleName; + EvaluationSucceeded = evaluationSucceeded; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotsFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotsFactory.cs index 4a7c86a27c..35f0eafdf4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotsFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectRuleSnapshotsFactory.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static class IProjectRuleSnapshotsFactory { - internal static class IProjectRuleSnapshotsFactory + public static IImmutableDictionary Create() { - public static IImmutableDictionary Create() - { - return ImmutableStringDictionary.EmptyOrdinal; - } + return ImmutableStringDictionary.EmptyOrdinal; + } - public static IImmutableDictionary Add(this IImmutableDictionary snapshots, string ruleName, string propertyName, string propertyValue) + public static IImmutableDictionary Add(this IImmutableDictionary snapshots, string ruleName, string propertyName, string propertyValue) + { + if (!snapshots.TryGetValue(ruleName, out IProjectRuleSnapshot snapshot)) { - if (!snapshots.TryGetValue(ruleName, out IProjectRuleSnapshot snapshot)) - { - snapshot = IProjectRuleSnapshotFactory.Create(ruleName, propertyName, propertyValue); - return snapshots.Add(ruleName, snapshot); - } - - return snapshots.SetItem(ruleName, snapshot.Add(propertyName, propertyValue)); + snapshot = IProjectRuleSnapshotFactory.Create(ruleName, propertyName, propertyValue); + return snapshots.Add(ruleName, snapshot); } + + return snapshots.SetItem(ruleName, snapshot.Add(propertyName, propertyValue)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceAccessorFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceAccessorFactory.cs index 54fa0ec11f..9554221b9d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceAccessorFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceAccessorFactory.cs @@ -2,24 +2,23 @@ using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class IProjectServiceAccessorFactory { - internal static class IProjectServiceAccessorFactory + public static IProjectServiceAccessor Create(IProjectCapabilitiesScope? scope = null, ConfiguredProject? configuredProject = null) { - public static IProjectServiceAccessor Create(IProjectCapabilitiesScope? scope = null, ConfiguredProject? configuredProject = null) - { - var mock = new Mock(); - mock.Setup(s => s.GetProjectService(It.IsAny())) - .Returns(() => IProjectServiceFactory.Create(scope: scope, configuredProject: configuredProject)); - return mock.Object; - } + var mock = new Mock(); + mock.Setup(s => s.GetProjectService(It.IsAny())) + .Returns(() => IProjectServiceFactory.Create(scope: scope, configuredProject: configuredProject)); + return mock.Object; + } - public static IProjectServiceAccessor Create(IEnumerable loadedUnconfiguredProjects, IProjectCapabilitiesScope? scope = null) - { - var mock = new Mock(); - mock.Setup(s => s.GetProjectService(It.IsAny())) - .Returns(() => IProjectServiceFactory.Create(scope: scope, loadedUnconfiguredProjects: loadedUnconfiguredProjects)); - return mock.Object; - } + public static IProjectServiceAccessor Create(IEnumerable loadedUnconfiguredProjects, IProjectCapabilitiesScope? scope = null) + { + var mock = new Mock(); + mock.Setup(s => s.GetProjectService(It.IsAny())) + .Returns(() => IProjectServiceFactory.Create(scope: scope, loadedUnconfiguredProjects: loadedUnconfiguredProjects)); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceFactory.cs index 97f160c58e..1a88155492 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectServiceFactory.cs @@ -1,40 +1,39 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectServiceFactory { - internal static class IProjectServiceFactory + public static IProjectService Create(ProjectServices? services = null, IProjectCapabilitiesScope? scope = null, ConfiguredProject? configuredProject = null) { - public static IProjectService Create(ProjectServices? services = null, IProjectCapabilitiesScope? scope = null, ConfiguredProject? configuredProject = null) - { - var mock = new Mock(); - - services ??= ProjectServicesFactory.Create(projectService: mock.Object); + var mock = new Mock(); - mock.Setup(p => p.Services) - .Returns(services); + services ??= ProjectServicesFactory.Create(projectService: mock.Object); - if (scope is not null) - { - mock.Setup(p => p.LoadedUnconfiguredProjects) - .Returns(new[] { UnconfiguredProjectFactory.Create(scope: scope, configuredProject: configuredProject) }); - } + mock.Setup(p => p.Services) + .Returns(services); - return mock.Object; + if (scope is not null) + { + mock.Setup(p => p.LoadedUnconfiguredProjects) + .Returns(new[] { UnconfiguredProjectFactory.Create(scope: scope, configuredProject: configuredProject) }); } - public static IProjectService Create(IEnumerable loadedUnconfiguredProjects, ProjectServices? services = null, IProjectCapabilitiesScope? scope = null) - { - var mock = new Mock(); + return mock.Object; + } - services ??= ProjectServicesFactory.Create(projectService: mock.Object); + public static IProjectService Create(IEnumerable loadedUnconfiguredProjects, ProjectServices? services = null, IProjectCapabilitiesScope? scope = null) + { + var mock = new Mock(); - mock.Setup(p => p.Services) - .Returns(services); + services ??= ProjectServicesFactory.Create(projectService: mock.Object); - mock.Setup(p => p.LoadedUnconfiguredProjects) - .Returns(loadedUnconfiguredProjects); + mock.Setup(p => p.Services) + .Returns(services); - return mock.Object; - } + mock.Setup(p => p.LoadedUnconfiguredProjects) + .Returns(loadedUnconfiguredProjects); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSnapshot2Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSnapshot2Factory.cs index f649f3a1a9..2e29a8d3a5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSnapshot2Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSnapshot2Factory.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectSnapshot2Factory { - internal static class IProjectSnapshot2Factory + public static IProjectSnapshot2 Create( + IImmutableDictionary? dependentFileTimes = null) { - public static IProjectSnapshot2 Create( - IImmutableDictionary? dependentFileTimes = null) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(s => s.AdditionalDependentFileTimes) - .Returns(dependentFileTimes ?? ImmutableDictionary.Empty); + mock.Setup(s => s.AdditionalDependentFileTimes) + .Returns(dependentFileTimes ?? ImmutableDictionary.Empty); - return mock.Object; - } + return mock.Object; + } - public static IProjectSnapshot2 WithAdditionalDependentFileTime(string path, DateTime fileTime) - { - var fileTimes = ImmutableDictionary.Empty.Add(path, fileTime); + public static IProjectSnapshot2 WithAdditionalDependentFileTime(string path, DateTime fileTime) + { + var fileTimes = ImmutableDictionary.Empty.Add(path, fileTime); - var mock = new Mock(); - mock.Setup(s => s.AdditionalDependentFileTimes) - .Returns(fileTimes); + var mock = new Mock(); + mock.Setup(s => s.AdditionalDependentFileTimes) + .Returns(fileTimes); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionServiceFactory.cs index 7e7b74d5cb..2830af4ca9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionServiceFactory.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectSubscriptionServiceFactory { - internal static class IProjectSubscriptionServiceFactory + public static IProjectSubscriptionService Create() { - public static IProjectSubscriptionService Create() - { - var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); - var ruleSource = ProjectValueDataSourceFactory.Create(services); - var projectSource = ProjectValueDataSourceFactory.Create(services); + var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); + var ruleSource = ProjectValueDataSourceFactory.Create(services); + var projectSource = ProjectValueDataSourceFactory.Create(services); - var mock = new Mock(); - mock.SetupGet(s => s.ProjectRuleSource) - .Returns(ruleSource); + var mock = new Mock(); + mock.SetupGet(s => s.ProjectRuleSource) + .Returns(ruleSource); - mock.SetupGet(s => s.ProjectBuildRuleSource) - .Returns(ruleSource); + mock.SetupGet(s => s.ProjectBuildRuleSource) + .Returns(ruleSource); - mock.SetupGet(s => s.ProjectSource) - .Returns(projectSource); + mock.SetupGet(s => s.ProjectSource) + .Returns(projectSource); - mock.SetupGet(s => s.SourceItemsRuleSource) - .Returns(ruleSource); + mock.SetupGet(s => s.SourceItemsRuleSource) + .Returns(ruleSource); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionUpdateFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionUpdateFactory.cs index 97e22a62f7..41017ce0d6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionUpdateFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSubscriptionUpdateFactory.cs @@ -2,103 +2,102 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectSubscriptionUpdateFactory { - internal static class IProjectSubscriptionUpdateFactory + public static IProjectVersionedValue CreateEmptyVersionedValue() { - public static IProjectVersionedValue CreateEmptyVersionedValue() - { - return IProjectVersionedValueFactory.Create(Mock.Of()); - } + return IProjectVersionedValueFactory.Create(Mock.Of()); + } - public static IProjectSubscriptionUpdate Implement( - IDictionary? currentState = null, - IDictionary? projectChanges = null, - MockBehavior mockBehavior = MockBehavior.Default) - { - var mock = new Mock(mockBehavior); + public static IProjectSubscriptionUpdate Implement( + IDictionary? currentState = null, + IDictionary? projectChanges = null, + MockBehavior mockBehavior = MockBehavior.Default) + { + var mock = new Mock(mockBehavior); - mock.Setup(x => x.CurrentState).Returns(currentState?.ToImmutableDictionary() ?? ImmutableDictionary.Empty); + mock.Setup(x => x.CurrentState).Returns(currentState?.ToImmutableDictionary() ?? ImmutableDictionary.Empty); - mock.Setup(x => x.ProjectChanges).Returns(projectChanges?.ToImmutableDictionary() ?? ImmutableDictionary.Empty); + mock.Setup(x => x.ProjectChanges).Returns(projectChanges?.ToImmutableDictionary() ?? ImmutableDictionary.Empty); - return mock.Object; - } + return mock.Object; + } - public static IProjectSubscriptionUpdate CreateEmpty() - { - return FromJson( - """ - { - "CurrentState": { - }, - "ProjectChanges": { - } + public static IProjectSubscriptionUpdate CreateEmpty() + { + return FromJson( + """ + { + "CurrentState": { + }, + "ProjectChanges": { } - """); - } + } + """); + } - public static IProjectSubscriptionUpdate FromJson(string jsonString) - { - var model = new IProjectSubscriptionUpdateModel(); - return model.FromJson(jsonString); - } + public static IProjectSubscriptionUpdate FromJson(string jsonString) + { + var model = new IProjectSubscriptionUpdateModel(); + return model.FromJson(jsonString); } +} + +internal class IProjectSubscriptionUpdateModel : JsonModel +{ + public IImmutableDictionary? CurrentState { get; set; } + public IImmutableDictionary? ProjectChanges { get; set; } + public ProjectConfigurationModel? ProjectConfiguration { get; set; } - internal class IProjectSubscriptionUpdateModel : JsonModel + public override IProjectSubscriptionUpdate ToActualModel() { - public IImmutableDictionary? CurrentState { get; set; } - public IImmutableDictionary? ProjectChanges { get; set; } - public ProjectConfigurationModel? ProjectConfiguration { get; set; } + IImmutableDictionary currentState; + IImmutableDictionary projectChanges; + ProjectConfiguration projectConfiguration; - public override IProjectSubscriptionUpdate ToActualModel() + if (CurrentState is not null) + { + currentState = CurrentState.Select(x => new KeyValuePair(x.Key, x.Value.ToActualModel())).ToImmutableDictionary(); + } + else { - IImmutableDictionary currentState; - IImmutableDictionary projectChanges; - ProjectConfiguration projectConfiguration; + currentState = ImmutableDictionary.Empty; + } - if (CurrentState is not null) - { - currentState = CurrentState.Select(x => new KeyValuePair(x.Key, x.Value.ToActualModel())).ToImmutableDictionary(); - } - else - { - currentState = ImmutableDictionary.Empty; - } + if (ProjectChanges is not null) + { + projectChanges = ProjectChanges.Select(x => new KeyValuePair(x.Key, x.Value.ToActualModel())).ToImmutableDictionary(); + } + else + { + projectChanges = ImmutableDictionary.Empty; + } - if (ProjectChanges is not null) - { - projectChanges = ProjectChanges.Select(x => new KeyValuePair(x.Key, x.Value.ToActualModel())).ToImmutableDictionary(); - } - else - { - projectChanges = ImmutableDictionary.Empty; - } + if (ProjectConfiguration is not null) + { + projectConfiguration = ProjectConfiguration.ToActualModel(); + } + else + { + projectConfiguration = ProjectConfigurationFactory.Create("TEST"); + } - if (ProjectConfiguration is not null) - { - projectConfiguration = ProjectConfiguration.ToActualModel(); - } - else - { - projectConfiguration = ProjectConfigurationFactory.Create("TEST"); - } + return new ActualModel(currentState, projectChanges, projectConfiguration); + } - return new ActualModel(currentState, projectChanges, projectConfiguration); - } + private class ActualModel : IProjectSubscriptionUpdate + { + public IImmutableDictionary CurrentState { get; } + public IImmutableDictionary ProjectChanges { get; } + public ProjectConfiguration ProjectConfiguration { get; } - private class ActualModel : IProjectSubscriptionUpdate + public ActualModel(IImmutableDictionary currentState, IImmutableDictionary projectChanges, ProjectConfiguration projectConfiguration) { - public IImmutableDictionary CurrentState { get; } - public IImmutableDictionary ProjectChanges { get; } - public ProjectConfiguration ProjectConfiguration { get; } - - public ActualModel(IImmutableDictionary currentState, IImmutableDictionary projectChanges, ProjectConfiguration projectConfiguration) - { - CurrentState = currentState; - ProjectChanges = projectChanges; - ProjectConfiguration = projectConfiguration; - } + CurrentState = currentState; + ProjectChanges = projectChanges; + ProjectConfiguration = projectConfiguration; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSystemOptionsFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSystemOptionsFactory.cs index b94c14a301..e55b817d90 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSystemOptionsFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectSystemOptionsFactory.cs @@ -1,48 +1,47 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectSystemOptionsFactory { - internal static class IProjectSystemOptionsFactory + public static IProjectSystemOptions Create() { - public static IProjectSystemOptions Create() - { - return Mock.Of(); - } - - public static IProjectSystemOptions ImplementGetUseDesignerByDefaultAsync(Func result) - { - var mock = new Mock(); - mock.Setup(o => o.GetUseDesignerByDefaultAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(result); - - return mock.Object; - } - - public static IProjectSystemOptions ImplementSetUseDesignerByDefaultAsync(Func result) - { - var mock = new Mock(); - mock.Setup(o => o.SetUseDesignerByDefaultAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(result); - - return mock.Object; - } - - public static IProjectSystemOptions ImplementGetSkipAnalyzersForImplicitlyTriggeredBuildAsync(Func result) - { - var mock = new Mock(); - mock.Setup(o => o.GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(It.IsAny())) - .ReturnsAsync(result); - - return mock.Object; - } - - public static IProjectSystemOptions ImplementGetPreferSingleTargetBuildsForStartupProjectsAsync(Func result) - { - var mock = new Mock(); - mock.Setup(o => o.GetPreferSingleTargetBuildsForStartupProjectsAsync(It.IsAny())) - .ReturnsAsync(result); - - return mock.Object; - } + return Mock.Of(); + } + + public static IProjectSystemOptions ImplementGetUseDesignerByDefaultAsync(Func result) + { + var mock = new Mock(); + mock.Setup(o => o.GetUseDesignerByDefaultAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(result); + + return mock.Object; + } + + public static IProjectSystemOptions ImplementSetUseDesignerByDefaultAsync(Func result) + { + var mock = new Mock(); + mock.Setup(o => o.SetUseDesignerByDefaultAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(result); + + return mock.Object; + } + + public static IProjectSystemOptions ImplementGetSkipAnalyzersForImplicitlyTriggeredBuildAsync(Func result) + { + var mock = new Mock(); + mock.Setup(o => o.GetSkipAnalyzersForImplicitlyTriggeredBuildAsync(It.IsAny())) + .ReturnsAsync(result); + + return mock.Object; + } + + public static IProjectSystemOptions ImplementGetPreferSingleTargetBuildsForStartupProjectsAsync(Func result) + { + var mock = new Mock(); + mock.Setup(o => o.GetPreferSingleTargetBuildsForStartupProjectsAsync(It.IsAny())) + .ReturnsAsync(result); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.ProjectThreadingService.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.ProjectThreadingService.cs index c6e3273915..50b6e51056 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.ProjectThreadingService.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.ProjectThreadingService.cs @@ -3,60 +3,59 @@ using Microsoft.VisualStudio.Threading; using Task = System.Threading.Tasks.Task; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal partial class IProjectThreadingServiceFactory { - internal partial class IProjectThreadingServiceFactory + private class ProjectThreadingService(bool verifyOnUIThread = true) : IProjectThreadingService { - private class ProjectThreadingService(bool verifyOnUIThread = true) : IProjectThreadingService - { - public JoinableTaskContextNode JoinableTaskContext { get; } = new JoinableTaskContextNode(new JoinableTaskContext()); + public JoinableTaskContextNode JoinableTaskContext { get; } = new JoinableTaskContextNode(new JoinableTaskContext()); - public JoinableTaskFactory JoinableTaskFactory => JoinableTaskContext.Factory; + public JoinableTaskFactory JoinableTaskFactory => JoinableTaskContext.Factory; - public bool IsOnMainThread => !verifyOnUIThread || JoinableTaskContext.IsOnMainThread; + public bool IsOnMainThread => !verifyOnUIThread || JoinableTaskContext.IsOnMainThread; - public void ExecuteSynchronously(Func asyncAction) - { - JoinableTaskFactory.Run(asyncAction); - } + public void ExecuteSynchronously(Func asyncAction) + { + JoinableTaskFactory.Run(asyncAction); + } - public T ExecuteSynchronously(Func> asyncAction) - { - return JoinableTaskFactory.Run(asyncAction); - } + public T ExecuteSynchronously(Func> asyncAction) + { + return JoinableTaskFactory.Run(asyncAction); + } - public void VerifyOnUIThread() + public void VerifyOnUIThread() + { + if (verifyOnUIThread && !IsOnMainThread) { - if (verifyOnUIThread && !IsOnMainThread) - { - throw new InvalidOperationException(); - } + throw new InvalidOperationException(); } + } - public IDisposable SuppressProjectExecutionContext() - { - return DisposableObject.Instance; - } + public IDisposable SuppressProjectExecutionContext() + { + return DisposableObject.Instance; + } - public void Fork( - Func asyncAction, - JoinableTaskFactory? factory = null, - UnconfiguredProject? project = null, - ConfiguredProject? configuredProject = null, - ErrorReportSettings? watsonReportSettings = null, - ProjectFaultSeverity faultSeverity = ProjectFaultSeverity.Recoverable, - ForkOptions options = ForkOptions.Default) - { - JoinableTaskFactory.Run(asyncAction); - } + public void Fork( + Func asyncAction, + JoinableTaskFactory? factory = null, + UnconfiguredProject? project = null, + ConfiguredProject? configuredProject = null, + ErrorReportSettings? watsonReportSettings = null, + ProjectFaultSeverity faultSeverity = ProjectFaultSeverity.Recoverable, + ForkOptions options = ForkOptions.Default) + { + JoinableTaskFactory.Run(asyncAction); + } - private class DisposableObject : IDisposable - { - public static IDisposable Instance { get; } = new DisposableObject(); + private class DisposableObject : IDisposable + { + public static IDisposable Instance { get; } = new DisposableObject(); - public void Dispose() - { - } + public void Dispose() + { } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.cs index 2c1e49a919..1a99596492 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectThreadingServiceFactory.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static partial class IProjectThreadingServiceFactory { - internal static partial class IProjectThreadingServiceFactory + public static IProjectThreadingService Create(bool verifyOnUIThread = true) { - public static IProjectThreadingService Create(bool verifyOnUIThread = true) - { - return new ProjectThreadingService(verifyOnUIThread); - } + return new ProjectThreadingService(verifyOnUIThread); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyContextFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyContextFactory.cs index 3bb07b5d49..8e9f22892d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyContextFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyContextFactory.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectTreeCustomizablePropertyContextFactory { - internal static class IProjectTreeCustomizablePropertyContextFactory + public static IProjectTreeCustomizablePropertyContext Create() { - public static IProjectTreeCustomizablePropertyContext Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IProjectTreeCustomizablePropertyContext Implement( - string? itemName = null, - string? itemType = null, - bool isFolder = false, - ProjectTreeFlags flags = default, - IImmutableDictionary? metadata = null) - { - var mock = new Mock(); - mock.Setup(x => x.ItemName).Returns(itemName ?? string.Empty); - mock.Setup(x => x.ItemType).Returns(itemType); - mock.Setup(x => x.IsFolder).Returns(isFolder); - mock.Setup(x => x.ParentNodeFlags).Returns(flags); - mock.Setup(x => x.Metadata).Returns(metadata ?? ImmutableStringDictionary.EmptyOrdinal); - return mock.Object; - } + public static IProjectTreeCustomizablePropertyContext Implement( + string? itemName = null, + string? itemType = null, + bool isFolder = false, + ProjectTreeFlags flags = default, + IImmutableDictionary? metadata = null) + { + var mock = new Mock(); + mock.Setup(x => x.ItemName).Returns(itemName ?? string.Empty); + mock.Setup(x => x.ItemType).Returns(itemType); + mock.Setup(x => x.IsFolder).Returns(isFolder); + mock.Setup(x => x.ParentNodeFlags).Returns(flags); + mock.Setup(x => x.Metadata).Returns(metadata ?? ImmutableStringDictionary.EmptyOrdinal); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyValuesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyValuesFactory.cs index 61b4c49b2e..6a5df2bc72 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyValuesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeCustomizablePropertyValuesFactory.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectTreeCustomizablePropertyValuesFactory { - internal static class IProjectTreeCustomizablePropertyValuesFactory + public static IProjectTreeCustomizablePropertyValues Create() { - public static IProjectTreeCustomizablePropertyValues Create() - { - return Mock.Of(); - } + return Mock.Of(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeProviderFactory.cs index f0c252b41b..d22d91cc20 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeProviderFactory.cs @@ -2,68 +2,67 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectTreeProviderFactory { - internal static class IProjectTreeProviderFactory + public static IProjectTreeProvider ImplementGetAddNewItemDirectory(Func action) { - public static IProjectTreeProvider ImplementGetAddNewItemDirectory(Func action) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(p => p.GetPath(It.IsAny())) - .Returns((IProjectTree tree) => tree.FilePath); + mock.Setup(p => p.GetPath(It.IsAny())) + .Returns((IProjectTree tree) => tree.FilePath); - mock.Setup(p => p.GetAddNewItemDirectory(It.IsAny())) - .Returns(action); + mock.Setup(p => p.GetAddNewItemDirectory(It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectTreeProvider ImplementGetPath(Func action) - { - var mock = new Mock(); - mock.Setup(p => p.GetPath(It.IsAny())) - .Returns(action); + public static IProjectTreeProvider ImplementGetPath(Func action) + { + var mock = new Mock(); + mock.Setup(p => p.GetPath(It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectTreeProvider ImplementFindByPath(Func action) - { - var mock = new Mock(); - mock.Setup(p => p.FindByPath(It.IsAny(), It.IsAny())) - .Returns(action); + public static IProjectTreeProvider ImplementFindByPath(Func action) + { + var mock = new Mock(); + mock.Setup(p => p.FindByPath(It.IsAny(), It.IsAny())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IProjectTreeProvider Create(string? addNewItemDirectoryReturn = null, Func? findByPathAction = null) - { - var mock = new Mock(); + public static IProjectTreeProvider Create(string? addNewItemDirectoryReturn = null, Func? findByPathAction = null) + { + var mock = new Mock(); - var mockTree = Mock.Of>>(); - mock.SetupGet(t => t.Tree) - .Returns(mockTree); + var mockTree = Mock.Of>>(); + mock.SetupGet(t => t.Tree) + .Returns(mockTree); - mock.Setup(t => t.GetPath(It.IsAny())) - .Returns(tree => tree.FilePath); + mock.Setup(t => t.GetPath(It.IsAny())) + .Returns(tree => tree.FilePath); - mock.Setup(t => t.RemoveAsync(It.IsAny>(), It.IsAny())) - .Returns, DeleteOptions>((nodes, options) => + mock.Setup(t => t.RemoveAsync(It.IsAny>(), It.IsAny())) + .Returns, DeleteOptions>((nodes, options) => + { + foreach (var node in nodes) { - foreach (var node in nodes) - { - node.Parent!.Remove(node); - } - return Task.CompletedTask; - }); + node.Parent!.Remove(node); + } + return Task.CompletedTask; + }); - mock.Setup(t => t.GetAddNewItemDirectory(It.IsAny())).Returns(addNewItemDirectoryReturn); + mock.Setup(t => t.GetAddNewItemDirectory(It.IsAny())).Returns(addNewItemDirectoryReturn); - mock.Setup(p => p.FindByPath(It.IsAny(), It.IsAny())) - .Returns(findByPathAction); + mock.Setup(p => p.FindByPath(It.IsAny(), It.IsAny())) + .Returns(findByPathAction); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceFactory.cs index a001349679..6560bc816e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceFactory.cs @@ -1,42 +1,41 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectTreeServiceFactory { - internal static class IProjectTreeServiceFactory + public static IProjectTreeService Create(IProjectTree? tree = null, IProjectTreeProvider? treeProvider = null) { - public static IProjectTreeService Create(IProjectTree? tree = null, IProjectTreeProvider? treeProvider = null) - { - var mock = new Mock(); + var mock = new Mock(); - var treeState = IProjectTreeServiceStateFactory.ImplementTree(() => tree, () => treeProvider ?? IProjectTreeProviderFactory.Create()); + var treeState = IProjectTreeServiceStateFactory.ImplementTree(() => tree, () => treeProvider ?? IProjectTreeProviderFactory.Create()); - mock.Setup(s => s.PublishAnyNonLoadingTreeAsync(It.IsAny())) - .ReturnsAsync(treeState); + mock.Setup(s => s.PublishAnyNonLoadingTreeAsync(It.IsAny())) + .ReturnsAsync(treeState); - mock.Setup(s => s.PublishAnyNonNullTreeAsync(It.IsAny())) - .ReturnsAsync(treeState); + mock.Setup(s => s.PublishAnyNonNullTreeAsync(It.IsAny())) + .ReturnsAsync(treeState); - mock.Setup(s => s.PublishLatestTreeAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(treeState); + mock.Setup(s => s.PublishLatestTreeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(treeState); - mock.SetupGet(s => s.CurrentTree) - .Returns(treeState); + mock.SetupGet(s => s.CurrentTree) + .Returns(treeState); - return mock.Object; - } + return mock.Object; + } - public static IProjectTreeService Create() - { - return Mock.Of(); - } + public static IProjectTreeService Create() + { + return Mock.Of(); + } - public static IProjectTreeService ImplementCurrentTree(Func action) - { - var mock = new Mock(); - mock.SetupGet(s => s.CurrentTree) - .Returns(action); + public static IProjectTreeService ImplementCurrentTree(Func action) + { + var mock = new Mock(); + mock.SetupGet(s => s.CurrentTree) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceStateFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceStateFactory.cs index 2022062364..0fce0108f6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceStateFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectTreeServiceStateFactory.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectTreeServiceStateFactory { - internal static class IProjectTreeServiceStateFactory + public static IProjectTreeServiceState Create() { - public static IProjectTreeServiceState Create() - { - return Mock.Of(); - } + return Mock.Of(); + } + + public static IProjectTreeServiceState ImplementTreeProvider(Func? action) + { + return ImplementTree(null, action); + } - public static IProjectTreeServiceState ImplementTreeProvider(Func? action) + public static IProjectTreeServiceState ImplementTree(Func? treeAction = null, Func? treeProviderAction = null) + { + var mock = new Mock(); + + if (treeAction is not null) { - return ImplementTree(null, action); + mock.SetupGet(s => s.Tree) + .Returns(treeAction); } - public static IProjectTreeServiceState ImplementTree(Func? treeAction = null, Func? treeProviderAction = null) + if (treeProviderAction is not null) { - var mock = new Mock(); - - if (treeAction is not null) - { - mock.SetupGet(s => s.Tree) - .Returns(treeAction); - } - - if (treeProviderAction is not null) - { - mock.SetupGet(s => s.TreeProvider) - .Returns(treeProviderAction); - } - - return mock.Object; + mock.SetupGet(s => s.TreeProvider) + .Returns(treeProviderAction); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectVersionedValueFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectVersionedValueFactory.cs index b297bdebb7..b9e0eb638a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectVersionedValueFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IProjectVersionedValueFactory.cs @@ -1,66 +1,65 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectVersionedValueFactory { - internal static class IProjectVersionedValueFactory + public static IProjectVersionedValue CreateEmpty() { - public static IProjectVersionedValue CreateEmpty() - { - return FromJson("{}"); - } + return FromJson("{}"); + } - internal static IProjectVersionedValue Create(T value) - { - var mock = new Mock>(); + internal static IProjectVersionedValue Create(T value) + { + var mock = new Mock>(); - mock.SetupGet(p => p.Value) - .Returns(value); + mock.SetupGet(p => p.Value) + .Returns(value); - mock.SetupGet(p => p.DataSourceVersions) - .Returns(Empty.ProjectValueVersions); + mock.SetupGet(p => p.DataSourceVersions) + .Returns(Empty.ProjectValueVersions); - return mock.Object; - } + return mock.Object; + } - internal static IProjectVersionedValue Create(T value, IImmutableDictionary dataSourceVersions) - { - var mock = new Mock>(); + internal static IProjectVersionedValue Create(T value, IImmutableDictionary dataSourceVersions) + { + var mock = new Mock>(); - mock.SetupGet(p => p.Value) - .Returns(value); + mock.SetupGet(p => p.Value) + .Returns(value); - mock.SetupGet(p => p.DataSourceVersions) - .Returns(dataSourceVersions); + mock.SetupGet(p => p.DataSourceVersions) + .Returns(dataSourceVersions); - return mock.Object; - } + return mock.Object; + } - internal static IProjectVersionedValue Create(T value, NamedIdentity identity, IComparable version) - { - var mock = new Mock>(); + internal static IProjectVersionedValue Create(T value, NamedIdentity identity, IComparable version) + { + var mock = new Mock>(); - mock.SetupGet(p => p.Value) - .Returns(value); + mock.SetupGet(p => p.Value) + .Returns(value); - var dataSourceVersions = Empty.ProjectValueVersions.Add(identity, version); + var dataSourceVersions = Empty.ProjectValueVersions.Add(identity, version); - mock.SetupGet(p => p.DataSourceVersions) - .Returns(dataSourceVersions); + mock.SetupGet(p => p.DataSourceVersions) + .Returns(dataSourceVersions); - return mock.Object; - } + return mock.Object; + } - public static IProjectVersionedValue FromJson(string jsonString) - { - return FromJson(version: 1, jsonString); - } + public static IProjectVersionedValue FromJson(string jsonString) + { + return FromJson(version: 1, jsonString); + } - public static IProjectVersionedValue FromJson(IComparable version, string jsonString) - { - var update = IProjectSubscriptionUpdateFactory.FromJson(jsonString); + public static IProjectVersionedValue FromJson(IComparable version, string jsonString) + { + var update = IProjectSubscriptionUpdateFactory.FromJson(jsonString); - // Every IProjectSubscriptionUpdate contains the version of the configured project - return Create(update, identity: ProjectDataSources.ConfiguredProjectVersion, version); - } + // Every IProjectSubscriptionUpdate contains the version of the configured project + return Create(update, identity: ProjectDataSources.ConfiguredProjectVersion, version); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyFactory.cs index 92dd780e9c..da54849132 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyFactory.cs @@ -3,61 +3,60 @@ using System.Collections.ObjectModel; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IPropertyFactory { - internal static class IPropertyFactory + public static IEnumProperty CreateEnum(IEnumValue[]? admissibleValues = null) { - public static IEnumProperty CreateEnum(IEnumValue[]? admissibleValues = null) - { - var mock = new Mock(); - - if (admissibleValues is not null) - { - mock.Setup(m => m.GetAdmissibleValuesAsync()).ReturnsAsync(new ReadOnlyCollection(admissibleValues)); - } + var mock = new Mock(); - return mock.Object; + if (admissibleValues is not null) + { + mock.Setup(m => m.GetAdmissibleValuesAsync()).ReturnsAsync(new ReadOnlyCollection(admissibleValues)); } - public static IProperty Create( - string? name = null, - IDataSource? dataSource = null, - Action? setValue = null) - { - var mock = new Mock(); + return mock.Object; + } - if (name is not null) - { - mock.SetupGet(m => m.Name).Returns(name); - } + public static IProperty Create( + string? name = null, + IDataSource? dataSource = null, + Action? setValue = null) + { + var mock = new Mock(); - if (dataSource is not null) - { - mock.SetupGet(m => m.DataSource).Returns(dataSource); - } + if (name is not null) + { + mock.SetupGet(m => m.Name).Returns(name); + } - if (setValue is not null) - { - mock.Setup(m => m.SetValueAsync(It.IsAny())) - .Returns((object? o) => { setValue(o); return Task.CompletedTask; }); - } + if (dataSource is not null) + { + mock.SetupGet(m => m.DataSource).Returns(dataSource); + } - return mock.Object; + if (setValue is not null) + { + mock.Setup(m => m.SetValueAsync(It.IsAny())) + .Returns((object? o) => { setValue(o); return Task.CompletedTask; }); } + + return mock.Object; } +} - internal static class IDataSourceFactory +internal static class IDataSourceFactory +{ + public static IDataSource Create(bool? hasConfigurationCondition = false) { - public static IDataSource Create(bool? hasConfigurationCondition = false) - { - var mock = new Mock(); - - if (hasConfigurationCondition is not null) - { - mock.SetupGet(m => m.HasConfigurationCondition).Returns(hasConfigurationCondition.Value); - } + var mock = new Mock(); - return mock.Object; + if (hasConfigurationCondition is not null) + { + mock.SetupGet(m => m.HasConfigurationCondition).Returns(hasConfigurationCondition.Value); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogFactory.cs index 381992c2cd..a701965a3f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogFactory.cs @@ -2,71 +2,70 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IPropertyPagesCatalogFactory { - internal static class IPropertyPagesCatalogFactory + public static IPropertyPagesCatalog Create(Dictionary rulesBySchemaName) { - public static IPropertyPagesCatalog Create(Dictionary rulesBySchemaName) - { - var catalog = new Mock(); - catalog.Setup(o => o.BindToContext(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((string schemaName, string file, string itemType, string itemName) => - { - rulesBySchemaName.TryGetValue(schemaName, out IRule rule); - return rule; - }); + var catalog = new Mock(); + catalog.Setup(o => o.BindToContext(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string schemaName, string file, string itemType, string itemName) => + { + rulesBySchemaName.TryGetValue(schemaName, out IRule rule); + return rule; + }); - catalog.Setup(o => o.GetPropertyPagesSchemas()) - .Returns(() => rulesBySchemaName.Keys); + catalog.Setup(o => o.GetPropertyPagesSchemas()) + .Returns(() => rulesBySchemaName.Keys); - catalog.Setup(o => o.GetSchema(It.IsAny())) - .Returns((string name) => rulesBySchemaName.TryGetValue(name, out IRule rule) ? rule.Schema : null); + catalog.Setup(o => o.GetSchema(It.IsAny())) + .Returns((string name) => rulesBySchemaName.TryGetValue(name, out IRule rule) ? rule.Schema : null); - catalog.Setup(o => o.BindToContext(It.IsAny(), It.IsAny())) - .Returns((string name, IProjectPropertiesContext context) => rulesBySchemaName.TryGetValue(name, out IRule rule) ? rule : null); + catalog.Setup(o => o.BindToContext(It.IsAny(), It.IsAny())) + .Returns((string name, IProjectPropertiesContext context) => rulesBySchemaName.TryGetValue(name, out IRule rule) ? rule : null); - return catalog.Object; - } - - public static IPropertyPagesCatalog Create(params PropertyPageData[] data) - { - return Create(CreateCatalogLookup(data)); - } + return catalog.Object; + } - private static Dictionary CreateCatalogLookup(PropertyPageData[] data) - { - var catalog = new Dictionary(); + public static IPropertyPagesCatalog Create(params PropertyPageData[] data) + { + return Create(CreateCatalogLookup(data)); + } - foreach (var category in data.GroupBy(p => p.Category)) - { - catalog.Add(category.Key, - IRuleFactory.Create( - properties: category.Select(property => CreateProperty(property.PropertyName, property.Value, property.SetValues)))); - } + private static Dictionary CreateCatalogLookup(PropertyPageData[] data) + { + var catalog = new Dictionary(); - return catalog; + foreach (var category in data.GroupBy(p => p.Category)) + { + catalog.Add(category.Key, + IRuleFactory.Create( + properties: category.Select(property => CreateProperty(property.PropertyName, property.Value, property.SetValues)))); } - private static IProperty CreateProperty(string name, object? value, List? setValues = null) - { - var property = new Mock(); - property.SetupGet(o => o.Name) - .Returns(name); + return catalog; + } - property.Setup(o => o.GetValueAsync()) - .ReturnsAsync(value); + private static IProperty CreateProperty(string name, object? value, List? setValues = null) + { + var property = new Mock(); + property.SetupGet(o => o.Name) + .Returns(name); - property.As().Setup(p => p.GetEvaluatedValueAtEndAsync()).ReturnsAsync(value?.ToString() ?? string.Empty); - property.As().Setup(p => p.GetEvaluatedValueAsync()).ReturnsAsync(value?.ToString() ?? string.Empty); + property.Setup(o => o.GetValueAsync()) + .ReturnsAsync(value); - if (setValues is not null) - { - property.Setup(p => p.SetValueAsync(It.IsAny())) - .Callback(setValues.Add) - .ReturnsAsync(() => { }); - } + property.As().Setup(p => p.GetEvaluatedValueAtEndAsync()).ReturnsAsync(value?.ToString() ?? string.Empty); + property.As().Setup(p => p.GetEvaluatedValueAsync()).ReturnsAsync(value?.ToString() ?? string.Empty); - return property.Object; + if (setValues is not null) + { + property.Setup(p => p.SetValueAsync(It.IsAny())) + .Callback(setValues.Add) + .ReturnsAsync(() => { }); } + + return property.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogProviderFactory.cs index 25a493f404..c91bb90e26 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IPropertyPagesCatalogProviderFactory.cs @@ -2,29 +2,28 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IPropertyPagesCatalogProviderFactory { - internal static class IPropertyPagesCatalogProviderFactory + public static IPropertyPagesCatalogProvider Create(Dictionary catalogsByContext, IPropertyPagesCatalog? memoryOnlyCatalog = null) { - public static IPropertyPagesCatalogProvider Create(Dictionary catalogsByContext, IPropertyPagesCatalog? memoryOnlyCatalog = null) - { - var catalogProvider = new Mock(); - catalogProvider - .Setup(o => o.GetCatalogsAsync(CancellationToken.None)) - .ReturnsAsync(catalogsByContext.ToImmutableDictionary()); - - catalogProvider - .Setup(o => o.GetCatalogAsync(It.IsAny(), CancellationToken.None)) - .Returns((string name, CancellationToken token) => Task.FromResult(catalogsByContext[name])); + var catalogProvider = new Mock(); + catalogProvider + .Setup(o => o.GetCatalogsAsync(CancellationToken.None)) + .ReturnsAsync(catalogsByContext.ToImmutableDictionary()); - if (memoryOnlyCatalog is not null) - { - catalogProvider - .Setup(o => o.GetMemoryOnlyCatalog(It.IsAny())) - .Returns(memoryOnlyCatalog); - } + catalogProvider + .Setup(o => o.GetCatalogAsync(It.IsAny(), CancellationToken.None)) + .Returns((string name, CancellationToken token) => Task.FromResult(catalogsByContext[name])); - return catalogProvider.Object; + if (memoryOnlyCatalog is not null) + { + catalogProvider + .Setup(o => o.GetMemoryOnlyCatalog(It.IsAny())) + .Returns(memoryOnlyCatalog); } + + return catalogProvider.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRemoteAuthenticationProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRemoteAuthenticationProviderFactory.cs index 88986a6602..dea17a2887 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRemoteAuthenticationProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRemoteAuthenticationProviderFactory.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.Mocks +namespace Microsoft.VisualStudio.Mocks; + +public static class IRemoteAuthenticationProviderFactory { - public static class IRemoteAuthenticationProviderFactory + internal static IRemoteAuthenticationProvider Create(string name, string displayName) { - internal static IRemoteAuthenticationProvider Create(string name, string displayName) - { - var provider = new Mock(); + var provider = new Mock(); - provider.SetupGet(o => o.Name).Returns(name); - provider.SetupGet(o => o.DisplayName).Returns(displayName); + provider.SetupGet(o => o.Name).Returns(name); + provider.SetupGet(o => o.DisplayName).Returns(displayName); - return provider.Object; - } + return provider.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRuleFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRuleFactory.cs index faddd60c4a..c98089e4ac 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRuleFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IRuleFactory.cs @@ -3,60 +3,59 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IRuleFactory { - internal static class IRuleFactory + public static IRule Create( + string? name = null, + string? displayName = null, + IEnumerable? properties = null, + string? pageTemplate = null, + Dictionary? metadata = null) { - public static IRule Create( - string? name = null, - string? displayName = null, - IEnumerable? properties = null, - string? pageTemplate = null, - Dictionary? metadata = null) + var schema = new Rule { - var schema = new Rule - { - Name = name, - DisplayName = displayName, - Metadata = metadata, - PageTemplate = pageTemplate - }; - - return CreateFromRule(schema, properties); - } + Name = name, + DisplayName = displayName, + Metadata = metadata, + PageTemplate = pageTemplate + }; + + return CreateFromRule(schema, properties); + } - public static IRule CreateFromRule( - Rule? schema = null, - IEnumerable? properties = null) + public static IRule CreateFromRule( + Rule? schema = null, + IEnumerable? properties = null) + { + var rule = new Mock(); + + if (properties is not null) { - var rule = new Mock(); - - if (properties is not null) - { - rule.Setup(o => o.GetProperty(It.IsAny())) - .Returns((string propertyName) => - { - return properties.FirstOrDefault(p => p.Name == propertyName); - }); - } - - if (schema is not null) - { - rule.Setup(o => o.Schema) - .Returns(schema); - } - - return rule.Object; + rule.Setup(o => o.GetProperty(It.IsAny())) + .Returns((string propertyName) => + { + return properties.FirstOrDefault(p => p.Name == propertyName); + }); } - public static IRule Create(Rule schema) + if (schema is not null) { - var rule = new Mock(); - rule.Setup(o => o.Schema) .Returns(schema); - - return rule.Object; } + + return rule.Object; + } + + public static IRule Create(Rule schema) + { + var rule = new Mock(); + + rule.Setup(o => o.Schema) + .Returns(schema); + + return rule.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISafeProjectGuidServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISafeProjectGuidServiceFactory.cs index d80cdfb2ef..e92056ae88 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISafeProjectGuidServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISafeProjectGuidServiceFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ISafeProjectGuidServiceFactory { - internal static class ISafeProjectGuidServiceFactory + public static ISafeProjectGuidService ImplementGetProjectGuidAsync(Guid guid) { - public static ISafeProjectGuidService ImplementGetProjectGuidAsync(Guid guid) - { - var mock = new Mock(); - mock.Setup(s => s.GetProjectGuidAsync(It.IsAny())) - .ReturnsAsync(guid); + var mock = new Mock(); + mock.Setup(s => s.GetProjectGuidAsync(It.IsAny())) + .ReturnsAsync(guid); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISolutionServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISolutionServiceFactory.cs index 55b6a81854..9538f6edf3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISolutionServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISolutionServiceFactory.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ISolutionServiceFactory { - internal static class ISolutionServiceFactory + public static ISolutionService Create() { - public static ISolutionService Create() - { - return ImplementSolutionLoadedInHost(() => new Task(() => { })); - } + return ImplementSolutionLoadedInHost(() => new Task(() => { })); + } - public static ISolutionService ImplementSolutionLoadedInHost(Func action) - { - var mock = new Mock(); - mock.Setup(m => m.LoadedInHost) - .Returns(action); + public static ISolutionService ImplementSolutionLoadedInHost(Func action) + { + var mock = new Mock(); + mock.Setup(m => m.LoadedInHost) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISpecialFilesManagerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISpecialFilesManagerFactory.cs index 06cdcf72a4..b8bc43825d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISpecialFilesManagerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ISpecialFilesManagerFactory.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +internal static class ISpecialFilesManagerFactory { - internal static class ISpecialFilesManagerFactory + public static ISpecialFilesManager ImplementGetFile(string? result) { - public static ISpecialFilesManager ImplementGetFile(string? result) - { - var mock = new Mock(); - mock.Setup(m => m.GetFileAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(result); + var mock = new Mock(); + mock.Setup(m => m.GetFileAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(result); - return mock.Object; - } + return mock.Object; + } - public static ISpecialFilesManager Create() - { - return Mock.Of(); - } + public static ISpecialFilesManager Create() + { + return Mock.Of(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITelemetryServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITelemetryServiceFactory.cs index bfb95dfef3..9f3732b446 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITelemetryServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITelemetryServiceFactory.cs @@ -1,90 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Telemetry +namespace Microsoft.VisualStudio.Telemetry; + +internal static class ITelemetryServiceFactory { - internal static class ITelemetryServiceFactory + public static ITelemetryService Create() { - public static ITelemetryService Create() - { - var mock = new Mock(); + var mock = new Mock(); - return mock.Object; - } + return mock.Object; + } - public class TelemetryParameters - { - public string? EventName { get; set; } + public class TelemetryParameters + { + public string? EventName { get; set; } - public List<(string Name, object Value)>? Properties { get; set; } - } + public List<(string Name, object Value)>? Properties { get; set; } + } - public static ITelemetryService Create(TelemetryParameters callParameters) - { - var telemetryService = new Mock(); + public static ITelemetryService Create(TelemetryParameters callParameters) + { + var telemetryService = new Mock(); - telemetryService.Setup(t => t.PostEvent(It.IsAny())) - .Callback((string name) => callParameters.EventName = name); + telemetryService.Setup(t => t.PostEvent(It.IsAny())) + .Callback((string name) => callParameters.EventName = name); - telemetryService.Setup(t => t.PostProperty(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string e, string p, object v) => + telemetryService.Setup(t => t.PostProperty(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((string e, string p, object v) => + { + callParameters.EventName = e; + callParameters.Properties = new List<(string, object)> { - callParameters.EventName = e; - callParameters.Properties = new List<(string, object)> - { - (p, v) - }; - }); + (p, v) + }; + }); - telemetryService.Setup(t => t.PostProperties(It.IsAny(), It.IsAny>())) - .Callback((string e, IEnumerable<(string propertyName, object propertyValue)> p) => - { - callParameters.EventName = e; - callParameters.Properties = p.ToList(); - }); + telemetryService.Setup(t => t.PostProperties(It.IsAny(), It.IsAny>())) + .Callback((string e, IEnumerable<(string propertyName, object propertyValue)> p) => + { + callParameters.EventName = e; + callParameters.Properties = p.ToList(); + }); - return telemetryService.Object; - } + return telemetryService.Object; + } - public static ITelemetryService Create(Action onTelemetryLogged) - { - var telemetryService = new Mock(); + public static ITelemetryService Create(Action onTelemetryLogged) + { + var telemetryService = new Mock(); - telemetryService.Setup(t => t.PostEvent(It.IsAny())) - .Callback((string name) => + telemetryService.Setup(t => t.PostEvent(It.IsAny())) + .Callback((string name) => + { + var callParameters = new TelemetryParameters { - var callParameters = new TelemetryParameters - { - EventName = name - }; - onTelemetryLogged(callParameters); - }); + EventName = name + }; + onTelemetryLogged(callParameters); + }); - telemetryService.Setup(t => t.PostProperty(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string e, string p, object v) => + telemetryService.Setup(t => t.PostProperty(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((string e, string p, object v) => + { + var callParameters = new TelemetryParameters { - var callParameters = new TelemetryParameters + EventName = e, + Properties = new List<(string, object)> { - EventName = e, - Properties = new List<(string, object)> - { - (p, v) - } - }; - onTelemetryLogged(callParameters); - }); + (p, v) + } + }; + onTelemetryLogged(callParameters); + }); - telemetryService.Setup(t => t.PostProperties(It.IsAny(), It.IsAny>())) - .Callback((string e, IEnumerable<(string propertyName, object propertyValue)> p) => + telemetryService.Setup(t => t.PostProperties(It.IsAny(), It.IsAny>())) + .Callback((string e, IEnumerable<(string propertyName, object propertyValue)> p) => + { + var callParameters = new TelemetryParameters { - var callParameters = new TelemetryParameters - { - EventName = e, - Properties = p.ToList() - }; - onTelemetryLogged(callParameters); - }); + EventName = e, + Properties = p.ToList() + }; + onTelemetryLogged(callParameters); + }); - return telemetryService.Object; - } + return telemetryService.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITemporaryPropertyStorageFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITemporaryPropertyStorageFactory.cs index 1a48f5529b..a039d0541f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITemporaryPropertyStorageFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ITemporaryPropertyStorageFactory.cs @@ -2,23 +2,22 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ITemporaryPropertyStorageFactory { - internal static class ITemporaryPropertyStorageFactory + public static ITemporaryPropertyStorage Create(Dictionary? values = null) { - public static ITemporaryPropertyStorage Create(Dictionary? values = null) - { - values ??= new(); + values ??= new(); - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(o => o.AddOrUpdatePropertyValue(It.IsAny(), It.IsAny())) - .Callback((name, value) => values[name] = value); + mock.Setup(o => o.AddOrUpdatePropertyValue(It.IsAny(), It.IsAny())) + .Callback((name, value) => values[name] = value); - mock.Setup(o => o.GetPropertyValue(It.IsAny())) - .Returns(name => { values.TryGetValue(name, out string value); return value; }); + mock.Setup(o => o.GetPropertyValue(It.IsAny())) + .Returns(name => { values.TryGetValue(name, out string value); return value; }); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectCommonServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectCommonServicesFactory.cs index c0d352a97f..578376727d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectCommonServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectCommonServicesFactory.cs @@ -1,36 +1,35 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IUnconfiguredProjectCommonServicesFactory { - internal static class IUnconfiguredProjectCommonServicesFactory + public static IUnconfiguredProjectCommonServices Create(UnconfiguredProject? project = null, IProjectThreadingService? threadingService = null, + ConfiguredProject? configuredProject = null, ProjectProperties? projectProperties = null, + IProjectAccessor? projectAccessor = null) { - public static IUnconfiguredProjectCommonServices Create(UnconfiguredProject? project = null, IProjectThreadingService? threadingService = null, - ConfiguredProject? configuredProject = null, ProjectProperties? projectProperties = null, - IProjectAccessor? projectAccessor = null) - { - var mock = new Mock(); - - if (project is not null) - mock.Setup(s => s.Project) - .Returns(project); - - if (threadingService is not null) - mock.Setup(s => s.ThreadingService) - .Returns(threadingService); - - if (configuredProject is not null) - mock.Setup(s => s.ActiveConfiguredProject) - .Returns(configuredProject); - - if (projectProperties is not null) - mock.Setup(s => s.ActiveConfiguredProjectProperties) - .Returns(projectProperties); - - if (projectAccessor is not null) - mock.Setup(s => s.ProjectAccessor) - .Returns(projectAccessor); - - return mock.Object; - } + var mock = new Mock(); + + if (project is not null) + mock.Setup(s => s.Project) + .Returns(project); + + if (threadingService is not null) + mock.Setup(s => s.ThreadingService) + .Returns(threadingService); + + if (configuredProject is not null) + mock.Setup(s => s.ActiveConfiguredProject) + .Returns(configuredProject); + + if (projectProperties is not null) + mock.Setup(s => s.ActiveConfiguredProjectProperties) + .Returns(projectProperties); + + if (projectAccessor is not null) + mock.Setup(s => s.ProjectAccessor) + .Returns(projectAccessor); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectServicesFactory.cs index 1b3668842f..3d72e9c9bb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectServicesFactory.cs @@ -1,38 +1,37 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IUnconfiguredProjectServicesFactory { - internal static class IUnconfiguredProjectServicesFactory + public static IUnconfiguredProjectServices Create(IProjectAsynchronousTasksService? asyncTaskService = null, IActiveConfiguredProjectProvider? activeConfiguredProjectProvider = null, IProjectConfigurationsService? projectConfigurationsService = null, IProjectService? projectService = null) { - public static IUnconfiguredProjectServices Create(IProjectAsynchronousTasksService? asyncTaskService = null, IActiveConfiguredProjectProvider? activeConfiguredProjectProvider = null, IProjectConfigurationsService? projectConfigurationsService = null, IProjectService? projectService = null) - { - var mock = new Mock(); - - if (asyncTaskService is not null) - { - mock.Setup(s => s.ProjectAsynchronousTasks) - .Returns(asyncTaskService); - } + var mock = new Mock(); - if (activeConfiguredProjectProvider is not null) - { - mock.Setup(s => s.ActiveConfiguredProjectProvider) - .Returns(activeConfiguredProjectProvider); - } + if (asyncTaskService is not null) + { + mock.Setup(s => s.ProjectAsynchronousTasks) + .Returns(asyncTaskService); + } - if (projectConfigurationsService is not null) - { - mock.Setup(s => s.ProjectConfigurationsService) - .Returns(projectConfigurationsService); - } + if (activeConfiguredProjectProvider is not null) + { + mock.Setup(s => s.ActiveConfiguredProjectProvider) + .Returns(activeConfiguredProjectProvider); + } - if (projectService is not null) - { - mock.Setup(s => s.ProjectService) - .Returns(projectService); - } + if (projectConfigurationsService is not null) + { + mock.Setup(s => s.ProjectConfigurationsService) + .Returns(projectConfigurationsService); + } - return mock.Object; + if (projectService is not null) + { + mock.Setup(s => s.ProjectService) + .Returns(projectService); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectTasksServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectTasksServiceFactory.cs index 39d618c3ff..a458a2eccd 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectTasksServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/IUnconfiguredProjectTasksServiceFactory.cs @@ -1,69 +1,68 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IUnconfiguredProjectTasksServiceFactory { - internal static class IUnconfiguredProjectTasksServiceFactory + public static IUnconfiguredProjectTasksService Create() { - public static IUnconfiguredProjectTasksService Create() - { - return ImplementLoadedProjectAsync(func => func()); - } + return ImplementLoadedProjectAsync(func => func()); + } - public static IUnconfiguredProjectTasksService ImplementPrioritizedProjectLoadedInHost(Func action) - { - var mock = new Mock(); + public static IUnconfiguredProjectTasksService ImplementPrioritizedProjectLoadedInHost(Func action) + { + var mock = new Mock(); - mock.SetupGet(s => s.PrioritizedProjectLoadedInHost) - .Returns(action); + mock.SetupGet(s => s.PrioritizedProjectLoadedInHost) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IUnconfiguredProjectTasksService CreateWithUnloadedProject() - { - var mock = new Mock(); - mock.Setup(t => t.LoadedProjectAsync(It.IsAny>())) - .Throws(new OperationCanceledException()); + public static IUnconfiguredProjectTasksService CreateWithUnloadedProject() + { + var mock = new Mock(); + mock.Setup(t => t.LoadedProjectAsync(It.IsAny>())) + .Throws(new OperationCanceledException()); - mock.Setup(t => t.LoadedProjectAsync(It.IsAny>>())) - .Throws(new OperationCanceledException()); + mock.Setup(t => t.LoadedProjectAsync(It.IsAny>>())) + .Throws(new OperationCanceledException()); - var cancelledTask = Task.FromCanceled(new CancellationToken(canceled: true)); + var cancelledTask = Task.FromCanceled(new CancellationToken(canceled: true)); - mock.SetupGet(t => t.PrioritizedProjectLoadedInHost) - .Returns(cancelledTask); + mock.SetupGet(t => t.PrioritizedProjectLoadedInHost) + .Returns(cancelledTask); - mock.SetupGet(t => t.ProjectLoadedInHost) - .Returns(cancelledTask); + mock.SetupGet(t => t.ProjectLoadedInHost) + .Returns(cancelledTask); - return mock.Object; - } + return mock.Object; + } - public static IUnconfiguredProjectTasksService ImplementLoadedProjectAsync(Func, Task> action) - { - var mock = new Mock(); - mock.Setup(t => t.LoadedProjectAsync(It.IsAny>())) - .Returns(action); + public static IUnconfiguredProjectTasksService ImplementLoadedProjectAsync(Func, Task> action) + { + var mock = new Mock(); + mock.Setup(t => t.LoadedProjectAsync(It.IsAny>())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IUnconfiguredProjectTasksService ImplementLoadedProjectAsync(Func>, Task> action) - { - var mock = new Mock(); - mock.Setup(t => t.LoadedProjectAsync(It.IsAny>>())) - .Returns(action); + public static IUnconfiguredProjectTasksService ImplementLoadedProjectAsync(Func>, Task> action) + { + var mock = new Mock(); + mock.Setup(t => t.LoadedProjectAsync(It.IsAny>>())) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IUnconfiguredProjectTasksService ImplementUnloadCancellationToken(CancellationToken token) - { - var mock = new Mock(); - mock.Setup(t => t.UnloadCancellationToken) - .Returns(token); + public static IUnconfiguredProjectTasksService ImplementUnloadCancellationToken(CancellationToken token) + { + var mock = new Mock(); + mock.Setup(t => t.UnloadCancellationToken) + .Returns(token); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ImmutableOrderedDictionary.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ImmutableOrderedDictionary.cs index 7a1588cd1b..97ec2ea22a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ImmutableOrderedDictionary.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ImmutableOrderedDictionary.cs @@ -2,45 +2,44 @@ using System.Collections; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal sealed class ImmutableOrderedDictionary + : IImmutableDictionary, + IDataWithOriginalSource> { - internal sealed class ImmutableOrderedDictionary - : IImmutableDictionary, - IDataWithOriginalSource> - { - public static ImmutableOrderedDictionary Empty { get; } = new(Enumerable.Empty>()); + public static ImmutableOrderedDictionary Empty { get; } = new(Enumerable.Empty>()); - private readonly Dictionary _dic; + private readonly Dictionary _dic; - public ImmutableOrderedDictionary(IEnumerable> pairs) - { - _dic = new(); + public ImmutableOrderedDictionary(IEnumerable> pairs) + { + _dic = new(); - foreach ((TKey key, TValue value) in pairs) - { - _dic.Add(key, value); - } + foreach ((TKey key, TValue value) in pairs) + { + _dic.Add(key, value); } - - public IReadOnlyCollection> SourceData => _dic.ToList(); - - public TValue this[TKey key] => _dic[key]; - public IEnumerable Keys => _dic.Keys; - public IEnumerable Values => _dic.Values; - public int Count => _dic.Count; - public bool Contains(KeyValuePair pair) => _dic.Contains(pair); - public bool ContainsKey(TKey key) => _dic.ContainsKey(key); - public bool TryGetValue(TKey key, out TValue value) => _dic.TryGetValue(key, out value); - IEnumerator IEnumerable.GetEnumerator() => _dic.GetEnumerator(); - public IEnumerator> GetEnumerator() => _dic.GetEnumerator(); - - public bool TryGetKey(TKey equalKey, out TKey actualKey) => throw new NotImplementedException(); - public IImmutableDictionary Add(TKey key, TValue value) => throw new NotImplementedException(); - public IImmutableDictionary AddRange(IEnumerable> pairs) => throw new NotImplementedException(); - public IImmutableDictionary Clear() => throw new NotImplementedException(); - public IImmutableDictionary Remove(TKey key) => throw new NotImplementedException(); - public IImmutableDictionary RemoveRange(IEnumerable keys) => throw new NotImplementedException(); - public IImmutableDictionary SetItem(TKey key, TValue value) => throw new NotImplementedException(); - public IImmutableDictionary SetItems(IEnumerable> items) => throw new NotImplementedException(); } + + public IReadOnlyCollection> SourceData => _dic.ToList(); + + public TValue this[TKey key] => _dic[key]; + public IEnumerable Keys => _dic.Keys; + public IEnumerable Values => _dic.Values; + public int Count => _dic.Count; + public bool Contains(KeyValuePair pair) => _dic.Contains(pair); + public bool ContainsKey(TKey key) => _dic.ContainsKey(key); + public bool TryGetValue(TKey key, out TValue value) => _dic.TryGetValue(key, out value); + IEnumerator IEnumerable.GetEnumerator() => _dic.GetEnumerator(); + public IEnumerator> GetEnumerator() => _dic.GetEnumerator(); + + public bool TryGetKey(TKey equalKey, out TKey actualKey) => throw new NotImplementedException(); + public IImmutableDictionary Add(TKey key, TValue value) => throw new NotImplementedException(); + public IImmutableDictionary AddRange(IEnumerable> pairs) => throw new NotImplementedException(); + public IImmutableDictionary Clear() => throw new NotImplementedException(); + public IImmutableDictionary Remove(TKey key) => throw new NotImplementedException(); + public IImmutableDictionary RemoveRange(IEnumerable keys) => throw new NotImplementedException(); + public IImmutableDictionary SetItem(TKey key, TValue value) => throw new NotImplementedException(); + public IImmutableDictionary SetItems(IEnumerable> items) => throw new NotImplementedException(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ItemType.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ItemType.cs index 9b8f9742ce..d589196267 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ItemType.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ItemType.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal sealed class ItemType : IItemType { - internal sealed class ItemType : IItemType - { - public string Name { get; } - public string DisplayName => Name; - public bool UpToDateCheckInput { get; } + public string Name { get; } + public string DisplayName => Name; + public bool UpToDateCheckInput { get; } - public ItemType(string name, bool upToDateCheckInput) - { - Name = name; - UpToDateCheckInput = upToDateCheckInput; - } + public ItemType(string name, bool upToDateCheckInput) + { + Name = name; + UpToDateCheckInput = upToDateCheckInput; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/JoinableTaskContextNodeFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/JoinableTaskContextNodeFactory.cs index 7485efd9ee..cc874f60eb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/JoinableTaskContextNodeFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/JoinableTaskContextNodeFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading +namespace Microsoft.VisualStudio.Threading; + +internal static class JoinableTaskContextNodeFactory { - internal static class JoinableTaskContextNodeFactory + public static JoinableTaskContextNode Create() { - public static JoinableTaskContextNode Create() - { #pragma warning disable VSSDK005 - var context = new JoinableTaskContext(); + var context = new JoinableTaskContext(); #pragma warning restore VSSDK005 - return new JoinableTaskContextNode(context); - } + return new JoinableTaskContextNode(context); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/MetadataFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/MetadataFactory.cs index cf4c68a81b..269539e78e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/MetadataFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/MetadataFactory.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class MetadataFactory { - internal static class MetadataFactory + public static IImmutableDictionary> Create(string fileName, (string name, string value) metadata) { - public static IImmutableDictionary> Create(string fileName, (string name, string value) metadata) - { - return ImmutableStringDictionary>.EmptyOrdinal.Add(fileName, - ImmutableStringDictionary.EmptyOrdinalIgnoreCase.Add(metadata.name, metadata.value)); - } + return ImmutableStringDictionary>.EmptyOrdinal.Add(fileName, + ImmutableStringDictionary.EmptyOrdinalIgnoreCase.Add(metadata.name, metadata.value)); + } - public static IImmutableDictionary> Add( - this IImmutableDictionary> metadata, - string fileName, - (string name, string value) itemMetadata) - { - return metadata.Add(fileName, - ImmutableStringDictionary.EmptyOrdinalIgnoreCase.Add(itemMetadata.name, itemMetadata.value)); - } + public static IImmutableDictionary> Add( + this IImmutableDictionary> metadata, + string fileName, + (string name, string value) itemMetadata) + { + return metadata.Add(fileName, + ImmutableStringDictionary.EmptyOrdinalIgnoreCase.Add(itemMetadata.name, itemMetadata.value)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PackageRestoreConfiguredInputFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PackageRestoreConfiguredInputFactory.cs index 766117ae5a..cbd1e2dc01 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PackageRestoreConfiguredInputFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PackageRestoreConfiguredInputFactory.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static class PackageRestoreConfiguredInputFactory { - internal static class PackageRestoreConfiguredInputFactory + public static IReadOnlyCollection Create(ProjectRestoreInfo restoreInfo) { - public static IReadOnlyCollection Create(ProjectRestoreInfo restoreInfo) - { - ProjectConfiguration projectConfiguration = ProjectConfigurationFactory.Create("Debug|x64"); - IComparable projectVersion = 0; + ProjectConfiguration projectConfiguration = ProjectConfigurationFactory.Create("Debug|x64"); + IComparable projectVersion = 0; - return new[] { new PackageRestoreConfiguredInput(projectConfiguration, restoreInfo, projectVersion) }; - } + return new[] { new PackageRestoreConfiguredInput(projectConfiguration, restoreInfo, projectVersion) }; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectConfigurationFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectConfigurationFactory.cs index 7a083880e4..41e7878c42 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectConfigurationFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectConfigurationFactory.cs @@ -1,103 +1,102 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ProjectConfigurationFactory { - internal static class ProjectConfigurationFactory + public static ProjectConfiguration Create(string name, IImmutableDictionary dimensions) { - public static ProjectConfiguration Create(string name, IImmutableDictionary dimensions) - { - return new StandardProjectConfiguration(name, dimensions); - } + return new StandardProjectConfiguration(name, dimensions); + } - public static ProjectConfiguration Create(string dimensionNames, string dimensionValues) + public static ProjectConfiguration Create(string dimensionNames, string dimensionValues) + { + var dimensionsBuilder = ImmutableDictionary.CreateBuilder(); + + string[] dimensionNamesArray = dimensionNames.Split('|'); + string[] dimensionValuesArray = dimensionValues.Split('|'); + + for (int i = 0; i < dimensionNamesArray.Length; i++) { - var dimensionsBuilder = ImmutableDictionary.CreateBuilder(); + dimensionsBuilder.Add(dimensionNamesArray[i], dimensionValuesArray[i]); + } - string[] dimensionNamesArray = dimensionNames.Split('|'); - string[] dimensionValuesArray = dimensionValues.Split('|'); + return Create(dimensionValues, dimensionsBuilder.ToImmutable()); + } - for (int i = 0; i < dimensionNamesArray.Length; i++) - { - dimensionsBuilder.Add(dimensionNamesArray[i], dimensionValuesArray[i]); - } + public static ProjectConfiguration Create(string configuration) + { + var dimensionsBuilder = ImmutableDictionary.CreateBuilder(); - return Create(dimensionValues, dimensionsBuilder.ToImmutable()); - } + string[] dimensions = configuration.Split('|'); - public static ProjectConfiguration Create(string configuration) + for (int i = 0; i < dimensions.Length; i++) { - var dimensionsBuilder = ImmutableDictionary.CreateBuilder(); + dimensionsBuilder.Add(GetDimensionName(i), dimensions[i]); + } - string[] dimensions = configuration.Split('|'); + return Create(configuration, dimensionsBuilder.ToImmutable()); + } - for (int i = 0; i < dimensions.Length; i++) - { - dimensionsBuilder.Add(GetDimensionName(i), dimensions[i]); - } + public static IReadOnlyList CreateMany(params string[] configurations) + { + var configurationsBuilder = ImmutableArray.CreateBuilder(); - return Create(configuration, dimensionsBuilder.ToImmutable()); + foreach (string configuration in configurations) + { + ProjectConfiguration config = Create(configuration); + configurationsBuilder.Add(config); } - public static IReadOnlyList CreateMany(params string[] configurations) + return configurationsBuilder.ToImmutable(); + } + + public static ProjectConfiguration FromJson(string jsonString) + { + var model = new ProjectConfigurationModel(); + return model.FromJson(jsonString); + } + + private static string GetDimensionName(int ordinal) + { + return ordinal switch { - var configurationsBuilder = ImmutableArray.CreateBuilder(); + 0 => "Configuration", + 1 => "Platform", + 2 => "TargetFramework", - foreach (string configuration in configurations) - { - ProjectConfiguration config = Create(configuration); - configurationsBuilder.Add(config); - } + _ => throw new InvalidOperationException(), + }; + } +} - return configurationsBuilder.ToImmutable(); - } +internal class ProjectConfigurationModel : JsonModel +{ + public IImmutableDictionary? Dimensions { get; set; } + public string? Name { get; set; } - public static ProjectConfiguration FromJson(string jsonString) - { - var model = new ProjectConfigurationModel(); - return model.FromJson(jsonString); - } + public override ProjectConfiguration ToActualModel() + { + Assumes.NotNull(Dimensions); + Assumes.NotNull(Name); - private static string GetDimensionName(int ordinal) - { - return ordinal switch - { - 0 => "Configuration", - 1 => "Platform", - 2 => "TargetFramework", - - _ => throw new InvalidOperationException(), - }; - } + return new ActualModel(Dimensions, Name); } - internal class ProjectConfigurationModel : JsonModel + private class ActualModel : ProjectConfiguration { - public IImmutableDictionary? Dimensions { get; set; } - public string? Name { get; set; } + public IImmutableDictionary Dimensions { get; } + public string Name { get; } - public override ProjectConfiguration ToActualModel() + public ActualModel(IImmutableDictionary dimensions, string name) { - Assumes.NotNull(Dimensions); - Assumes.NotNull(Name); - - return new ActualModel(Dimensions, Name); + Dimensions = dimensions; + Name = name; } - private class ActualModel : ProjectConfiguration + public bool Equals(ProjectConfiguration? other) { - public IImmutableDictionary Dimensions { get; } - public string Name { get; } - - public ActualModel(IImmutableDictionary dimensions, string name) - { - Dimensions = dimensions; - Name = name; - } - - public bool Equals(ProjectConfiguration? other) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectFactory.cs index d1c7558ffc..61d6cdb3db 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectFactory.cs @@ -2,13 +2,12 @@ using Microsoft.Build.Construction; -namespace Microsoft.Build.Evaluation +namespace Microsoft.Build.Evaluation; + +internal static class ProjectFactory { - internal static class ProjectFactory + public static Project Create(ProjectRootElement rootElement) { - public static Project Create(ProjectRootElement rootElement) - { - return new Project(rootElement); - } + return new Project(rootElement); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectPropertiesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectPropertiesFactory.cs index 3d1c710341..9c60fc0dc3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectPropertiesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectPropertiesFactory.cs @@ -2,51 +2,50 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ProjectPropertiesFactory { - internal static class ProjectPropertiesFactory + public static ProjectProperties CreateEmpty() + { + return Create(UnconfiguredProjectFactory.Create()); + } + + public static ProjectProperties Create(string category, string propertyName, string? value) + { + var data = new PropertyPageData(category, propertyName, value); + + return Create(data); + } + + public static ProjectProperties Create(params PropertyPageData[] data) + { + return Create(UnconfiguredProjectFactory.Create(), data); + } + + public static ProjectProperties Create(UnconfiguredProject project, params PropertyPageData[] data) { - public static ProjectProperties CreateEmpty() - { - return Create(UnconfiguredProjectFactory.Create()); - } - - public static ProjectProperties Create(string category, string propertyName, string? value) - { - var data = new PropertyPageData(category, propertyName, value); - - return Create(data); - } - - public static ProjectProperties Create(params PropertyPageData[] data) - { - return Create(UnconfiguredProjectFactory.Create(), data); - } - - public static ProjectProperties Create(UnconfiguredProject project, params PropertyPageData[] data) - { - var catalog = IPropertyPagesCatalogFactory.Create(data); - var propertyPagesCatalogProvider = IPropertyPagesCatalogProviderFactory.Create( - new Dictionary - { - { "Project", catalog } - }, - catalog - ); - - IAdditionalRuleDefinitionsService ruleService = Mock.Of(); - - var configuredProjectServices = ConfiguredProjectServicesFactory.Create( - propertyPagesCatalogProvider: propertyPagesCatalogProvider, - ruleService: ruleService); - - var cfg = new StandardProjectConfiguration("Debug|" + "AnyCPU", Empty.PropertiesMap.SetItem("Configuration", "Debug").SetItem("Platform", "AnyCPU")); - ConfiguredProject configuredProject = Mock.Of(o => - o.UnconfiguredProject == project && - o.Services == configuredProjectServices && - o.ProjectConfiguration == cfg); - - return new ProjectProperties(configuredProject); - } + var catalog = IPropertyPagesCatalogFactory.Create(data); + var propertyPagesCatalogProvider = IPropertyPagesCatalogProviderFactory.Create( + new Dictionary + { + { "Project", catalog } + }, + catalog + ); + + IAdditionalRuleDefinitionsService ruleService = Mock.Of(); + + var configuredProjectServices = ConfiguredProjectServicesFactory.Create( + propertyPagesCatalogProvider: propertyPagesCatalogProvider, + ruleService: ruleService); + + var cfg = new StandardProjectConfiguration("Debug|" + "AnyCPU", Empty.PropertiesMap.SetItem("Configuration", "Debug").SetItem("Platform", "AnyCPU")); + ConfiguredProject configuredProject = Mock.Of(o => + o.UnconfiguredProject == project && + o.Services == configuredProjectServices && + o.ProjectConfiguration == cfg); + + return new ProjectProperties(configuredProject); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectRestoreInfoFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectRestoreInfoFactory.cs index 968f4bed79..fe7d632046 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectRestoreInfoFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectRestoreInfoFactory.cs @@ -1,14 +1,13 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal static class ProjectRestoreInfoFactory { - internal static class ProjectRestoreInfoFactory + public static ProjectRestoreInfo Create(string? msbuildProjectExtensionsPath = null) { - public static ProjectRestoreInfo Create(string? msbuildProjectExtensionsPath = null) - { - return new ProjectRestoreInfo(msbuildProjectExtensionsPath ?? string.Empty, string.Empty, string.Empty, - RestoreBuilder.EmptyTargetFrameworks, - RestoreBuilder.EmptyReferences); - } + return new ProjectRestoreInfo(msbuildProjectExtensionsPath ?? string.Empty, string.Empty, string.Empty, + RestoreBuilder.EmptyTargetFrameworks, + RestoreBuilder.EmptyReferences); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectServicesFactory.cs index 0227a12a49..eebb1b63b3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectServicesFactory.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ProjectServicesFactory { - internal static class ProjectServicesFactory + public static ProjectServices Create(IProjectThreadingService? threadingService = null, IProjectFaultHandlerService? faultHandlerService = null, IProjectService? projectService = null, IProjectLockService? projectLockService = null) { - public static ProjectServices Create(IProjectThreadingService? threadingService = null, IProjectFaultHandlerService? faultHandlerService = null, IProjectService? projectService = null, IProjectLockService? projectLockService = null) - { - threadingService ??= IProjectThreadingServiceFactory.Create(); - faultHandlerService ??= IProjectFaultHandlerServiceFactory.Create(); + threadingService ??= IProjectThreadingServiceFactory.Create(); + faultHandlerService ??= IProjectFaultHandlerServiceFactory.Create(); - var projectServices = new Mock(); - projectServices.Setup(u => u.ThreadingPolicy) - .Returns(threadingService); + var projectServices = new Mock(); + projectServices.Setup(u => u.ThreadingPolicy) + .Returns(threadingService); - projectServices.Setup(u => u.FaultHandler) - .Returns(faultHandlerService); + projectServices.Setup(u => u.FaultHandler) + .Returns(faultHandlerService); - projectServices.Setup(u => u.ProjectService) - .Returns(projectService); + projectServices.Setup(u => u.ProjectService) + .Returns(projectService); - projectServices.Setup(u => u.ProjectLockService) - .Returns(projectLockService!); + projectServices.Setup(u => u.ProjectLockService) + .Returns(projectLockService!); - return projectServices.Object; - } + return projectServices.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSource.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSource.cs index dd9b889b9b..b2f0bf9eab 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSource.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSource.cs @@ -2,63 +2,62 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal class ProjectValueDataSource : ProjectValueDataSourceBase + where T : class { - internal class ProjectValueDataSource : ProjectValueDataSourceBase - where T : class - { - private IBroadcastBlock>? _broadcastBlock; - private int _version; + private IBroadcastBlock>? _broadcastBlock; + private int _version; - public ProjectValueDataSource(IProjectCommonServices services) - : base(services) - { - } + public ProjectValueDataSource(IProjectCommonServices services) + : base(services) + { + } - public override NamedIdentity DataSourceKey { get; } = new NamedIdentity("DataSource"); + public override NamedIdentity DataSourceKey { get; } = new NamedIdentity("DataSource"); - public override IComparable DataSourceVersion - { - get { return _version; } - } + public override IComparable DataSourceVersion + { + get { return _version; } + } - public override IReceivableSourceBlock> SourceBlock + public override IReceivableSourceBlock> SourceBlock + { + get { - get - { - EnsureInitialized(true); + EnsureInitialized(true); - return _broadcastBlock!; - } + return _broadcastBlock!; } + } - protected override void Initialize() - { - base.Initialize(); + protected override void Initialize() + { + base.Initialize(); - _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(options: null); - } + _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock>(options: null); + } - public async Task SendAndCompleteAsync(T value, IDataflowBlock targetBlock) - { - await SendAsync(value); + public async Task SendAndCompleteAsync(T value, IDataflowBlock targetBlock) + { + await SendAsync(value); - _broadcastBlock!.Complete(); + _broadcastBlock!.Complete(); - // Note, we have to wait for both the source *and* target block as - // the Completion of the source block doesn't mean that the target - // block has finished. - await Task.WhenAll(_broadcastBlock.Completion, targetBlock.Completion); - } + // Note, we have to wait for both the source *and* target block as + // the Completion of the source block doesn't mean that the target + // block has finished. + await Task.WhenAll(_broadcastBlock.Completion, targetBlock.Completion); + } - public async Task SendAsync(T value) - { - EnsureInitialized(true); + public async Task SendAsync(T value) + { + EnsureInitialized(true); - _version++; - await _broadcastBlock!.SendAsync(new ProjectVersionedValue( - value, - Empty.ProjectValueVersions.Add(DataSourceKey, _version))); - } + _version++; + await _broadcastBlock!.SendAsync(new ProjectVersionedValue( + value, + Empty.ProjectValueVersions.Add(DataSourceKey, _version))); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSourceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSourceFactory.cs index 74760c49dd..f47b8a630c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSourceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectValueDataSourceFactory.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class ProjectValueDataSourceFactory { - internal static class ProjectValueDataSourceFactory + public static ProjectValueDataSource Create(IProjectCommonServices services) + where T : class { - public static ProjectValueDataSource Create(IProjectCommonServices services) - where T : class - { - return new ProjectValueDataSource(services); - } + return new ProjectValueDataSource(services); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PropertyPageData.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PropertyPageData.cs index 40e1c557e7..d3824cdb01 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PropertyPageData.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/PropertyPageData.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal class PropertyPageData { - internal class PropertyPageData + public PropertyPageData(string category, string propertyName, object? value, List? setValues = null) { - public PropertyPageData(string category, string propertyName, object? value, List? setValues = null) - { - Category = category; - PropertyName = propertyName; - Value = value; - SetValues = setValues ?? new List(); - } - - public string Category { get; } - public string PropertyName { get; } - public object? Value { get; } - public List SetValues { get; } + Category = category; + PropertyName = propertyName; + Value = value; + SetValues = setValues ?? new List(); } + + public string Category { get; } + public string PropertyName { get; } + public object? Value { get; } + public List SetValues { get; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectFactory.cs index fe9fe2122d..c56efc58b9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectFactory.cs @@ -2,146 +2,145 @@ using System.Text; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class UnconfiguredProjectFactory { - internal static class UnconfiguredProjectFactory + public static UnconfiguredProject ImplementFullPath(string? fullPath) { - public static UnconfiguredProject ImplementFullPath(string? fullPath) - { - return Create(fullPath: fullPath); - } + return Create(fullPath: fullPath); + } - public static UnconfiguredProject Create(IProjectThreadingService threadingService) - { - var project = CreateDefault(threadingService); + public static UnconfiguredProject Create(IProjectThreadingService threadingService) + { + var project = CreateDefault(threadingService); - return project.Object; - } + return project.Object; + } - public static UnconfiguredProject CreateWithActiveConfiguredProjectProvider(IProjectThreadingService threadingService) - { - var project = CreateDefault(threadingService); + public static UnconfiguredProject CreateWithActiveConfiguredProjectProvider(IProjectThreadingService threadingService) + { + var project = CreateDefault(threadingService); - var activeConfiguredProject = new Mock().Object; - project.Setup(s => s.Services.ActiveConfiguredProjectProvider).Returns(activeConfiguredProject); + var activeConfiguredProject = new Mock().Object; + project.Setup(s => s.Services.ActiveConfiguredProjectProvider).Returns(activeConfiguredProject); - var configuredProject = new Mock(); + var configuredProject = new Mock(); - project.Setup(s => s.Services.ActiveConfiguredProjectProvider!.ActiveConfiguredProject).Returns(configuredProject.Object); - project.Setup(s => s.Services.ActiveConfiguredProjectProvider!.ActiveConfiguredProject!.ProjectVersion).Returns(configuredProject.Object.ProjectVersion); - return project.Object; - } + project.Setup(s => s.Services.ActiveConfiguredProjectProvider!.ActiveConfiguredProject).Returns(configuredProject.Object); + project.Setup(s => s.Services.ActiveConfiguredProjectProvider!.ActiveConfiguredProject!.ProjectVersion).Returns(configuredProject.Object.ProjectVersion); + return project.Object; + } - public static UnconfiguredProject Create(object? hostObject = null, - string? fullPath = null, - IProjectConfigurationsService? projectConfigurationsService = null, - ConfiguredProject? configuredProject = null, - IEnumerable? configuredProjects = null, - Encoding? projectEncoding = null, - IProjectAsynchronousTasksService? projectAsynchronousTasksService = null, - IProjectCapabilitiesScope? scope = null, - UnconfiguredProjectServices? unconfiguredProjectServices = null) + public static UnconfiguredProject Create(object? hostObject = null, + string? fullPath = null, + IProjectConfigurationsService? projectConfigurationsService = null, + ConfiguredProject? configuredProject = null, + IEnumerable? configuredProjects = null, + Encoding? projectEncoding = null, + IProjectAsynchronousTasksService? projectAsynchronousTasksService = null, + IProjectCapabilitiesScope? scope = null, + UnconfiguredProjectServices? unconfiguredProjectServices = null) + { + if (configuredProject is not null + && configuredProjects is null) { - if (configuredProject is not null - && configuredProjects is null) - { - configuredProjects = new[] { configuredProject }; - } - else if (configuredProjects is not null - && configuredProject is null) - { - configuredProject = configuredProjects.First(); - } - - var service = IProjectServiceFactory.Create(); - - if (unconfiguredProjectServices is null) - { - var unconfiguredProjectServicesMock = new Mock(); - - unconfiguredProjectServicesMock.SetupGet(u => u.FaultHandler) - .Returns(IProjectFaultHandlerServiceFactory.Create()); + configuredProjects = new[] { configuredProject }; + } + else if (configuredProjects is not null + && configuredProject is null) + { + configuredProject = configuredProjects.First(); + } - unconfiguredProjectServicesMock.SetupGet(u => u.HostObject) - .Returns(hostObject); + var service = IProjectServiceFactory.Create(); - unconfiguredProjectServicesMock.SetupGet(u => u.ProjectConfigurationsService) - .Returns(projectConfigurationsService); + if (unconfiguredProjectServices is null) + { + var unconfiguredProjectServicesMock = new Mock(); - var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.Create(getActiveConfiguredProject: () => configuredProject); - unconfiguredProjectServicesMock.Setup(u => u.ActiveConfiguredProjectProvider) - .Returns(activeConfiguredProjectProvider); + unconfiguredProjectServicesMock.SetupGet(u => u.FaultHandler) + .Returns(IProjectFaultHandlerServiceFactory.Create()); - unconfiguredProjectServicesMock.Setup(u => u.ProjectAsynchronousTasks) - .Returns(projectAsynchronousTasksService!); + unconfiguredProjectServicesMock.SetupGet(u => u.HostObject) + .Returns(hostObject); - var projectThreadingService = IProjectThreadingServiceFactory.Create(verifyOnUIThread: false); - unconfiguredProjectServicesMock.Setup(u => u.ThreadingPolicy) - .Returns(projectThreadingService); + unconfiguredProjectServicesMock.SetupGet(u => u.ProjectConfigurationsService) + .Returns(projectConfigurationsService); - unconfiguredProjectServices = unconfiguredProjectServicesMock.Object; - } + var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.Create(getActiveConfiguredProject: () => configuredProject); + unconfiguredProjectServicesMock.Setup(u => u.ActiveConfiguredProjectProvider) + .Returns(activeConfiguredProjectProvider); - var project = CreateDefault(); - project.Setup(u => u.ProjectService) - .Returns(service); + unconfiguredProjectServicesMock.Setup(u => u.ProjectAsynchronousTasks) + .Returns(projectAsynchronousTasksService!); - project.Setup(u => u.Services) - .Returns(unconfiguredProjectServices); + var projectThreadingService = IProjectThreadingServiceFactory.Create(verifyOnUIThread: false); + unconfiguredProjectServicesMock.Setup(u => u.ThreadingPolicy) + .Returns(projectThreadingService); - project.SetupGet(u => u.FullPath) - .Returns(fullPath); + unconfiguredProjectServices = unconfiguredProjectServicesMock.Object; + } - project.Setup(u => u.Capabilities) - .Returns(scope!); + var project = CreateDefault(); + project.Setup(u => u.ProjectService) + .Returns(service); - project.Setup(u => u.GetSuggestedConfiguredProjectAsync()).ReturnsAsync(configuredProject); + project.Setup(u => u.Services) + .Returns(unconfiguredProjectServices); - if (projectEncoding is not null) - { - project.Setup(u => u.GetFileEncodingAsync()).ReturnsAsync(projectEncoding); - } + project.SetupGet(u => u.FullPath) + .Returns(fullPath); - if (configuredProjects is not null) - { - project.Setup(p => p.LoadConfiguredProjectAsync(It.IsAny())) - .ReturnsAsync((ProjectConfiguration desiredConfig) => configuredProjects.First(configuredProject => configuredProject.ProjectConfiguration == desiredConfig)); - } + project.Setup(u => u.Capabilities) + .Returns(scope!); - return project.Object; - } + project.Setup(u => u.GetSuggestedConfiguredProjectAsync()).ReturnsAsync(configuredProject); - public static UnconfiguredProject CreateWithUnconfiguredProjectAdvanced() + if (projectEncoding is not null) { - var mock = CreateDefault(); - mock.As(); - return mock.Object; + project.Setup(u => u.GetFileEncodingAsync()).ReturnsAsync(projectEncoding); } - public static UnconfiguredProject ImplementGetEncodingAsync(Func> encoding) + if (configuredProjects is not null) { - var mock = CreateDefault(); - mock.Setup(u => u.GetFileEncodingAsync()).Returns(encoding); - return mock.Object; + project.Setup(p => p.LoadConfiguredProjectAsync(It.IsAny())) + .ReturnsAsync((ProjectConfiguration desiredConfig) => configuredProjects.First(configuredProject => configuredProject.ProjectConfiguration == desiredConfig)); } - public static UnconfiguredProject ImplementLoadConfiguredProjectAsync(Func> action) - { - var mock = CreateDefault(); - mock.Setup(p => p.LoadConfiguredProjectAsync(It.IsAny())) - .Returns(action); + return project.Object; + } - return mock.Object; - } + public static UnconfiguredProject CreateWithUnconfiguredProjectAdvanced() + { + var mock = CreateDefault(); + mock.As(); + return mock.Object; + } - private static Mock CreateDefault(IProjectThreadingService? threadingService = null) - { - var unconfiguredProjectServices = UnconfiguredProjectServicesFactory.Create(threadingService); - var project = new Mock(); - project.Setup(u => u.Services) - .Returns(unconfiguredProjectServices); + public static UnconfiguredProject ImplementGetEncodingAsync(Func> encoding) + { + var mock = CreateDefault(); + mock.Setup(u => u.GetFileEncodingAsync()).Returns(encoding); + return mock.Object; + } - return project; - } + public static UnconfiguredProject ImplementLoadConfiguredProjectAsync(Func> action) + { + var mock = CreateDefault(); + mock.Setup(p => p.LoadConfiguredProjectAsync(It.IsAny())) + .Returns(action); + + return mock.Object; + } + + private static Mock CreateDefault(IProjectThreadingService? threadingService = null) + { + var unconfiguredProjectServices = UnconfiguredProjectServicesFactory.Create(threadingService); + var project = new Mock(); + project.Setup(u => u.Services) + .Returns(unconfiguredProjectServices); + + return project; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectServicesFactory.cs index 34502838a7..3dd12d04ff 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/UnconfiguredProjectServicesFactory.cs @@ -1,43 +1,42 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class UnconfiguredProjectServicesFactory { - internal static class UnconfiguredProjectServicesFactory + public static UnconfiguredProjectServices Create( + IProjectThreadingService? threadingService = null, + IProjectFaultHandlerService? projectFaultHandlerService = null, + IProjectService? projectService = null, + IProjectConfigurationsService? projectConfigurationsService = null) { - public static UnconfiguredProjectServices Create( - IProjectThreadingService? threadingService = null, - IProjectFaultHandlerService? projectFaultHandlerService = null, - IProjectService? projectService = null, - IProjectConfigurationsService? projectConfigurationsService = null) - { - projectFaultHandlerService ??= IProjectFaultHandlerServiceFactory.Create(); - threadingService ??= IProjectThreadingServiceFactory.Create(); - - var projectLockService = IProjectLockServiceFactory.Create(); + projectFaultHandlerService ??= IProjectFaultHandlerServiceFactory.Create(); + threadingService ??= IProjectThreadingServiceFactory.Create(); - var mock = new Mock(); + var projectLockService = IProjectLockServiceFactory.Create(); - projectService ??= IProjectServiceFactory.Create(ProjectServicesFactory.Create(threadingService, projectLockService: projectLockService)); + var mock = new Mock(); - mock.SetupGet(p => p.ProjectService) - .Returns(projectService); + projectService ??= IProjectServiceFactory.Create(ProjectServicesFactory.Create(threadingService, projectLockService: projectLockService)); - mock.Setup(p => p.ProjectLockService) - .Returns(projectLockService); + mock.SetupGet(p => p.ProjectService) + .Returns(projectService); - mock.Setup(p => p.FaultHandler) - .Returns(projectFaultHandlerService); + mock.Setup(p => p.ProjectLockService) + .Returns(projectLockService); - mock.Setup(p => p.ThreadingPolicy) - .Returns(threadingService); + mock.Setup(p => p.FaultHandler) + .Returns(projectFaultHandlerService); - if (projectConfigurationsService is not null) - { - mock.SetupGet(p => p.ProjectConfigurationsService) - .Returns(projectConfigurationsService); - } + mock.Setup(p => p.ThreadingPolicy) + .Returns(threadingService); - return mock.Object; + if (projectConfigurationsService is not null) + { + mock.SetupGet(p => p.ProjectConfigurationsService) + .Returns(projectConfigurationsService); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/OrderPrecedenceImportCollectionExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/OrderPrecedenceImportCollectionExtensions.cs index 2509003261..7b1ce7b973 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/OrderPrecedenceImportCollectionExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/OrderPrecedenceImportCollectionExtensions.cs @@ -1,25 +1,24 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class OrderPrecedenceImportCollectionExtensions { - internal static class OrderPrecedenceImportCollectionExtensions + public static void Add(this OrderPrecedenceImportCollection collection, T value, string? appliesTo = null, int orderPrecedence = 0) { - public static void Add(this OrderPrecedenceImportCollection collection, T value, string? appliesTo = null, int orderPrecedence = 0) - { - var metadata = IOrderPrecedenceMetadataViewFactory.Create(appliesTo, orderPrecedence); + var metadata = IOrderPrecedenceMetadataViewFactory.Create(appliesTo, orderPrecedence); - var export = new Lazy(() => value, metadata); + var export = new Lazy(() => value, metadata); - collection.Add(export); - } + collection.Add(export); + } - public static void Add(this OrderPrecedenceExportFactoryCollection collection, T value, string? appliesTo = null, int orderPrecedence = 0) - { - var metadata = IOrderPrecedenceMetadataViewFactory.Create(appliesTo, orderPrecedence); + public static void Add(this OrderPrecedenceExportFactoryCollection collection, T value, string? appliesTo = null, int orderPrecedence = 0) + { + var metadata = IOrderPrecedenceMetadataViewFactory.Create(appliesTo, orderPrecedence); - var factory = ExportFactoryFactory.ImplementCreateValueWithAutoDispose(() => value, metadata); + var factory = ExportFactoryFactory.ImplementCreateValueWithAutoDispose(() => value, metadata); - collection.Add(factory); - } + collection.Add(factory); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/AbstractMultiLifetimeComponentTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/AbstractMultiLifetimeComponentTests.cs index b2f03efe60..54eecffaac 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/AbstractMultiLifetimeComponentTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/AbstractMultiLifetimeComponentTests.cs @@ -1,186 +1,185 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class AbstractMultiLifetimeComponentTests { - public class AbstractMultiLifetimeComponentTests + [Fact] + public void WaitForLoadedAsync_WhenNotLoadedAsync_ReturnsNonCompletedTask() { - [Fact] - public void WaitForLoadedAsync_WhenNotLoadedAsync_ReturnsNonCompletedTask() - { - var component = CreateInstance(); + var component = CreateInstance(); - var result = component.WaitForLoadedAsync(); + var result = component.WaitForLoadedAsync(); - Assert.False(result.IsCanceled); - Assert.False(result.IsCompleted); - Assert.False(result.IsFaulted); - } + Assert.False(result.IsCanceled); + Assert.False(result.IsCompleted); + Assert.False(result.IsFaulted); + } - [Fact] - public async Task WaitForLoadedAsync_WhenLoaded_ReturnsCompletedTask() - { - var component = CreateInstance(); + [Fact] + public async Task WaitForLoadedAsync_WhenLoaded_ReturnsCompletedTask() + { + var component = CreateInstance(); - await component.LoadAsync(); + await component.LoadAsync(); - var result = component.WaitForLoadedAsync(); + var result = component.WaitForLoadedAsync(); - Assert.True(result.IsCompleted); - } + Assert.True(result.IsCompleted); + } - [Fact] - public async Task WaitForLoadedAsync_WhenUnloaded_ReturnsNonCompletedTask() - { - var component = CreateInstance(); + [Fact] + public async Task WaitForLoadedAsync_WhenUnloaded_ReturnsNonCompletedTask() + { + var component = CreateInstance(); - await component.LoadAsync(); - await component.UnloadAsync(); + await component.LoadAsync(); + await component.UnloadAsync(); - var result = component.WaitForLoadedAsync(); + var result = component.WaitForLoadedAsync(); - Assert.False(result.IsCanceled); - Assert.False(result.IsCompleted); - Assert.False(result.IsFaulted); - } + Assert.False(result.IsCanceled); + Assert.False(result.IsCompleted); + Assert.False(result.IsFaulted); + } - [Fact] - public async Task WaitForLoadedAsync_DisposedWhenUnloaded_ReturnsCancelledTask() - { - var component = CreateInstance(); + [Fact] + public async Task WaitForLoadedAsync_DisposedWhenUnloaded_ReturnsCancelledTask() + { + var component = CreateInstance(); - await component.DisposeAsync(); + await component.DisposeAsync(); - var result = component.WaitForLoadedAsync(); + var result = component.WaitForLoadedAsync(); - Assert.True(result.IsCanceled); - } + Assert.True(result.IsCanceled); + } - [Fact] - public async Task WaitForLoadedAsync_DisposedWhenLoaded_ReturnsCancelledTask() - { - var component = CreateInstance(); + [Fact] + public async Task WaitForLoadedAsync_DisposedWhenLoaded_ReturnsCancelledTask() + { + var component = CreateInstance(); - await component.LoadAsync(); - await component.DisposeAsync(); + await component.LoadAsync(); + await component.DisposeAsync(); - var result = component.WaitForLoadedAsync(); + var result = component.WaitForLoadedAsync(); - Assert.True(result.IsCanceled); - } + Assert.True(result.IsCanceled); + } - [Fact] - public async Task LoadAsync_Initializes() - { - var component = CreateInstance(); + [Fact] + public async Task LoadAsync_Initializes() + { + var component = CreateInstance(); - await component.LoadAsync(); + await component.LoadAsync(); - Assert.True(component.IsInitialized); - } + Assert.True(component.IsInitialized); + } - [Fact] - public async Task LoadAsync_InitializesUnderlyingInstance() - { - var component = CreateInstance(); + [Fact] + public async Task LoadAsync_InitializesUnderlyingInstance() + { + var component = CreateInstance(); - await component.LoadAsync(); + await component.LoadAsync(); - var result = await component.WaitForLoadedAsync(); + var result = await component.WaitForLoadedAsync(); - Assert.True(result.IsInitialized); - } + Assert.True(result.IsInitialized); + } - [Fact] - public async Task LoadAsync_WhenAlreadyLoaded_DoesNotCreateNewInstance() - { - var component = CreateInstance(); + [Fact] + public async Task LoadAsync_WhenAlreadyLoaded_DoesNotCreateNewInstance() + { + var component = CreateInstance(); - await component.LoadAsync(); + await component.LoadAsync(); - var instance = await component.WaitForLoadedAsync(); + var instance = await component.WaitForLoadedAsync(); - await component.LoadAsync(); + await component.LoadAsync(); - var result = await component.WaitForLoadedAsync(); + var result = await component.WaitForLoadedAsync(); - Assert.Same(instance, result); - } + Assert.Same(instance, result); + } - [Fact] - public async Task LoadAsync_WhenUnloaded_CreatesNewInstance() - { - var component = CreateInstance(); + [Fact] + public async Task LoadAsync_WhenUnloaded_CreatesNewInstance() + { + var component = CreateInstance(); - await component.LoadAsync(); + await component.LoadAsync(); - var instance = await component.WaitForLoadedAsync(); + var instance = await component.WaitForLoadedAsync(); - await component.UnloadAsync(); + await component.UnloadAsync(); - // We should create a new instance here - await component.LoadAsync(); + // We should create a new instance here + await component.LoadAsync(); - var result = await component.WaitForLoadedAsync(); + var result = await component.WaitForLoadedAsync(); - Assert.NotSame(instance, result); - } + Assert.NotSame(instance, result); + } - [Fact] - public async Task UnloadAsync_WhenLoaded_DisposesUnderlyingInstance() - { - var component = CreateInstance(); + [Fact] + public async Task UnloadAsync_WhenLoaded_DisposesUnderlyingInstance() + { + var component = CreateInstance(); - await component.LoadAsync(); - var result = await component.WaitForLoadedAsync(); + await component.LoadAsync(); + var result = await component.WaitForLoadedAsync(); - await component.UnloadAsync(); + await component.UnloadAsync(); - Assert.True(result.IsDisposed); - } + Assert.True(result.IsDisposed); + } - [Fact] - public async Task UnloadAsync_WhenNotLoaded_DoesNothing() - { - var component = CreateInstance(); + [Fact] + public async Task UnloadAsync_WhenNotLoaded_DoesNothing() + { + var component = CreateInstance(); - await component.UnloadAsync(); - } + await component.UnloadAsync(); + } - [Fact] - public async Task UnloadAsync_DoesNotDispose() - { - var component = CreateInstance(); + [Fact] + public async Task UnloadAsync_DoesNotDispose() + { + var component = CreateInstance(); - await component.UnloadAsync(); + await component.UnloadAsync(); - Assert.False(component.IsDisposed); - } + Assert.False(component.IsDisposed); + } - [Fact] - public async Task DisposeAsync_WhenNotLoaded_DoesNothing() - { - var component = CreateInstance(); - await component.DisposeAsync(); + [Fact] + public async Task DisposeAsync_WhenNotLoaded_DoesNothing() + { + var component = CreateInstance(); + await component.DisposeAsync(); - Assert.True(component.IsDisposed); - } + Assert.True(component.IsDisposed); + } - [Fact] - public async Task DisposeAsync_WhenLoaded_DisposesUnderlyingInstance() - { - var component = CreateInstance(); + [Fact] + public async Task DisposeAsync_WhenLoaded_DisposesUnderlyingInstance() + { + var component = CreateInstance(); - await component.LoadAsync(); - var instance = await component.WaitForLoadedAsync(); + await component.LoadAsync(); + var instance = await component.WaitForLoadedAsync(); - await component.DisposeAsync(); + await component.DisposeAsync(); - Assert.True(instance.IsDisposed); - } + Assert.True(instance.IsDisposed); + } - private static AbstractMultiLifetimeComponentFactory.MultiLifetimeComponent CreateInstance() - { - return AbstractMultiLifetimeComponentFactory.Create(); - } + private static AbstractMultiLifetimeComponentFactory.MultiLifetimeComponent CreateInstance() + { + return AbstractMultiLifetimeComponentFactory.Create(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsLoaderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsLoaderTests.cs index c5654e11a7..f03fd9cbf9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsLoaderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsLoaderTests.cs @@ -1,141 +1,140 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class ActiveConfiguredProjectsLoaderTests { - public class ActiveConfiguredProjectsLoaderTests + [Theory] + [InlineData(new object[] { new[] { "Debug|x86" } })] + [InlineData(new object[] { new[] { "Debug|x86", "Release|x86" } })] + [InlineData(new object[] { new[] { "Debug|x86", "Release|x86", "Release|AnyCPU" } })] + public async Task WhenActiveConfigurationChanges_LoadsConfiguredProject(string[] configurationNames) { - [Theory] - [InlineData(new object[] { new[] { "Debug|x86" } })] - [InlineData(new object[] { new[] { "Debug|x86", "Release|x86" } })] - [InlineData(new object[] { new[] { "Debug|x86", "Release|x86", "Release|AnyCPU" } })] - public async Task WhenActiveConfigurationChanges_LoadsConfiguredProject(string[] configurationNames) + var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames(configurationNames); + var configuredProject = ConfiguredProjectFactory.Create(); + + var results = new List(); + var project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => { - var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames(configurationNames); - var configuredProject = ConfiguredProjectFactory.Create(); + results.Add(configuration.Name); + return Task.FromResult(configuredProject); + }); - var results = new List(); - var project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => - { - results.Add(configuration.Name); - return Task.FromResult(configuredProject); - }); + var loader = CreateInstance(project, out ProjectValueDataSource> source); + await loader.InitializeAsync(); - var loader = CreateInstance(project, out ProjectValueDataSource> source); - await loader.InitializeAsync(); + // Change the active configurations + await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); - // Change the active configurations - await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); + Assert.Equal(configurationNames, results); + } - Assert.Equal(configurationNames, results); - } + [Fact] + public async Task WhenProjectUnloading_DoesNotLoadConfiguredProject() + { + var tasksService = IUnconfiguredProjectTasksServiceFactory.CreateWithUnloadedProject(); + var configuredProject = ConfiguredProjectFactory.Create(); - [Fact] - public async Task WhenProjectUnloading_DoesNotLoadConfiguredProject() + int callCount = 0; + UnconfiguredProject project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => { - var tasksService = IUnconfiguredProjectTasksServiceFactory.CreateWithUnloadedProject(); - var configuredProject = ConfiguredProjectFactory.Create(); + callCount++; + return Task.FromResult(configuredProject); + }); - int callCount = 0; - UnconfiguredProject project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => - { - callCount++; - return Task.FromResult(configuredProject); - }); + var loader = CreateInstance(project, tasksService, out ProjectValueDataSource> source); + await loader.InitializeAsync(); - var loader = CreateInstance(project, tasksService, out ProjectValueDataSource> source); - await loader.InitializeAsync(); + var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU"); - var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU"); + // Change the active configurations + await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); - // Change the active configurations - await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); + // Should not be listening + Assert.Equal(0, callCount); + } - // Should not be listening - Assert.Equal(0, callCount); - } + [Fact] + public async Task InitializeAsync_CanNotInitializeTwice() + { + var configuredProject = ConfiguredProjectFactory.Create(); - [Fact] - public async Task InitializeAsync_CanNotInitializeTwice() + var results = new List(); + var project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => { - var configuredProject = ConfiguredProjectFactory.Create(); + results.Add(configuration.Name); + return Task.FromResult(configuredProject); + }); - var results = new List(); - var project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => - { - results.Add(configuration.Name); - return Task.FromResult(configuredProject); - }); + var loader = CreateInstance(project, out var source); - var loader = CreateInstance(project, out var source); + await loader.InitializeAsync(); + await loader.InitializeAsync(); - await loader.InitializeAsync(); - await loader.InitializeAsync(); + var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU"); - var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU"); + // Change the active configurations + await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); - // Change the active configurations - await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); + Assert.Equal(new string[] { "Debug|AnyCPU" }, results); + } - Assert.Equal(new string[] { "Debug|AnyCPU" }, results); - } + [Fact] + public async Task Dispose_WhenNotInitialized_DoesNotThrow() + { + var project = UnconfiguredProjectFactory.CreateWithActiveConfiguredProjectProvider(IProjectThreadingServiceFactory.Create()); - [Fact] - public async Task Dispose_WhenNotInitialized_DoesNotThrow() - { - var project = UnconfiguredProjectFactory.CreateWithActiveConfiguredProjectProvider(IProjectThreadingServiceFactory.Create()); + var loader = CreateInstance(project, out _); - var loader = CreateInstance(project, out _); + await loader.DisposeAsync(); - await loader.DisposeAsync(); + Assert.True(loader.IsDisposed); + } - Assert.True(loader.IsDisposed); - } + [Fact] + public async Task Dispose_WhenInitialized_DisposesSubscription() + { + var configuredProject = ConfiguredProjectFactory.Create(); - [Fact] - public async Task Dispose_WhenInitialized_DisposesSubscription() + int callCount = 0; + UnconfiguredProject project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => { - var configuredProject = ConfiguredProjectFactory.Create(); + callCount++; + return Task.FromResult(configuredProject); + }); - int callCount = 0; - UnconfiguredProject project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync(configuration => - { - callCount++; - return Task.FromResult(configuredProject); - }); + var loader = CreateInstance(project, out ProjectValueDataSource> source); + await loader.InitializeAsync(); + await loader.DisposeAsync(); - var loader = CreateInstance(project, out ProjectValueDataSource> source); - await loader.InitializeAsync(); - await loader.DisposeAsync(); + var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU"); - var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU"); + // Change the active configurations + await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); - // Change the active configurations - await source.SendAndCompleteAsync(configurationGroups, loader.TargetBlock); - - // Should not be listening - Assert.Equal(0, callCount); - } - private static ActiveConfiguredProjectsLoader CreateInstance(UnconfiguredProject project, out ProjectValueDataSource> source) - { - return CreateInstance(project, null, out source); - } + // Should not be listening + Assert.Equal(0, callCount); + } + private static ActiveConfiguredProjectsLoader CreateInstance(UnconfiguredProject project, out ProjectValueDataSource> source) + { + return CreateInstance(project, null, out source); + } - private static ActiveConfiguredProjectsLoader CreateInstance(UnconfiguredProject project, IUnconfiguredProjectTasksService? tasksService, out ProjectValueDataSource> source) - { - var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); - source = ProjectValueDataSourceFactory.Create>(services); - var activeConfigurationGroupService = IActiveConfigurationGroupServiceFactory.Implement(source); + private static ActiveConfiguredProjectsLoader CreateInstance(UnconfiguredProject project, IUnconfiguredProjectTasksService? tasksService, out ProjectValueDataSource> source) + { + var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); + source = ProjectValueDataSourceFactory.Create>(services); + var activeConfigurationGroupService = IActiveConfigurationGroupServiceFactory.Implement(source); - var loader = CreateInstance(project, activeConfigurationGroupService, tasksService); + var loader = CreateInstance(project, activeConfigurationGroupService, tasksService); - return loader; - } + return loader; + } - private static ActiveConfiguredProjectsLoader CreateInstance(UnconfiguredProject project, IActiveConfigurationGroupService activeConfigurationGroupService, IUnconfiguredProjectTasksService? tasksService) - { - tasksService ??= IUnconfiguredProjectTasksServiceFactory.ImplementLoadedProjectAsync(t => t()); + private static ActiveConfiguredProjectsLoader CreateInstance(UnconfiguredProject project, IActiveConfigurationGroupService activeConfigurationGroupService, IUnconfiguredProjectTasksService? tasksService) + { + tasksService ??= IUnconfiguredProjectTasksServiceFactory.ImplementLoadedProjectAsync(t => t()); - return new ActiveConfiguredProjectsLoader(project, activeConfigurationGroupService, tasksService); - } + return new ActiveConfiguredProjectsLoader(project, activeConfigurationGroupService, tasksService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsProviderTests.cs index 5452cf33eb..51d1a77a32 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ActiveConfiguredProjectsProviderTests.cs @@ -2,160 +2,159 @@ using Microsoft.VisualStudio.ProjectSystem.Configuration; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class ActiveConfiguredProjectsProviderTests { - public class ActiveConfiguredProjectsProviderTests + [Fact] + public async Task GetActiveProjectConfigurationsAsync_WhenNoActiveConfiguration_ReturnsNull() { - [Fact] - public async Task GetActiveProjectConfigurationsAsync_WhenNoActiveConfiguration_ReturnsNull() - { - var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.ImplementActiveProjectConfiguration(() => null); - var services = IUnconfiguredProjectServicesFactory.Create(activeConfiguredProjectProvider: activeConfiguredProjectProvider); + var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.ImplementActiveProjectConfiguration(() => null); + var services = IUnconfiguredProjectServicesFactory.Create(activeConfiguredProjectProvider: activeConfiguredProjectProvider); - var provider = CreateInstance(services: services); + var provider = CreateInstance(services: services); - var result = await provider.GetActiveProjectConfigurationsAsync(); + var result = await provider.GetActiveProjectConfigurationsAsync(); - Assert.Null(result); - } + Assert.Null(result); + } - [Fact] - public async Task GetActiveConfiguredProjectAsync_WhenNoActiveConfiguration_ReturnsNull() - { - var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.ImplementActiveProjectConfiguration(() => null); - var services = IUnconfiguredProjectServicesFactory.Create(activeConfiguredProjectProvider: activeConfiguredProjectProvider); + [Fact] + public async Task GetActiveConfiguredProjectAsync_WhenNoActiveConfiguration_ReturnsNull() + { + var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.ImplementActiveProjectConfiguration(() => null); + var services = IUnconfiguredProjectServicesFactory.Create(activeConfiguredProjectProvider: activeConfiguredProjectProvider); - var provider = CreateInstance(services: services); + var provider = CreateInstance(services: services); - var result = await provider.GetActiveConfiguredProjectsAsync(); + var result = await provider.GetActiveConfiguredProjectsAsync(); - Assert.Null(result); - } + Assert.Null(result); + } - [Fact] - public async Task GetActiveProjectConfigurationsAsync_WhenNoDimensionProviders_ReturnsNoDimensionNames() - { - var provider = CreateInstance("Debug|AnyCPU", "Debug|AnyCPU"); + [Fact] + public async Task GetActiveProjectConfigurationsAsync_WhenNoDimensionProviders_ReturnsNoDimensionNames() + { + var provider = CreateInstance("Debug|AnyCPU", "Debug|AnyCPU"); - var result = await provider.GetActiveProjectConfigurationsAsync(); + var result = await provider.GetActiveProjectConfigurationsAsync(); - Assert.Empty(result!.DimensionNames); - } + Assert.Empty(result!.DimensionNames); + } - [Fact] - public async Task GetActiveConfiguredProjectAsync_WhenNoDimensionProviders_ReturnsNoDimensionNames() - { - var provider = CreateInstance("Debug|AnyCPU", "Debug|AnyCPU"); + [Fact] + public async Task GetActiveConfiguredProjectAsync_WhenNoDimensionProviders_ReturnsNoDimensionNames() + { + var provider = CreateInstance("Debug|AnyCPU", "Debug|AnyCPU"); - var result = await provider.GetActiveConfiguredProjectsAsync(); + var result = await provider.GetActiveConfiguredProjectsAsync(); - Assert.Empty(result!.DimensionNames); - } + Assert.Empty(result!.DimensionNames); + } - [Theory] // ActiveConfiguration Configurations - [InlineData("Debug|AnyCPU", "Debug|AnyCPU")] - [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46")] - [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46;Release|AnyCPU|net46")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] - [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] - [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] - [InlineData("Release|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] - [InlineData("Release|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] - [InlineData("Debug|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] - [InlineData("Release|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] - [InlineData("Release|x86", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] - public async Task GetActiveProjectConfigurationsAsync_WhenNoDimensionProviders_ReturnsActiveProjectConfiguration(string activeConfiguration, string configurations) - { - var provider = CreateInstance(activeConfiguration, configurations); + [Theory] // ActiveConfiguration Configurations + [InlineData("Debug|AnyCPU", "Debug|AnyCPU")] + [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46")] + [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46;Release|AnyCPU|net46")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] + [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] + [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] + [InlineData("Release|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] + [InlineData("Release|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] + [InlineData("Debug|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] + [InlineData("Release|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] + [InlineData("Release|x86", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] + public async Task GetActiveProjectConfigurationsAsync_WhenNoDimensionProviders_ReturnsActiveProjectConfiguration(string activeConfiguration, string configurations) + { + var provider = CreateInstance(activeConfiguration, configurations); - var result = await provider.GetActiveProjectConfigurationsAsync(); + var result = await provider.GetActiveProjectConfigurationsAsync(); - Assert.Single(result!.Objects); - Assert.Equal(activeConfiguration, result.Objects[0].Name); - } + Assert.Single(result!.Objects); + Assert.Equal(activeConfiguration, result.Objects[0].Name); + } - [Theory] // ActiveConfiguration Configurations Expected Active Configurations - [InlineData("Debug|AnyCPU|net45", "Debug|AnyCPU|net45", "Debug|AnyCPU|net45")] - [InlineData("Debug|AnyCPU|net45", "Debug|AnyCPU|net45;Release|AnyCPU|net45", "Debug|AnyCPU|net45")] - [InlineData("Debug|AnyCPU|net45", "Debug|AnyCPU|net45;Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46")] - [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU|net45", "Debug|AnyCPU|net45")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU|net45;Release|AnyCPU|net45", "Debug|AnyCPU|net45")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU|net45;Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46")] - public async Task GetActiveProjectConfigurationsAsync_ConfigurationsWithTargetFrameworkDimensionProvider_ReturnsConfigsThatMatchConfigurationAndPlatformFromActiveConfiguration(string activeConfiguration, string configurations, string expected) - { - var provider = CreateInstance(activeConfiguration, configurations, "TargetFramework"); + [Theory] // ActiveConfiguration Configurations Expected Active Configurations + [InlineData("Debug|AnyCPU|net45", "Debug|AnyCPU|net45", "Debug|AnyCPU|net45")] + [InlineData("Debug|AnyCPU|net45", "Debug|AnyCPU|net45;Release|AnyCPU|net45", "Debug|AnyCPU|net45")] + [InlineData("Debug|AnyCPU|net45", "Debug|AnyCPU|net45;Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46")] + [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU|net45", "Debug|AnyCPU|net45")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU|net45;Release|AnyCPU|net45", "Debug|AnyCPU|net45")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU|net45;Debug|AnyCPU|net46", "Debug|AnyCPU|net45;Debug|AnyCPU|net46")] + public async Task GetActiveProjectConfigurationsAsync_ConfigurationsWithTargetFrameworkDimensionProvider_ReturnsConfigsThatMatchConfigurationAndPlatformFromActiveConfiguration(string activeConfiguration, string configurations, string expected) + { + var provider = CreateInstance(activeConfiguration, configurations, "TargetFramework"); - var result = await provider.GetActiveProjectConfigurationsAsync(); + var result = await provider.GetActiveProjectConfigurationsAsync(); - var activeConfigs = ProjectConfigurationFactory.CreateMany(expected.Split(';')); + var activeConfigs = ProjectConfigurationFactory.CreateMany(expected.Split(';')); - Assert.NotNull(result); - Assert.Equal(activeConfigs.OrderBy(c => c.Name), result.Objects.OrderBy(c => c.Name)); - Assert.Equal(new[] { "TargetFramework" }, result.DimensionNames); - } + Assert.NotNull(result); + Assert.Equal(activeConfigs.OrderBy(c => c.Name), result.Objects.OrderBy(c => c.Name)); + Assert.Equal(new[] { "TargetFramework" }, result.DimensionNames); + } - [Theory] // ActiveConfiguration Configurations - [InlineData("Debug|AnyCPU", "Debug|AnyCPU")] - [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46")] - [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46;Release|AnyCPU|net46")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] - [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] - [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] - [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] - [InlineData("Release|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] - [InlineData("Release|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] - [InlineData("Debug|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] - [InlineData("Release|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] - [InlineData("Release|x86", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] - public async Task GetActiveConfiguredProjects__WhenNoDimensionProviders_LoadsAndReturnsConfiguredProject(string activeConfiguration, string configurations) - { - var provider = CreateInstance(activeConfiguration, configurations); + [Theory] // ActiveConfiguration Configurations + [InlineData("Debug|AnyCPU", "Debug|AnyCPU")] + [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46")] + [InlineData("Debug|AnyCPU|net46", "Debug|AnyCPU|net46;Release|AnyCPU|net46")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] + [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] + [InlineData("Debug|AnyCPU", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] + [InlineData("Debug|AnyCPU", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] + [InlineData("Release|AnyCPU", "Debug|AnyCPU;Release|AnyCPU")] + [InlineData("Release|AnyCPU", "Release|AnyCPU;Debug|AnyCPU")] + [InlineData("Debug|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86")] + [InlineData("Release|x86", "Debug|AnyCPU;Release|AnyCPU;Debug|x86;Release|x86")] + [InlineData("Release|x86", "Release|AnyCPU;Debug|x86;Release|x86;Debug|AnyCPU")] + public async Task GetActiveConfiguredProjects__WhenNoDimensionProviders_LoadsAndReturnsConfiguredProject(string activeConfiguration, string configurations) + { + var provider = CreateInstance(activeConfiguration, configurations); - var result = await provider.GetActiveConfiguredProjectsAsync(); + var result = await provider.GetActiveConfiguredProjectsAsync(); - Assert.NotNull(result); - Assert.Single(result.Objects); - Assert.Equal(activeConfiguration, result.Objects[0].ProjectConfiguration.Name); - Assert.Empty(result.DimensionNames); - } + Assert.NotNull(result); + Assert.Single(result.Objects); + Assert.Equal(activeConfiguration, result.Objects[0].ProjectConfiguration.Name); + Assert.Empty(result.DimensionNames); + } - private static ActiveConfiguredProjectsProvider CreateInstance(string activeConfiguration, string configurations, params string[] dimensionNames) + private static ActiveConfiguredProjectsProvider CreateInstance(string activeConfiguration, string configurations, params string[] dimensionNames) + { + var activeConfig = ProjectConfigurationFactory.Create(activeConfiguration); + var configs = ProjectConfigurationFactory.CreateMany(configurations.Split(';')); + var configurationsService = IProjectConfigurationsServiceFactory.ImplementGetKnownProjectConfigurationsAsync(configs.ToImmutableHashSet()); + var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.ImplementActiveProjectConfiguration(() => activeConfig); + var services = IUnconfiguredProjectServicesFactory.Create(activeConfiguredProjectProvider: activeConfiguredProjectProvider, projectConfigurationsService: configurationsService); + var project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync((projectConfiguration) => { - var activeConfig = ProjectConfigurationFactory.Create(activeConfiguration); - var configs = ProjectConfigurationFactory.CreateMany(configurations.Split(';')); - var configurationsService = IProjectConfigurationsServiceFactory.ImplementGetKnownProjectConfigurationsAsync(configs.ToImmutableHashSet()); - var activeConfiguredProjectProvider = IActiveConfiguredProjectProviderFactory.ImplementActiveProjectConfiguration(() => activeConfig); - var services = IUnconfiguredProjectServicesFactory.Create(activeConfiguredProjectProvider: activeConfiguredProjectProvider, projectConfigurationsService: configurationsService); - var project = UnconfiguredProjectFactory.ImplementLoadConfiguredProjectAsync((projectConfiguration) => - { - return Task.FromResult(ConfiguredProjectFactory.ImplementProjectConfiguration(projectConfiguration)); - }); + return Task.FromResult(ConfiguredProjectFactory.ImplementProjectConfiguration(projectConfiguration)); + }); - var dimensionProviders = dimensionNames.Select(IActiveConfiguredProjectsDimensionProviderFactory.ImplementDimensionName); + var dimensionProviders = dimensionNames.Select(IActiveConfiguredProjectsDimensionProviderFactory.ImplementDimensionName); - return CreateInstance(services: services, project: project, dimensionProviders: dimensionProviders); - } + return CreateInstance(services: services, project: project, dimensionProviders: dimensionProviders); + } - private static ActiveConfiguredProjectsProvider CreateInstance(IUnconfiguredProjectServices? services = null, UnconfiguredProject? project = null, IEnumerable? dimensionProviders = null) - { - services ??= IUnconfiguredProjectServicesFactory.Create(); - project ??= UnconfiguredProjectFactory.Create(); + private static ActiveConfiguredProjectsProvider CreateInstance(IUnconfiguredProjectServices? services = null, UnconfiguredProject? project = null, IEnumerable? dimensionProviders = null) + { + services ??= IUnconfiguredProjectServicesFactory.Create(); + project ??= UnconfiguredProjectFactory.Create(); - var provider = new ActiveConfiguredProjectsProvider(services, project); + var provider = new ActiveConfiguredProjectsProvider(services, project); - if (dimensionProviders is not null) + if (dimensionProviders is not null) + { + foreach (var dimensionProvider in dimensionProviders) { - foreach (var dimensionProvider in dimensionProviders) - { - provider.DimensionProviders.Add(dimensionProvider, appliesTo: ProjectCapabilities.AlwaysApplicable); - } + provider.DimensionProviders.Add(dimensionProvider, appliesTo: ProjectCapabilities.AlwaysApplicable); } - - return provider; } + + return provider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/PublishableProjectConfigProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/PublishableProjectConfigProviderTests.cs index ac4d431674..6137db1d5a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/PublishableProjectConfigProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/PublishableProjectConfigProviderTests.cs @@ -1,42 +1,41 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +public class PublishableProjectConfigProviderTests { - public class PublishableProjectConfigProviderTests + [Fact] + public async Task IsPublishSupportedAsync_ReturnsFalse() { - [Fact] - public async Task IsPublishSupportedAsync_ReturnsFalse() - { - var provider = CreateInstance(); - - var result = await provider.IsPublishSupportedAsync(); + var provider = CreateInstance(); - Assert.False(result); - } + var result = await provider.IsPublishSupportedAsync(); - [Fact] - public async Task PublishAsync_ThrowsInvalidOperation() - { - var provider = CreateInstance(); - var writer = new StringWriter(); + Assert.False(result); + } - await Assert.ThrowsAsync(() => - { - return provider.PublishAsync(CancellationToken.None, writer); - }); - } + [Fact] + public async Task PublishAsync_ThrowsInvalidOperation() + { + var provider = CreateInstance(); + var writer = new StringWriter(); - [Fact] - public async Task ShowPublishPromptAsync_ThrowsInvalidOperation() + await Assert.ThrowsAsync(() => { - var provider = CreateInstance(); + return provider.PublishAsync(CancellationToken.None, writer); + }); + } - await Assert.ThrowsAsync(provider.ShowPublishPromptAsync); - } + [Fact] + public async Task ShowPublishPromptAsync_ThrowsInvalidOperation() + { + var provider = CreateInstance(); - private static PublishableProjectConfigProvider CreateInstance() - { - return new PublishableProjectConfigProvider(); - } + await Assert.ThrowsAsync(provider.ShowPublishPromptAsync); + } + + private static PublishableProjectConfigProvider CreateInstance() + { + return new PublishableProjectConfigProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProviderTests.cs index 2f0da6bae7..1c2bb257a9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/SkipAnalyzersGlobalPropertiesProviderTests.cs @@ -2,38 +2,37 @@ using Microsoft.VisualStudio.ProjectSystem.Managed.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Build +namespace Microsoft.VisualStudio.ProjectSystem.Build; + +public sealed class SkipAnalyzersGlobalPropertiesProviderTests { - public sealed class SkipAnalyzersGlobalPropertiesProviderTests + [Theory, CombinatorialData] + public async Task TestSkipAnalyzersGlobalPropertiesProvider( + bool implicitBuild, + bool skipAnalyzersSettingTurnedOn) { - [Theory, CombinatorialData] - public async Task TestSkipAnalyzersGlobalPropertiesProvider( - bool implicitBuild, - bool skipAnalyzersSettingTurnedOn) - { - UnconfiguredProject project = UnconfiguredProjectFactory.Create( - unconfiguredProjectServices: UnconfiguredProjectServicesFactory.Create( - projectService: IProjectServiceFactory.Create())); - IImplicitlyTriggeredBuildState buildState = IImplicityTriggeredBuildStateFactory.Create(implicitBuild); - IProjectSystemOptions options = IProjectSystemOptionsFactory.ImplementGetSkipAnalyzersForImplicitlyTriggeredBuildAsync(ct => skipAnalyzersSettingTurnedOn); + UnconfiguredProject project = UnconfiguredProjectFactory.Create( + unconfiguredProjectServices: UnconfiguredProjectServicesFactory.Create( + projectService: IProjectServiceFactory.Create())); + IImplicitlyTriggeredBuildState buildState = IImplicityTriggeredBuildStateFactory.Create(implicitBuild); + IProjectSystemOptions options = IProjectSystemOptionsFactory.ImplementGetSkipAnalyzersForImplicitlyTriggeredBuildAsync(ct => skipAnalyzersSettingTurnedOn); - SkipAnalyzersGlobalPropertiesProvider provider = new SkipAnalyzersGlobalPropertiesProvider( - project, - buildState, - options); + SkipAnalyzersGlobalPropertiesProvider provider = new SkipAnalyzersGlobalPropertiesProvider( + project, + buildState, + options); - IImmutableDictionary properties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); + IImmutableDictionary properties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); - if (implicitBuild && skipAnalyzersSettingTurnedOn) - { - Assert.Equal(expected: 2, actual: properties.Count); - Assert.Equal(expected: "true", actual: properties["IsImplicitlyTriggeredBuild"]); - Assert.Equal(expected: "ImplicitBuild", actual: properties["FastUpToDateCheckIgnoresKinds"]); - } - else - { - Assert.Empty(properties); - } + if (implicitBuild && skipAnalyzersSettingTurnedOn) + { + Assert.Equal(expected: 2, actual: properties.Count); + Assert.Equal(expected: "true", actual: properties["IsImplicitlyTriggeredBuild"]); + Assert.Equal(expected: "ImplicitBuild", actual: properties["FastUpToDateCheckIgnoresKinds"]); + } + else + { + Assert.Empty(properties); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProviderTests.cs index 2b7505e0a4..83c14ac476 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/StartupProjectSingleTargetGlobalBuildPropertyProviderTests.cs @@ -4,67 +4,66 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.ProjectSystem.Managed.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public sealed class StartupProjectSingleTargetGlobalBuildPropertyProviderTests { - public sealed class StartupProjectSingleTargetGlobalBuildPropertyProviderTests + [Theory] + // projectPath crossTargeting implicitlyTriggeredBuild startupProjects globalOptionEnabled expectTargetFrameworkSet + [InlineData(@"C:\alpha.csproj", true, true, new[] { @"C:\alpha.csproj" }, true, true)] + [InlineData(@"C:\alpha.csproj", false, true, new[] { @"C:\alpha.csproj" }, true, false)] + [InlineData(@"C:\alpha.csproj", true, false, new[] { @"C:\alpha.csproj" }, true, false)] + [InlineData(@"C:\beta.csproj", true, true, new[] { @"C:\alpha.csproj" }, true, false)] + [InlineData(@"C:\alpha.csproj", true, true, new[] { @"C:\alpha.csproj", @"C:\beta.csproj" }, true, false)] + [InlineData(@"C:\alpha.csproj", true, true, new[] { @"C:\alpha.csproj" }, false, false)] + public async Task VerifyExpectedBehaviors(string projectPath, bool crossTargeting, bool implicitlyTriggeredBuild, string[] startupProjects, bool globalOptionEnabled, bool expectTargetFrameworkSet) { - [Theory] - // projectPath crossTargeting implicitlyTriggeredBuild startupProjects globalOptionEnabled expectTargetFrameworkSet - [InlineData(@"C:\alpha.csproj", true, true, new[] { @"C:\alpha.csproj" }, true, true)] - [InlineData(@"C:\alpha.csproj", false, true, new[] { @"C:\alpha.csproj" }, true, false)] - [InlineData(@"C:\alpha.csproj", true, false, new[] { @"C:\alpha.csproj" }, true, false)] - [InlineData(@"C:\beta.csproj", true, true, new[] { @"C:\alpha.csproj" }, true, false)] - [InlineData(@"C:\alpha.csproj", true, true, new[] { @"C:\alpha.csproj", @"C:\beta.csproj" }, true, false)] - [InlineData(@"C:\alpha.csproj", true, true, new[] { @"C:\alpha.csproj" }, false, false)] - public async Task VerifyExpectedBehaviors(string projectPath, bool crossTargeting, bool implicitlyTriggeredBuild, string[] startupProjects, bool globalOptionEnabled, bool expectTargetFrameworkSet) - { - var projectService = IProjectServiceFactory.Create(); + var projectService = IProjectServiceFactory.Create(); - var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: projectPath); + var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: projectPath); - ConfiguredProject? configuredProject; - if (crossTargeting) - { - var dimensions = Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU") - .Add("TargetFramework", "netcoreapp1.0"); - var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU|netcoreapp1.0", dimensions); - configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration, unconfiguredProject: unconfiguredProject); - } - else - { - var dimensions = Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU"); - var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU", dimensions); - configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration, unconfiguredProject: unconfiguredProject); - } + ConfiguredProject? configuredProject; + if (crossTargeting) + { + var dimensions = Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU") + .Add("TargetFramework", "netcoreapp1.0"); + var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU|netcoreapp1.0", dimensions); + configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration, unconfiguredProject: unconfiguredProject); + } + else + { + var dimensions = Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU"); + var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU", dimensions); + configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration, unconfiguredProject: unconfiguredProject); + } - var activeDebugFrameworkServices = IActiveDebugFrameworkServicesFactory.ImplementGetActiveDebuggingFrameworkPropertyAsync("myFramework1.0"); + var activeDebugFrameworkServices = IActiveDebugFrameworkServicesFactory.ImplementGetActiveDebuggingFrameworkPropertyAsync("myFramework1.0"); - var implicitlyTriggeredBuildState = IImplicityTriggeredBuildStateFactory.Create(implicitlyTriggeredBuild, startupProjects); + var implicitlyTriggeredBuildState = IImplicityTriggeredBuildStateFactory.Create(implicitlyTriggeredBuild, startupProjects); - var projectSystemOptions = IProjectSystemOptionsFactory.ImplementGetPreferSingleTargetBuildsForStartupProjectsAsync(ct => globalOptionEnabled); + var projectSystemOptions = IProjectSystemOptionsFactory.ImplementGetPreferSingleTargetBuildsForStartupProjectsAsync(ct => globalOptionEnabled); - var provider = new StartupProjectSingleTargetGlobalBuildPropertyProvider( - projectService, - configuredProject, - activeDebugFrameworkServices, - implicitlyTriggeredBuildState, - projectSystemOptions); + var provider = new StartupProjectSingleTargetGlobalBuildPropertyProvider( + projectService, + configuredProject, + activeDebugFrameworkServices, + implicitlyTriggeredBuildState, + projectSystemOptions); - var globalProperties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); + var globalProperties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); - if (expectTargetFrameworkSet) - { - Assert.Equal(expected: 1, actual: globalProperties.Count); - Assert.Equal(expected: "myFramework1.0", actual: globalProperties[ConfigurationGeneral.TargetFrameworkProperty]); - } - else - { - Assert.Empty(globalProperties); - } + if (expectTargetFrameworkSet) + { + Assert.Equal(expected: 1, actual: globalProperties.Count); + Assert.Equal(expected: "myFramework1.0", actual: globalProperties[ConfigurationGeneral.TargetFrameworkProperty]); + } + else + { + Assert.Empty(globalProperties); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProviderTests.cs index 33f6a17515..b502e7ec92 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Build/TargetFrameworkGlobalBuildPropertyProviderTests.cs @@ -2,41 +2,40 @@ using Microsoft.VisualStudio.ProjectSystem.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class TargetFrameworkGlobalBuildPropertyProviderTests { - public class TargetFrameworkGlobalBuildPropertyProviderTests + [Fact] + public async Task VerifyTargetFrameworkOverrideForCrossTargetingBuild() { - [Fact] - public async Task VerifyTargetFrameworkOverrideForCrossTargetingBuild() - { - var dimensions = Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU") - .Add("TargetFramework", "netcoreapp1.0"); - var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU|netcoreapp1.0", dimensions); - var configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration); - var projectService = IProjectServiceFactory.Create(); - var provider = new TargetFrameworkGlobalBuildPropertyProvider(projectService, configuredProject); + var dimensions = Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU") + .Add("TargetFramework", "netcoreapp1.0"); + var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU|netcoreapp1.0", dimensions); + var configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration); + var projectService = IProjectServiceFactory.Create(); + var provider = new TargetFrameworkGlobalBuildPropertyProvider(projectService, configuredProject); - var properties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); - Assert.Single(properties); - Assert.Equal("TargetFramework", properties.Keys.First()); - Assert.Equal(string.Empty, properties.Values.First()); - } + var properties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); + Assert.Single(properties); + Assert.Equal("TargetFramework", properties.Keys.First()); + Assert.Equal(string.Empty, properties.Values.First()); + } - [Fact] - public async Task VerifyNoTargetFrameworkOverrideForRegularBuild() - { - var dimensions = Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU"); - var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU", dimensions); - var configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration); - var projectService = IProjectServiceFactory.Create(); - var provider = new TargetFrameworkGlobalBuildPropertyProvider(projectService, configuredProject); + [Fact] + public async Task VerifyNoTargetFrameworkOverrideForRegularBuild() + { + var dimensions = Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU"); + var projectConfiguration = new StandardProjectConfiguration("Debug|AnyCPU", dimensions); + var configuredProject = ConfiguredProjectFactory.Create(projectConfiguration: projectConfiguration); + var projectService = IProjectServiceFactory.Create(); + var provider = new TargetFrameworkGlobalBuildPropertyProvider(projectService, configuredProject); - var properties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); - Assert.Equal(0, properties.Count); - } + var properties = await provider.GetGlobalPropertiesAsync(CancellationToken.None); + Assert.Equal(0, properties.Count); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProviderTests.cs index 9aa91370db..0bf552eb4a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ConfigurationProjectConfigurationDimensionProviderTests.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +public sealed class ConfigurationProjectConfigurationDimensionProviderTests : ProjectConfigurationDimensionProviderTestBase { - public sealed class ConfigurationProjectConfigurationDimensionProviderTests : ProjectConfigurationDimensionProviderTestBase - { - protected override string PropertyName => "Configurations"; - protected override string DimensionName => "Configuration"; - protected override string? DimensionDefaultValue => "Debug"; + protected override string PropertyName => "Configurations"; + protected override string DimensionName => "Configuration"; + protected override string? DimensionDefaultValue => "Debug"; - private protected override BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml) - { - var projectAccessor = IProjectAccessorFactory.Create(projectXml); + private protected override BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml) + { + var projectAccessor = IProjectAccessorFactory.Create(projectXml); - return CreateInstance(projectAccessor); - } + return CreateInstance(projectAccessor); + } - private protected override BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor) - { - return new ConfigurationProjectConfigurationDimensionProvider(projectAccessor); - } + private protected override BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor) + { + return new ConfigurationProjectConfigurationDimensionProvider(projectAccessor); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ImplicitlyActiveDimensionProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ImplicitlyActiveDimensionProviderTests.cs index ef08e5c0d9..31539bb9dd 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ImplicitlyActiveDimensionProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ImplicitlyActiveDimensionProviderTests.cs @@ -1,67 +1,66 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +public class ImplicitlyActiveDimensionProviderTests { - public class ImplicitlyActiveDimensionProviderTests + [Fact] + public void GetImplicitlyActiveDimensions_NullAsDimensionNames_ThrowsArgumentNull() { - [Fact] - public void GetImplicitlyActiveDimensions_NullAsDimensionNames_ThrowsArgumentNull() - { - var provider = CreateInstance(); + var provider = CreateInstance(); - Assert.Throws("dimensionNames", () => - { - provider.GetImplicitlyActiveDimensions(null!); - }); - } - - [Fact] - public void GetImplicitlyActiveDimensions_EmptyAsDimensionNames_ReturnsEmpty() + Assert.Throws("dimensionNames", () => { - var provider = CreateInstance(); + provider.GetImplicitlyActiveDimensions(null!); + }); + } - var result = provider.GetImplicitlyActiveDimensions(Enumerable.Empty()); + [Fact] + public void GetImplicitlyActiveDimensions_EmptyAsDimensionNames_ReturnsEmpty() + { + var provider = CreateInstance(); - Assert.Empty(result); - } + var result = provider.GetImplicitlyActiveDimensions(Enumerable.Empty()); - [Fact] - public void GetImplicitlyActiveDimensions_WhenNoProviders_ReturnsEmpty() - { - var provider = CreateInstance(); + Assert.Empty(result); + } - var result = provider.GetImplicitlyActiveDimensions(new string[] { "Configuration", "Platform" }); + [Fact] + public void GetImplicitlyActiveDimensions_WhenNoProviders_ReturnsEmpty() + { + var provider = CreateInstance(); - Assert.Empty(result); - } + var result = provider.GetImplicitlyActiveDimensions(new string[] { "Configuration", "Platform" }); - [Theory] // Input All Dimensions Variant dimensions Expected - [InlineData("Configuration", "Configuration;Platform", new[] { false, false }, "")] - [InlineData("Configuration", "Configuration;Platform", new[] { true, false }, "Configuration")] - [InlineData("Configuration;Platform", "Configuration;Platform;TargetFramework", new[] { false, false, true }, "")] - [InlineData("Configuration;Platform;TargetFramework", "Configuration;Platform;TargetFramework", new[] { false, false, true }, "TargetFramework")] - [InlineData("Configuration;Platform;TargetFramework", "Configuration;Platform;TargetFramework", new[] { true, false, true }, "Configuration;TargetFramework")] - [InlineData("Configuration;Platform;TargetFramework", "Configuration;Platform;TargetFramework", new[] { true, true, true }, "Configuration;Platform;TargetFramework")] - [InlineData("Configuration", "Configuration;Platform;TargetFramework", new[] { true, true, true }, "Configuration")] - [InlineData("Configuration;Platform", "Configuration;Platform;TargetFramework", new[] { true, true, true }, "Configuration;Platform")] - [InlineData("Configuration;Platform", "Configuration;Platform;TargetFramework;TargetFramework", new[] { true, true, true, true }, "Configuration;Platform")] - public void GetImplicitlyActiveDimensions_ReturnsImplicitlyActiveDimensions(string dimensionNames, string allDimensionsNames, bool[] isVariantDimension, string expected) - { - var metadata = IConfigurationDimensionDescriptionMetadataViewFactory.Create(allDimensionsNames.SplitReturningEmptyIfEmpty(';'), isVariantDimension); + Assert.Empty(result); + } - var provider = CreateInstance(); - provider.DimensionProviders.Add(new Lazy(metadata)); + [Theory] // Input All Dimensions Variant dimensions Expected + [InlineData("Configuration", "Configuration;Platform", new[] { false, false }, "")] + [InlineData("Configuration", "Configuration;Platform", new[] { true, false }, "Configuration")] + [InlineData("Configuration;Platform", "Configuration;Platform;TargetFramework", new[] { false, false, true }, "")] + [InlineData("Configuration;Platform;TargetFramework", "Configuration;Platform;TargetFramework", new[] { false, false, true }, "TargetFramework")] + [InlineData("Configuration;Platform;TargetFramework", "Configuration;Platform;TargetFramework", new[] { true, false, true }, "Configuration;TargetFramework")] + [InlineData("Configuration;Platform;TargetFramework", "Configuration;Platform;TargetFramework", new[] { true, true, true }, "Configuration;Platform;TargetFramework")] + [InlineData("Configuration", "Configuration;Platform;TargetFramework", new[] { true, true, true }, "Configuration")] + [InlineData("Configuration;Platform", "Configuration;Platform;TargetFramework", new[] { true, true, true }, "Configuration;Platform")] + [InlineData("Configuration;Platform", "Configuration;Platform;TargetFramework;TargetFramework", new[] { true, true, true, true }, "Configuration;Platform")] + public void GetImplicitlyActiveDimensions_ReturnsImplicitlyActiveDimensions(string dimensionNames, string allDimensionsNames, bool[] isVariantDimension, string expected) + { + var metadata = IConfigurationDimensionDescriptionMetadataViewFactory.Create(allDimensionsNames.SplitReturningEmptyIfEmpty(';'), isVariantDimension); - var result = provider.GetImplicitlyActiveDimensions(dimensionNames.SplitReturningEmptyIfEmpty(';')); + var provider = CreateInstance(); + provider.DimensionProviders.Add(new Lazy(metadata)); - Assert.Equal(expected.SplitReturningEmptyIfEmpty(';'), result); - } + var result = provider.GetImplicitlyActiveDimensions(dimensionNames.SplitReturningEmptyIfEmpty(';')); - private static ImplicitlyActiveDimensionProvider CreateInstance() - { - var project = UnconfiguredProjectFactory.Create(); + Assert.Equal(expected.SplitReturningEmptyIfEmpty(';'), result); + } + + private static ImplicitlyActiveDimensionProvider CreateInstance() + { + var project = UnconfiguredProjectFactory.Create(); - return new ImplicitlyActiveDimensionProvider(project); - } + return new ImplicitlyActiveDimensionProvider(project); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProviderTests.cs index e600633042..e86242795c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/PlatformProjectConfigurationDimensionProviderTests.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +public sealed class PlatformProjectConfigurationDimensionProviderTests : ProjectConfigurationDimensionProviderTestBase { - public sealed class PlatformProjectConfigurationDimensionProviderTests : ProjectConfigurationDimensionProviderTestBase - { - protected override string PropertyName => "Platforms"; - protected override string DimensionName => "Platform"; - protected override string? DimensionDefaultValue => "AnyCPU"; + protected override string PropertyName => "Platforms"; + protected override string DimensionName => "Platform"; + protected override string? DimensionDefaultValue => "AnyCPU"; - private protected override BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml) - { - var projectAccessor = IProjectAccessorFactory.Create(projectXml); + private protected override BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml) + { + var projectAccessor = IProjectAccessorFactory.Create(projectXml); - return CreateInstance(projectAccessor); - } + return CreateInstance(projectAccessor); + } - private protected override BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor) - { - return new PlatformProjectConfigurationDimensionProvider(projectAccessor); - } + private protected override BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor) + { + return new PlatformProjectConfigurationDimensionProvider(projectAccessor); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ProjectConfigurationDimensionProviderTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ProjectConfigurationDimensionProviderTestBase.cs index 354ce434d2..9f08a96cd0 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ProjectConfigurationDimensionProviderTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/ProjectConfigurationDimensionProviderTestBase.cs @@ -3,580 +3,579 @@ using Microsoft.Build.Construction; using Microsoft.VisualStudio.Build; -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +public abstract class ProjectConfigurationDimensionProviderTestBase { - public abstract class ProjectConfigurationDimensionProviderTestBase - { - protected abstract string PropertyName { get; } - protected abstract string DimensionName { get; } - protected abstract string? DimensionDefaultValue { get; } + protected abstract string PropertyName { get; } + protected abstract string DimensionName { get; } + protected abstract string? DimensionDefaultValue { get; } - private protected abstract BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml); - private protected abstract BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor); + private protected abstract BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml); + private protected abstract BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor); - [Fact] - public void PropertiesHaveExpectedValues() - { - var provider = CreateInstance(""); - - Assert.Equal(PropertyName, provider.PropertyName); - Assert.Equal(DimensionName, provider.DimensionName); - Assert.Equal(DimensionDefaultValue, provider.DimensionDefaultValue); - } + [Fact] + public void PropertiesHaveExpectedValues() + { + var provider = CreateInstance(""); - [Fact] - public async Task GetDefaultValuesForDimensionsAsync() - { - string projectXml = - """ - - - A;B;C - X - - - """; - - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName).Replace("DIM", DimensionName)); - - var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); - var values = await provider.GetDefaultValuesForDimensionsAsync(project); - - var (key, value) = Assert.Single(values); - Assert.Equal(DimensionName, key); - Assert.Equal("A", value); - } + Assert.Equal(PropertyName, provider.PropertyName); + Assert.Equal(DimensionName, provider.DimensionName); + Assert.Equal(DimensionDefaultValue, provider.DimensionDefaultValue); + } - [Theory] - [InlineData("")] - [InlineData( + [Fact] + public async Task GetDefaultValuesForDimensionsAsync() + { + string projectXml = """ + A;B;C X - """)] - public async Task GetDefaultValuesForDimensionsAsync_ReturnsEmptyIfUndefined(string projectXml) - { - var provider = CreateInstance(projectXml.Replace("DIM", DimensionName)); + """; - var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); - var values = await provider.GetDefaultValuesForDimensionsAsync(project); + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName).Replace("DIM", DimensionName)); - Assert.Empty(values); - } + var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); + var values = await provider.GetDefaultValuesForDimensionsAsync(project); - [Fact] - public async Task GetProjectConfigurationDimensionsAsync() - { - string projectXml = - """ - - - A;B;C - X - - - """; - - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName).Replace("DIM", DimensionName)); - - var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); - var values = await provider.GetProjectConfigurationDimensionsAsync(project); - - var (key, value) = Assert.Single(values); - Assert.Equal(DimensionName, key); - string[] dimensionValues = value.ToArray(); - AssertEx.CollectionLength(dimensionValues, 3); - Assert.Equal("A", dimensionValues[0]); - Assert.Equal("B", dimensionValues[1]); - Assert.Equal("C", dimensionValues[2]); - } + var (key, value) = Assert.Single(values); + Assert.Equal(DimensionName, key); + Assert.Equal("A", value); + } + + [Theory] + [InlineData("")] + [InlineData( + """ + + + X + + + """)] + public async Task GetDefaultValuesForDimensionsAsync_ReturnsEmptyIfUndefined(string projectXml) + { + var provider = CreateInstance(projectXml.Replace("DIM", DimensionName)); + + var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); + var values = await provider.GetDefaultValuesForDimensionsAsync(project); - [Theory] - [InlineData("")] - [InlineData( + Assert.Empty(values); + } + + [Fact] + public async Task GetProjectConfigurationDimensionsAsync() + { + string projectXml = """ + A;B;C X - """)] - public async Task GetProjectConfigurationDimensionsAsync_ReturnsEmptyIfUndefined(string projectXml) - { - var provider = CreateInstance(projectXml.Replace("DIM", DimensionName)); - - var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); - var values = await provider.GetProjectConfigurationDimensionsAsync(project); - - Assert.Empty(values); - } - - [Fact] - public async Task OnDimensionValueChanged_Add() - { - string projectXml = - """ - - - A;B;C - - - """; - - var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); - var projectAccessor = IProjectAccessorFactory.Create(rootElement); - var configuredProject = ConfiguredProjectFactory.Create(); - var provider = CreateInstance(projectAccessor); - var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); - - // On ChangeEventStage.After nothing should be changed - var args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Add, - ChangeEventStage.After, - DimensionName, - "Added"); - await provider.OnDimensionValueChangedAsync(args); - var property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;B;C", property.Value); - - // On ChangeEventStage.Before the property should be added - args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Add, - ChangeEventStage.Before, - DimensionName, - "Added"); - await provider.OnDimensionValueChangedAsync(args); - property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;B;C;Added", property.Value); - } + """; - [Fact] - public async Task OnDimensionValueChanged_Remove() - { - string projectXml = - """ - - - A;B;C - - - """; - - var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); - var projectAccessor = IProjectAccessorFactory.Create(rootElement); - var configuredProject = ConfiguredProjectFactory.Create(); - var provider = CreateInstance(projectAccessor); - var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); - - // On ChangeEventStage.After nothing should be changed - var args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Delete, - ChangeEventStage.After, - DimensionName, - "B"); - await provider.OnDimensionValueChangedAsync(args); - var property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;B;C", property.Value); - - // On ChangeEventStage.Before the property should be removed - args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Delete, - ChangeEventStage.Before, - DimensionName, - "B"); - await provider.OnDimensionValueChangedAsync(args); - property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;C", property.Value); - } + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName).Replace("DIM", DimensionName)); - [Fact] - public async Task OnDimensionValueChanged_Remove_UnknownValue() - { - string projectXml = - """ - - - A;B;C - - - """; - - var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); - var projectAccessor = IProjectAccessorFactory.Create(rootElement); - var configuredProject = ConfiguredProjectFactory.Create(); - var provider = CreateInstance(projectAccessor); - var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); - - var args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Delete, - ChangeEventStage.Before, - DimensionName, - "Unknown"); - await Assert.ThrowsAsync(() => provider.OnDimensionValueChangedAsync(args)); - var property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;B;C", property.Value); - } + var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); + var values = await provider.GetProjectConfigurationDimensionsAsync(project); - [Fact] - public async Task OnDimensionValueChanged_Rename() - { - string projectXml = - """ - - - A;B;C - - - """; - - var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); - var projectAccessor = IProjectAccessorFactory.Create(rootElement); - var configuredProject = ConfiguredProjectFactory.Create(); - var provider = CreateInstance(projectAccessor); - var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); - - // On ChangeEventStage.Before nothing should be changed - var args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Rename, - ChangeEventStage.Before, - DimensionName, - "Renamed", - "B"); - await provider.OnDimensionValueChangedAsync(args); - var property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;B;C", property.Value); - - // On ChangeEventStage.Before the property should be renamed - args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Rename, - ChangeEventStage.After, - DimensionName, - "Renamed", - "B"); - await provider.OnDimensionValueChangedAsync(args); - property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;Renamed;C", property.Value); - } - - [Fact] - public async Task OnDimensionValueChanged_Rename_UnknownValue() - { - string projectXml = - """ - - - A;B;C - - - """; - - var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); - var projectAccessor = IProjectAccessorFactory.Create(rootElement); - var configuredProject = ConfiguredProjectFactory.Create(); - var provider = CreateInstance(projectAccessor); - var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); - - var args = new ProjectConfigurationDimensionValueChangedEventArgs( - project, - ConfigurationDimensionChange.Rename, - ChangeEventStage.After, - DimensionName, - "Renamed", - "Unknown"); - await Assert.ThrowsAsync(() => provider.OnDimensionValueChangedAsync(args)); - var property = BuildUtilities.GetProperty(rootElement, PropertyName); - Assert.NotNull(property); - Assert.Equal("A;B;C", property.Value); - } - - [Theory] - [InlineData("net45", "net45")] - [InlineData(" net45 ", "net45")] - [InlineData("net46", "net46")] - [InlineData("net45;", "net45")] - [InlineData("net45;net46", "net45")] - [InlineData(";net45;net46", "net45")] - [InlineData("$(Foo);net45;net46", "net45")] - [InlineData("$(Foo); net45 ;net46", "net45")] - [InlineData("net45_$(Foo); net45 ;net46", "net45")] - public async Task GetBestGuessDefaultValuesForDimensionsAsync_ReturnsFirstParseableValue(string propertyValue, string expected) - { - string projectXml = - $""" - - - {propertyValue} - - - """; - - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); - - var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); - - Assert.Single(result); - Assert.Equal(provider.DimensionName, result.First().Key); - Assert.Equal(expected, result.First().Value); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData(";")] - [InlineData(" ;")] - [InlineData(" ; ")] - [InlineData(";;;")] - [InlineData("$(Property)")] - [InlineData("Foo_$(Property)")] - [InlineData("Foo_$(Property);")] - [InlineData(";Foo_$(Property);")] - public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPropertyIsEmpty_ReturnsDefaultOrEmpty(string propertyValue) - { - string projectXml = - $""" - - - {propertyValue} - - - """; - - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); - - var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); - - AssertDefaultOrEmpty(provider, result); - } - - [Fact] - public async Task GetBestGuessDefaultValuesForDimensionsAsync_ReturnsFirstValueFromLastElement() - { - string projectXml = - """ - - - first - last - - - """; - - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); - - var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); - - Assert.Single(result); - Assert.Equal(provider.DimensionName, result.First().Key); - Assert.Equal("last", result.First().Value); - } - - [Fact] - public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPropertyIsMissing_ReturnsDefaultOrEmpty() - { - string projectXml = - """ - - - - - """; + var (key, value) = Assert.Single(values); + Assert.Equal(DimensionName, key); + string[] dimensionValues = value.ToArray(); + AssertEx.CollectionLength(dimensionValues, 3); + Assert.Equal("A", dimensionValues[0]); + Assert.Equal("B", dimensionValues[1]); + Assert.Equal("C", dimensionValues[2]); + } - var provider = CreateInstance(projectXml); + [Theory] + [InlineData("")] + [InlineData( + """ + + + X + + + """)] + public async Task GetProjectConfigurationDimensionsAsync_ReturnsEmptyIfUndefined(string projectXml) + { + var provider = CreateInstance(projectXml.Replace("DIM", DimensionName)); - var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + var project = UnconfiguredProjectFactory.Create(configuredProject: ConfiguredProjectFactory.Create()); + var values = await provider.GetProjectConfigurationDimensionsAsync(project); - AssertDefaultOrEmpty(provider, result); - } + Assert.Empty(values); + } - [Theory] - [InlineData( - """ - - - net45 - - - """)] - [InlineData( + [Fact] + public async Task OnDimensionValueChanged_Add() + { + string projectXml = """ - net45 - net45 + A;B;C - """)] - [InlineData( + """; + + var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); + var projectAccessor = IProjectAccessorFactory.Create(rootElement); + var configuredProject = ConfiguredProjectFactory.Create(); + var provider = CreateInstance(projectAccessor); + var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); + + // On ChangeEventStage.After nothing should be changed + var args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Add, + ChangeEventStage.After, + DimensionName, + "Added"); + await provider.OnDimensionValueChangedAsync(args); + var property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;B;C", property.Value); + + // On ChangeEventStage.Before the property should be added + args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Add, + ChangeEventStage.Before, + DimensionName, + "Added"); + await provider.OnDimensionValueChangedAsync(args); + property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;B;C;Added", property.Value); + } + + [Fact] + public async Task OnDimensionValueChanged_Remove() + { + string projectXml = """ - net45 - net45 + A;B;C - """)] - [InlineData( + """; + + var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); + var projectAccessor = IProjectAccessorFactory.Create(rootElement); + var configuredProject = ConfiguredProjectFactory.Create(); + var provider = CreateInstance(projectAccessor); + var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); + + // On ChangeEventStage.After nothing should be changed + var args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Delete, + ChangeEventStage.After, + DimensionName, + "B"); + await provider.OnDimensionValueChangedAsync(args); + var property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;B;C", property.Value); + + // On ChangeEventStage.Before the property should be removed + args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Delete, + ChangeEventStage.Before, + DimensionName, + "B"); + await provider.OnDimensionValueChangedAsync(args); + property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;C", property.Value); + } + + [Fact] + public async Task OnDimensionValueChanged_Remove_UnknownValue() + { + string projectXml = """ - net45 + A;B;C - """)] - [InlineData( + """; + + var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); + var projectAccessor = IProjectAccessorFactory.Create(rootElement); + var configuredProject = ConfiguredProjectFactory.Create(); + var provider = CreateInstance(projectAccessor); + var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); + + var args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Delete, + ChangeEventStage.Before, + DimensionName, + "Unknown"); + await Assert.ThrowsAsync(() => provider.OnDimensionValueChangedAsync(args)); + var property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;B;C", property.Value); + } + + [Fact] + public async Task OnDimensionValueChanged_Rename() + { + string projectXml = """ - net45 + A;B;C - """)] - [InlineData( + """; + + var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); + var projectAccessor = IProjectAccessorFactory.Create(rootElement); + var configuredProject = ConfiguredProjectFactory.Create(); + var provider = CreateInstance(projectAccessor); + var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); + + // On ChangeEventStage.Before nothing should be changed + var args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Rename, + ChangeEventStage.Before, + DimensionName, + "Renamed", + "B"); + await provider.OnDimensionValueChangedAsync(args); + var property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;B;C", property.Value); + + // On ChangeEventStage.Before the property should be renamed + args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Rename, + ChangeEventStage.After, + DimensionName, + "Renamed", + "B"); + await provider.OnDimensionValueChangedAsync(args); + property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;Renamed;C", property.Value); + } + + [Fact] + public async Task OnDimensionValueChanged_Rename_UnknownValue() + { + string projectXml = """ - net45 + A;B;C - """)] - [InlineData( - """ + """; + + var rootElement = ProjectRootElementFactory.Create(projectXml.Replace("PROP", PropertyName)); + var projectAccessor = IProjectAccessorFactory.Create(rootElement); + var configuredProject = ConfiguredProjectFactory.Create(); + var provider = CreateInstance(projectAccessor); + var project = UnconfiguredProjectFactory.Create(configuredProject: configuredProject); + + var args = new ProjectConfigurationDimensionValueChangedEventArgs( + project, + ConfigurationDimensionChange.Rename, + ChangeEventStage.After, + DimensionName, + "Renamed", + "Unknown"); + await Assert.ThrowsAsync(() => provider.OnDimensionValueChangedAsync(args)); + var property = BuildUtilities.GetProperty(rootElement, PropertyName); + Assert.NotNull(property); + Assert.Equal("A;B;C", property.Value); + } + + [Theory] + [InlineData("net45", "net45")] + [InlineData(" net45 ", "net45")] + [InlineData("net46", "net46")] + [InlineData("net45;", "net45")] + [InlineData("net45;net46", "net45")] + [InlineData(";net45;net46", "net45")] + [InlineData("$(Foo);net45;net46", "net45")] + [InlineData("$(Foo); net45 ;net46", "net45")] + [InlineData("net45_$(Foo); net45 ;net46", "net45")] + public async Task GetBestGuessDefaultValuesForDimensionsAsync_ReturnsFirstParseableValue(string propertyValue, string expected) + { + string projectXml = + $""" - net45 - net45 + {propertyValue} - """)] - public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPropertyHasUnrecognizedCondition_ReturnsDefaultOrEmpty(string projectXml) - { - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); + """; - var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); - AssertDefaultOrEmpty(provider, result); - } + var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); - [Theory] - [InlineData( - """ - - - expected - other - - - """)] - [InlineData( - """ - - - expected - other - - - """)] - [InlineData( - """ - - - expected - other - - - """)] - [InlineData( - """ - - - expected - - - """)] - [InlineData( - """ - - - expected - - - """)] - [InlineData( - """ - - - other - expected - - - """)] - [InlineData( - """ + Assert.Single(result); + Assert.Equal(provider.DimensionName, result.First().Key); + Assert.Equal(expected, result.First().Value); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData(";")] + [InlineData(" ;")] + [InlineData(" ; ")] + [InlineData(";;;")] + [InlineData("$(Property)")] + [InlineData("Foo_$(Property)")] + [InlineData("Foo_$(Property);")] + [InlineData(";Foo_$(Property);")] + public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPropertyIsEmpty_ReturnsDefaultOrEmpty(string propertyValue) + { + string projectXml = + $""" - other - expected + {propertyValue} - """)] - [InlineData( + """; + + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); + + var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + + AssertDefaultOrEmpty(provider, result); + } + + [Fact] + public async Task GetBestGuessDefaultValuesForDimensionsAsync_ReturnsFirstValueFromLastElement() + { + string projectXml = """ - other - expected + first + last - """)] - [InlineData( + """; + + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); + + var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + + Assert.Single(result); + Assert.Equal(provider.DimensionName, result.First().Key); + Assert.Equal("last", result.First().Value); + } + + [Fact] + public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPropertyIsMissing_ReturnsDefaultOrEmpty() + { + string projectXml = """ - expected - other - """)] - public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPlatformsHasRecognizedCondition_ReturnsValue2222222222222222(string projectXml) - { - var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); + """; + + var provider = CreateInstance(projectXml); + + var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + + AssertDefaultOrEmpty(provider, result); + } - var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + [Theory] + [InlineData( + """ + + + net45 + + + """)] + [InlineData( + """ + + + net45 + net45 + + + """)] + [InlineData( + """ + + + net45 + net45 + + + """)] + [InlineData( + """ + + + net45 + + + """)] + [InlineData( + """ + + + net45 + + + """)] + [InlineData( + """ + + + net45 + + + """)] + [InlineData( + """ + + + net45 + net45 + + + """)] + public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPropertyHasUnrecognizedCondition_ReturnsDefaultOrEmpty(string projectXml) + { + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); + + var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + + AssertDefaultOrEmpty(provider, result); + } + [Theory] + [InlineData( + """ + + + expected + other + + + """)] + [InlineData( + """ + + + expected + other + + + """)] + [InlineData( + """ + + + expected + other + + + """)] + [InlineData( + """ + + + expected + + + """)] + [InlineData( + """ + + + expected + + + """)] + [InlineData( + """ + + + other + expected + + + """)] + [InlineData( + """ + + + other + expected + + + """)] + [InlineData( + """ + + + other + expected + + + """)] + [InlineData( + """ + + + expected + other + + + """)] + public async Task GetBestGuessDefaultValuesForDimensionsAsync_WhenPlatformsHasRecognizedCondition_ReturnsValue2222222222222222(string projectXml) + { + var provider = CreateInstance(projectXml.Replace("PROP", PropertyName)); + + var result = await provider.GetBestGuessDefaultValuesForDimensionsAsync(UnconfiguredProjectFactory.Create()); + + (string key, string value) = Assert.Single(result); + Assert.Equal(provider.DimensionName, key); + Assert.Equal("expected", value); + } + + private static void AssertDefaultOrEmpty(BaseProjectConfigurationDimensionProvider provider, IEnumerable> result) + { + if (provider.DimensionDefaultValue is not null) + { (string key, string value) = Assert.Single(result); Assert.Equal(provider.DimensionName, key); - Assert.Equal("expected", value); + Assert.Equal(provider.DimensionDefaultValue, value); } - - private static void AssertDefaultOrEmpty(BaseProjectConfigurationDimensionProvider provider, IEnumerable> result) + else { - if (provider.DimensionDefaultValue is not null) - { - (string key, string value) = Assert.Single(result); - Assert.Equal(provider.DimensionName, key); - Assert.Equal(provider.DimensionDefaultValue, value); - } - else - { - Assert.Empty(result); - } + Assert.Empty(result); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProviderTests.cs index a014036f0e..9e671abb27 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Configuration/TargetFrameworkProjectConfigurationDimensionProviderTests.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Configuration +namespace Microsoft.VisualStudio.ProjectSystem.Configuration; + +public sealed class TargetFrameworkProjectConfigurationDimensionProviderTests : ProjectConfigurationDimensionProviderTestBase { - public sealed class TargetFrameworkProjectConfigurationDimensionProviderTests : ProjectConfigurationDimensionProviderTestBase - { - protected override string PropertyName => "TargetFrameworks"; - protected override string DimensionName => "TargetFramework"; - protected override string? DimensionDefaultValue => null; + protected override string PropertyName => "TargetFrameworks"; + protected override string DimensionName => "TargetFramework"; + protected override string? DimensionDefaultValue => null; - private protected override BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml) - { - var projectAccessor = IProjectAccessorFactory.Create(projectXml); + private protected override BaseProjectConfigurationDimensionProvider CreateInstance(string projectXml) + { + var projectAccessor = IProjectAccessorFactory.Create(projectXml); - return CreateInstance(projectAccessor); - } + return CreateInstance(projectAccessor); + } - private protected override BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor) - { - return new TargetFrameworkProjectConfigurationDimensionProvider(projectAccessor); - } + private protected override BaseProjectConfigurationDimensionProvider CreateInstance(IProjectAccessor projectAccessor) + { + return new TargetFrameworkProjectConfigurationDimensionProvider(projectAccessor); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/ActiveDebugFrameworkServicesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/ActiveDebugFrameworkServicesTests.cs index 8023dfae88..c3ec9b9d59 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/ActiveDebugFrameworkServicesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/ActiveDebugFrameworkServicesTests.cs @@ -2,102 +2,101 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.Input.Commands; + +public class ActiveDebugFrameworkServicesTests { - public class ActiveDebugFrameworkServicesTests + [Theory] + [InlineData("netcoreapp1.0;net462", new string[] { "netcoreapp1.0", "net462" })] + [InlineData("net461;netcoreapp1.0;net45;net462", new string[] { "net461", "netcoreapp1.0", "net45", "net462" })] + public async Task GetProjectFrameworksAsync_ReturnsFrameworksInCorrectOrder(string frameworks, string[] expectedOrder) { - [Theory] - [InlineData("netcoreapp1.0;net462", new string[] { "netcoreapp1.0", "net462" })] - [InlineData("net461;netcoreapp1.0;net45;net462", new string[] { "net461", "netcoreapp1.0", "net45", "net462" })] - public async Task GetProjectFrameworksAsync_ReturnsFrameworksInCorrectOrder(string frameworks, string[] expectedOrder) - { - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworksProperty, frameworks); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworksProperty, frameworks); - var projectProperties = ProjectPropertiesFactory.Create(project, data); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); - var debugFrameworkSvcs = new ActiveDebugFrameworkServices(null!, commonServices); - var result = await debugFrameworkSvcs.GetProjectFrameworksAsync(); + var debugFrameworkSvcs = new ActiveDebugFrameworkServices(null!, commonServices); + var result = await debugFrameworkSvcs.GetProjectFrameworksAsync(); - Assert.NotNull(result); - Assert.Equal(expectedOrder.Length, result.Count); - for (int i = 0; i < result.Count; i++) - { - Assert.Equal(expectedOrder[i], result[i]); - } + Assert.NotNull(result); + Assert.Equal(expectedOrder.Length, result.Count); + for (int i = 0; i < result.Count; i++) + { + Assert.Equal(expectedOrder[i], result[i]); } + } - [Theory] - [InlineData("netcoreapp1.0")] - [InlineData("net461")] - public async Task GetActiveDebuggingFrameworkPropertyAsync_ReturnsFrameworkValue(string framework) - { - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugFrameworkProperty, framework); + [Theory] + [InlineData("netcoreapp1.0")] + [InlineData("net461")] + public async Task GetActiveDebuggingFrameworkPropertyAsync_ReturnsFrameworkValue(string framework) + { + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugFrameworkProperty, framework); - var projectProperties = ProjectPropertiesFactory.Create(project, data); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); - var debugFrameworkSvcs = new ActiveDebugFrameworkServices(null!, commonServices); - var result = await debugFrameworkSvcs.GetActiveDebuggingFrameworkPropertyAsync(); + var debugFrameworkSvcs = new ActiveDebugFrameworkServices(null!, commonServices); + var result = await debugFrameworkSvcs.GetActiveDebuggingFrameworkPropertyAsync(); - Assert.Equal(framework, result); - } + Assert.Equal(framework, result); + } - [Fact] - public async Task SetActiveDebuggingFrameworkPropertyAsync_SetsValue() - { - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugFrameworkProperty, "FrameworkOne"); + [Fact] + public async Task SetActiveDebuggingFrameworkPropertyAsync_SetsValue() + { + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugFrameworkProperty, "FrameworkOne"); - var projectProperties = ProjectPropertiesFactory.Create(project, data); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); - var debugFrameworkSvcs = new ActiveDebugFrameworkServices(null!, commonServices); - await debugFrameworkSvcs.SetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0"); - // Just verifies it doesn't throw. In other words, the function is trying to set the correct property. The way the property mocks - // are set up there is no easy way to capture the value being set without rewriting how they work. - } + var debugFrameworkSvcs = new ActiveDebugFrameworkServices(null!, commonServices); + await debugFrameworkSvcs.SetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0"); + // Just verifies it doesn't throw. In other words, the function is trying to set the correct property. The way the property mocks + // are set up there is no easy way to capture the value being set without rewriting how they work. + } - [Theory] - [InlineData("netcoreapp1.0", "netcoreapp1.0")] - [InlineData("net461", "net461")] - [InlineData("", "net462")] - [InlineData("someframework", "net462")] - public async Task GetConfiguredProjectForActiveFrameworkAsync_ReturnsCorrectProject(string framework, string selectedConfigFramework) - { - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugFrameworkProperty, framework); - var data2 = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworksProperty, "net462;net461;netcoreapp1.0"); - - var projects = ImmutableStringDictionary.EmptyOrdinal - .Add("net461", ConfiguredProjectFactory.Create(null, new StandardProjectConfiguration("Debug|AnyCPU|net461", Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU") - .Add("TargetFramework", "net461")))) - .Add("netcoreapp1.0", ConfiguredProjectFactory.Create(null, new StandardProjectConfiguration("Debug|AnyCPU|netcoreapp1.0", Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU") - .Add("TargetFramework", "netcoreapp1.0")))) - .Add("net462", ConfiguredProjectFactory.Create(null, new StandardProjectConfiguration("Debug|AnyCPU|net462", Empty.PropertiesMap - .Add("Configuration", "Debug") - .Add("Platform", "AnyCPU") - .Add("TargetFramework", "net462")))); - - var projectProperties = ProjectPropertiesFactory.Create(project, data, data2); - var projectConfigProvider = new IActiveConfiguredProjectsProviderFactory(MockBehavior.Strict) - .ImplementGetActiveConfiguredProjectsMapAsync(projects); - - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); - - var debugFrameworkSvcs = new ActiveDebugFrameworkServices(projectConfigProvider.Object, commonServices); - var activeConfiguredProject = await debugFrameworkSvcs.GetConfiguredProjectForActiveFrameworkAsync(); - Assert.NotNull(activeConfiguredProject); - Assert.Equal(selectedConfigFramework, activeConfiguredProject.ProjectConfiguration.Dimensions.GetValueOrDefault("TargetFramework")); - } + [Theory] + [InlineData("netcoreapp1.0", "netcoreapp1.0")] + [InlineData("net461", "net461")] + [InlineData("", "net462")] + [InlineData("someframework", "net462")] + public async Task GetConfiguredProjectForActiveFrameworkAsync_ReturnsCorrectProject(string framework, string selectedConfigFramework) + { + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugFrameworkProperty, framework); + var data2 = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworksProperty, "net462;net461;netcoreapp1.0"); + + var projects = ImmutableStringDictionary.EmptyOrdinal + .Add("net461", ConfiguredProjectFactory.Create(null, new StandardProjectConfiguration("Debug|AnyCPU|net461", Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU") + .Add("TargetFramework", "net461")))) + .Add("netcoreapp1.0", ConfiguredProjectFactory.Create(null, new StandardProjectConfiguration("Debug|AnyCPU|netcoreapp1.0", Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU") + .Add("TargetFramework", "netcoreapp1.0")))) + .Add("net462", ConfiguredProjectFactory.Create(null, new StandardProjectConfiguration("Debug|AnyCPU|net462", Empty.PropertiesMap + .Add("Configuration", "Debug") + .Add("Platform", "AnyCPU") + .Add("TargetFramework", "net462")))); + + var projectProperties = ProjectPropertiesFactory.Create(project, data, data2); + var projectConfigProvider = new IActiveConfiguredProjectsProviderFactory(MockBehavior.Strict) + .ImplementGetActiveConfiguredProjectsMapAsync(projects); + + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); + + var debugFrameworkSvcs = new ActiveDebugFrameworkServices(projectConfigProvider.Object, commonServices); + var activeConfiguredProject = await debugFrameworkSvcs.GetConfiguredProjectForActiveFrameworkAsync(); + Assert.NotNull(activeConfiguredProject); + Assert.Equal(selectedConfigFramework, activeConfiguredProject.ProjectConfiguration.Dimensions.GetValueOrDefault("TargetFramework")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/DebugTokenReplacerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/DebugTokenReplacerTests.cs index 331cb5d1a4..b016704935 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/DebugTokenReplacerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/DebugTokenReplacerTests.cs @@ -2,104 +2,103 @@ using Microsoft.VisualStudio.ProjectSystem.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public class DebugTokenReplacerTests { - public class DebugTokenReplacerTests + private readonly Dictionary _envVars = new(StringComparer.OrdinalIgnoreCase) { - private readonly Dictionary _envVars = new(StringComparer.OrdinalIgnoreCase) - { - { "%env1%", "envVariable1" }, - { "%env2%", "envVariable2" }, - { "%env3%", "$(msbuildProperty6)" } - }; + { "%env1%", "envVariable1" }, + { "%env2%", "envVariable2" }, + { "%env3%", "$(msbuildProperty6)" } + }; - private readonly Mock _envHelper; + private readonly Mock _envHelper; - public DebugTokenReplacerTests() + public DebugTokenReplacerTests() + { + _envHelper = new Mock(); + _envHelper.Setup(x => x.ExpandEnvironmentVariables(It.IsAny())).Returns((str) => { - _envHelper = new Mock(); - _envHelper.Setup(x => x.ExpandEnvironmentVariables(It.IsAny())).Returns((str) => + foreach ((string key, string value) in _envVars) { - foreach ((string key, string value) in _envVars) - { - str = str.Replace(key, value); - } + str = str.Replace(key, value); + } - return str; - }); - } + return str; + }); + } - [Fact] - public async Task ReplaceTokensInProfileTests() - { - var replacer = CreateInstance(); - - // Tests all the possible replacements. env3 tests that environment vars are resolved before msbuild tokens - var launchProfile = new LaunchProfile( - name: "$(msbuildProperty1)", - commandLineArgs: "%env1%", - commandName: "$(msbuildProperty2)", - executablePath: "$(test this string", // Not a valid token - workingDirectory: "c:\\test\\%env3%", - launchBrowser: false, - launchUrl: "http://localhost:8080/$(unknownproperty)", - environmentVariables: ImmutableArray.Create(("var1", "%env1%"), ("var2", "$(msbuildProperty3)")), - otherSettings: ImmutableArray.Create(("setting1", (object)"%env1%"), ("setting2", true))); - - var resolvedProfile = (ILaunchProfile2)await replacer.ReplaceTokensInProfileAsync(launchProfile); - - // Name and Command name should never be touched - Assert.Equal("$(msbuildProperty1)", resolvedProfile.Name); - Assert.Equal("$(msbuildProperty2)", resolvedProfile.CommandName); - Assert.Equal("envVariable1", resolvedProfile.CommandLineArgs); - Assert.Equal("$(test this string", resolvedProfile.ExecutablePath); - Assert.False(resolvedProfile.LaunchBrowser); - Assert.Equal("http://localhost:8080/", resolvedProfile.LaunchUrl); - Assert.Equal("c:\\test\\Property6", resolvedProfile.WorkingDirectory); - Assert.Equal(new[] { ("var1", "envVariable1"), ("var2", "Property3") }, resolvedProfile.EnvironmentVariables); - Assert.Equal(new[] { ("setting1", (object)"envVariable1"), ("setting2", true) }, resolvedProfile.OtherSettings); - } - - [Theory] - [InlineData("this is msbuild: $(msbuildProperty5) %env1%", "this is msbuild: Property5 envVariable1", true)] - [InlineData("this is msbuild: $(msbuildProperty5) %env1%", "this is msbuild: Property5 %env1%", false)] - [InlineData("this is msbuild: $(UnknownMsbuildProperty) %env1%", "this is msbuild: envVariable1", true)] - [InlineData("this is msbuild: $(UnknownMsbuildProperty) %Unknown%", "this is msbuild: %Unknown%", true)] - [InlineData("this is msbuild: %env3% $(msbuildProperty2) $(msbuildProperty3)", "this is msbuild: Property6 Property2 Property3", true)] - [InlineData(null, null, true)] - [InlineData(" ", " ", true)] - public async Task ReplaceTokensInStringTests(string input, string expected, bool expandEnvVars) - { - var replacer = CreateInstance(); + [Fact] + public async Task ReplaceTokensInProfileTests() + { + var replacer = CreateInstance(); - // Test msbuild vars - string result = await replacer.ReplaceTokensInStringAsync(input, expandEnvVars); - Assert.Equal(expected, result); - } + // Tests all the possible replacements. env3 tests that environment vars are resolved before msbuild tokens + var launchProfile = new LaunchProfile( + name: "$(msbuildProperty1)", + commandLineArgs: "%env1%", + commandName: "$(msbuildProperty2)", + executablePath: "$(test this string", // Not a valid token + workingDirectory: "c:\\test\\%env3%", + launchBrowser: false, + launchUrl: "http://localhost:8080/$(unknownproperty)", + environmentVariables: ImmutableArray.Create(("var1", "%env1%"), ("var2", "$(msbuildProperty3)")), + otherSettings: ImmutableArray.Create(("setting1", (object)"%env1%"), ("setting2", true))); - private DebugTokenReplacer CreateInstance() - { - var environmentHelper = _envHelper.Object; - - var activeDebugFramework = new Mock(); - activeDebugFramework.Setup(s => s.GetConfiguredProjectForActiveFrameworkAsync()) - .Returns(() => Task.FromResult(ConfiguredProjectFactory.Create())); - - string projectFile = - """ - - - Property1 - Property2 - Property3 - Property4 - Property5 - Property6 - - - """; - - return new DebugTokenReplacer(environmentHelper, activeDebugFramework.Object, IProjectAccessorFactory.Create(projectFile)); - } + var resolvedProfile = (ILaunchProfile2)await replacer.ReplaceTokensInProfileAsync(launchProfile); + + // Name and Command name should never be touched + Assert.Equal("$(msbuildProperty1)", resolvedProfile.Name); + Assert.Equal("$(msbuildProperty2)", resolvedProfile.CommandName); + Assert.Equal("envVariable1", resolvedProfile.CommandLineArgs); + Assert.Equal("$(test this string", resolvedProfile.ExecutablePath); + Assert.False(resolvedProfile.LaunchBrowser); + Assert.Equal("http://localhost:8080/", resolvedProfile.LaunchUrl); + Assert.Equal("c:\\test\\Property6", resolvedProfile.WorkingDirectory); + Assert.Equal(new[] { ("var1", "envVariable1"), ("var2", "Property3") }, resolvedProfile.EnvironmentVariables); + Assert.Equal(new[] { ("setting1", (object)"envVariable1"), ("setting2", true) }, resolvedProfile.OtherSettings); + } + + [Theory] + [InlineData("this is msbuild: $(msbuildProperty5) %env1%", "this is msbuild: Property5 envVariable1", true)] + [InlineData("this is msbuild: $(msbuildProperty5) %env1%", "this is msbuild: Property5 %env1%", false)] + [InlineData("this is msbuild: $(UnknownMsbuildProperty) %env1%", "this is msbuild: envVariable1", true)] + [InlineData("this is msbuild: $(UnknownMsbuildProperty) %Unknown%", "this is msbuild: %Unknown%", true)] + [InlineData("this is msbuild: %env3% $(msbuildProperty2) $(msbuildProperty3)", "this is msbuild: Property6 Property2 Property3", true)] + [InlineData(null, null, true)] + [InlineData(" ", " ", true)] + public async Task ReplaceTokensInStringTests(string input, string expected, bool expandEnvVars) + { + var replacer = CreateInstance(); + + // Test msbuild vars + string result = await replacer.ReplaceTokensInStringAsync(input, expandEnvVars); + Assert.Equal(expected, result); + } + + private DebugTokenReplacer CreateInstance() + { + var environmentHelper = _envHelper.Object; + + var activeDebugFramework = new Mock(); + activeDebugFramework.Setup(s => s.GetConfiguredProjectForActiveFrameworkAsync()) + .Returns(() => Task.FromResult(ConfiguredProjectFactory.Create())); + + string projectFile = + """ + + + Property1 + Property2 + Property3 + Property4 + Property5 + Property6 + + + """; + + return new DebugTokenReplacer(environmentHelper, activeDebugFramework.Object, IProjectAccessorFactory.Create(projectFile)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileExtensionsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileExtensionsTests.cs index 0574fa95b2..4b283de234 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileExtensionsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileExtensionsTests.cs @@ -1,187 +1,186 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public class LaunchProfileExtensionsTests { - public class LaunchProfileExtensionsTests + [Theory] + [InlineData(false)] + [InlineData(true)] + public void IsInMemoryProfile_ILaunchProfile(bool isInMemory) { - [Theory] - [InlineData(false)] - [InlineData(true)] - public void IsInMemoryProfile_ILaunchProfile(bool isInMemory) - { - var data = new LaunchProfile( - name: null, - commandName: null, - doNotPersist: isInMemory); - - var lp = (ILaunchProfile)data; - Assert.Equal(isInMemory, lp.IsInMemoryObject()); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void IsInMemoryProfile_IWritableLaunchProfile(bool isInMemory) - { - var data = new WritableLaunchProfile() - { - DoNotPersist = isInMemory - }; - - var lp = (IWritableLaunchProfile)data; - Assert.Equal(isInMemory, lp.IsInMemoryObject()); - } - - [Theory] - [InlineData(false, false)] - [InlineData(true, true)] - [InlineData(null, false)] - public void IsNativeDebuggingEnabled_ILaunchProfile(bool? value, bool expected) - { - var data = new LaunchProfile( - name: null, - commandName: null, - otherSettings: value == null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.NativeDebuggingProperty, value.Value))); - - Assert.Equal(expected, data.IsNativeDebuggingEnabled()); - } - - [Theory] - [InlineData(false, false)] - [InlineData(true, true)] - [InlineData(null, false)] - public void IsSqlDebuggingEnabled_ILaunchProfile(bool? value, bool expected) - { - var data = new LaunchProfile( - name: null, - commandName: null, - otherSettings: value == null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.SqlDebuggingProperty, value.Value))); - - Assert.Equal(expected, data.IsSqlDebuggingEnabled()); - } - - [Theory] - [InlineData(false, false)] - [InlineData(true, true)] - [InlineData(null, false)] - public void IsRemoteDebugEnabled_ILaunchProfile(bool? value, bool expected) - { - var data = new LaunchProfile( - name: null, - commandName: null, - otherSettings: value == null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.RemoteDebugEnabledProperty, value.Value))); - - Assert.Equal(expected, data.IsRemoteDebugEnabled()); - } - - [Theory] - [InlineData("host", "host")] - [InlineData("", "")] - [InlineData(null, null)] - public void RemoteDebugMachine_ILaunchProfile(string? value, string? expected) - { - var data = new LaunchProfile( - name: null, - commandName: null, - otherSettings: value is null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.RemoteDebugMachineProperty, value))); + var data = new LaunchProfile( + name: null, + commandName: null, + doNotPersist: isInMemory); - Assert.Equal(expected, data.RemoteDebugMachine()); - } + var lp = (ILaunchProfile)data; + Assert.Equal(isInMemory, lp.IsInMemoryObject()); + } - [Fact] - public void TryGetSetting_ILaunchProfile() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void IsInMemoryProfile_IWritableLaunchProfile(bool isInMemory) + { + var data = new WritableLaunchProfile() { - var mock = new Mock(); - mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableDictionary.Empty.Add("A", 1)); + DoNotPersist = isInMemory + }; - Assert.True(mock.Object.TryGetSetting("A", out object? o)); - Assert.Equal(1, o); + var lp = (IWritableLaunchProfile)data; + Assert.Equal(isInMemory, lp.IsInMemoryObject()); + } - Assert.False(mock.Object.TryGetSetting("B", out o)); - Assert.Null(o); - } + [Theory] + [InlineData(false, false)] + [InlineData(true, true)] + [InlineData(null, false)] + public void IsNativeDebuggingEnabled_ILaunchProfile(bool? value, bool expected) + { + var data = new LaunchProfile( + name: null, + commandName: null, + otherSettings: value == null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.NativeDebuggingProperty, value.Value))); - [Fact] - public void TryGetSetting_ILaunchProfile2() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableArray.Create<(string, object)>(("A", 1))); + Assert.Equal(expected, data.IsNativeDebuggingEnabled()); + } - Assert.True(mock.Object.TryGetSetting("A", out object? o)); - Assert.Equal(1, o); + [Theory] + [InlineData(false, false)] + [InlineData(true, true)] + [InlineData(null, false)] + public void IsSqlDebuggingEnabled_ILaunchProfile(bool? value, bool expected) + { + var data = new LaunchProfile( + name: null, + commandName: null, + otherSettings: value == null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.SqlDebuggingProperty, value.Value))); - Assert.False(mock.Object.TryGetSetting("B", out o)); - Assert.Null(o); - } + Assert.Equal(expected, data.IsSqlDebuggingEnabled()); + } - [Fact] - public void FlattenEnvironmentVariables_ILaunchProfile_Null() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => null); + [Theory] + [InlineData(false, false)] + [InlineData(true, true)] + [InlineData(null, false)] + public void IsRemoteDebugEnabled_ILaunchProfile(bool? value, bool expected) + { + var data = new LaunchProfile( + name: null, + commandName: null, + otherSettings: value == null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.RemoteDebugEnabledProperty, value.Value))); - Assert.Empty(mock.Object.FlattenEnvironmentVariables()); - } + Assert.Equal(expected, data.IsRemoteDebugEnabled()); + } - [Fact] - public void FlattenEnvironmentVariables_ILaunchProfile_Empty() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => ImmutableDictionary.Empty); - Assert.Empty(mock.Object.FlattenEnvironmentVariables()); - } + [Theory] + [InlineData("host", "host")] + [InlineData("", "")] + [InlineData(null, null)] + public void RemoteDebugMachine_ILaunchProfile(string? value, string? expected) + { + var data = new LaunchProfile( + name: null, + commandName: null, + otherSettings: value is null ? ImmutableArray<(string, object)>.Empty : ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.RemoteDebugMachineProperty, value))); - [Fact] - public void FlattenEnvironmentVariables_ILaunchProfile_WithItems() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => ImmutableDictionary.Empty.Add("A", "1")); + Assert.Equal(expected, data.RemoteDebugMachine()); + } - Assert.Equal(new[] { ("A", "1") }, mock.Object.FlattenEnvironmentVariables()); - } + [Fact] + public void TryGetSetting_ILaunchProfile() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableDictionary.Empty.Add("A", 1)); - [Fact] - public void FlattenEnvironmentVariables_ILaunchProfile2() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => ImmutableArray.Create(("A", "1"))); + Assert.True(mock.Object.TryGetSetting("A", out object? o)); + Assert.Equal(1, o); - Assert.Equal(new[] { ("A", "1") }, mock.Object.FlattenEnvironmentVariables()); - } + Assert.False(mock.Object.TryGetSetting("B", out o)); + Assert.Null(o); + } - [Fact] - public void FlattenOtherSettings_ILaunchProfile_Null() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.OtherSettings).Returns(() => null); + [Fact] + public void TryGetSetting_ILaunchProfile2() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableArray.Create<(string, object)>(("A", 1))); - Assert.Empty(mock.Object.FlattenOtherSettings()); - } + Assert.True(mock.Object.TryGetSetting("A", out object? o)); + Assert.Equal(1, o); - [Fact] - public void FlattenOtherSettings_ILaunchProfile_Empty() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableDictionary.Empty); - Assert.Empty(mock.Object.FlattenOtherSettings()); - } + Assert.False(mock.Object.TryGetSetting("B", out o)); + Assert.Null(o); + } - [Fact] - public void FlattenOtherSettings_ILaunchProfile_WithItems() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableDictionary.Empty.Add("A", 1)); + [Fact] + public void FlattenEnvironmentVariables_ILaunchProfile_Null() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => null); - Assert.Equal(new[] { ("A", (object)1) }, mock.Object.FlattenOtherSettings()); - } + Assert.Empty(mock.Object.FlattenEnvironmentVariables()); + } - [Fact] - public void FlattenOtherSettings_ILaunchProfile2() - { - var mock = new Mock(); - mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableArray.Create(("A", (object)1))); + [Fact] + public void FlattenEnvironmentVariables_ILaunchProfile_Empty() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => ImmutableDictionary.Empty); + Assert.Empty(mock.Object.FlattenEnvironmentVariables()); + } + + [Fact] + public void FlattenEnvironmentVariables_ILaunchProfile_WithItems() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => ImmutableDictionary.Empty.Add("A", "1")); + + Assert.Equal(new[] { ("A", "1") }, mock.Object.FlattenEnvironmentVariables()); + } + + [Fact] + public void FlattenEnvironmentVariables_ILaunchProfile2() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.EnvironmentVariables).Returns(() => ImmutableArray.Create(("A", "1"))); + + Assert.Equal(new[] { ("A", "1") }, mock.Object.FlattenEnvironmentVariables()); + } + + [Fact] + public void FlattenOtherSettings_ILaunchProfile_Null() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.OtherSettings).Returns(() => null); + + Assert.Empty(mock.Object.FlattenOtherSettings()); + } + + [Fact] + public void FlattenOtherSettings_ILaunchProfile_Empty() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableDictionary.Empty); + Assert.Empty(mock.Object.FlattenOtherSettings()); + } + + [Fact] + public void FlattenOtherSettings_ILaunchProfile_WithItems() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableDictionary.Empty.Add("A", 1)); + + Assert.Equal(new[] { ("A", (object)1) }, mock.Object.FlattenOtherSettings()); + } + + [Fact] + public void FlattenOtherSettings_ILaunchProfile2() + { + var mock = new Mock(); + mock.SetupGet(lp => lp.OtherSettings).Returns(() => ImmutableArray.Create(("A", (object)1))); - Assert.Equal(new[] { ("A", (object)1) }, mock.Object.FlattenOtherSettings()); - } + Assert.Equal(new[] { ("A", (object)1) }, mock.Object.FlattenOtherSettings()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileTests.cs index b6ccbd8bef..77c0fa1d6f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchProfileTests.cs @@ -1,71 +1,70 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public class LaunchProfileTests { - public class LaunchProfileTests + [Fact] + public void LaunchProfile_CtorTests() { - [Fact] - public void LaunchProfile_CtorTests() - { - string? name = "Test"; - string? executablePath = "c:\\this\\is\\a\\exe\\path"; - string? commandName = null; - string? commandLineArgs = "args"; - string? workingDirectory = "c:\\working\\directory\\"; - bool launchBrowser = true; - string? launchUrl = "LaunchPage.html"; - var environmentVariables = ImmutableArray.Create(("var1", "Value1"), ("var2", "Value2")); - var otherSettings = ImmutableArray.Create<(string, object)>(("setting1", true), ("setting2", "mysetting")); - var doNotPersist = true; + string? name = "Test"; + string? executablePath = "c:\\this\\is\\a\\exe\\path"; + string? commandName = null; + string? commandLineArgs = "args"; + string? workingDirectory = "c:\\working\\directory\\"; + bool launchBrowser = true; + string? launchUrl = "LaunchPage.html"; + var environmentVariables = ImmutableArray.Create(("var1", "Value1"), ("var2", "Value2")); + var otherSettings = ImmutableArray.Create<(string, object)>(("setting1", true), ("setting2", "mysetting")); + var doNotPersist = true; - var profile = new LaunchProfile( - name: name, - executablePath: executablePath, - commandName: commandName, - commandLineArgs: commandLineArgs, - workingDirectory: workingDirectory, - launchBrowser: launchBrowser, - launchUrl: launchUrl, - environmentVariables: environmentVariables, - otherSettings: otherSettings, - doNotPersist: doNotPersist); + var profile = new LaunchProfile( + name: name, + executablePath: executablePath, + commandName: commandName, + commandLineArgs: commandLineArgs, + workingDirectory: workingDirectory, + launchBrowser: launchBrowser, + launchUrl: launchUrl, + environmentVariables: environmentVariables, + otherSettings: otherSettings, + doNotPersist: doNotPersist); - Assert.Equal(name, profile.Name); - Assert.Equal(executablePath, profile.ExecutablePath); - Assert.Equal(commandLineArgs, profile.CommandLineArgs); - Assert.Equal(workingDirectory, profile.WorkingDirectory); - Assert.Equal(launchBrowser, profile.LaunchBrowser); - Assert.Equal(launchUrl, profile.LaunchUrl); - Assert.Equal(environmentVariables, profile.EnvironmentVariables); - Assert.Equal(otherSettings, profile.OtherSettings); - Assert.Equal(doNotPersist, profile.DoNotPersist); - } + Assert.Equal(name, profile.Name); + Assert.Equal(executablePath, profile.ExecutablePath); + Assert.Equal(commandLineArgs, profile.CommandLineArgs); + Assert.Equal(workingDirectory, profile.WorkingDirectory); + Assert.Equal(launchBrowser, profile.LaunchBrowser); + Assert.Equal(launchUrl, profile.LaunchUrl); + Assert.Equal(environmentVariables, profile.EnvironmentVariables); + Assert.Equal(otherSettings, profile.OtherSettings); + Assert.Equal(doNotPersist, profile.DoNotPersist); + } - [Fact] - public void Clone_DoesNotCopyIfAlreadyConcreteType() - { - var source = new LaunchProfile("Name", "Command"); + [Fact] + public void Clone_DoesNotCopyIfAlreadyConcreteType() + { + var source = new LaunchProfile("Name", "Command"); - var clone = LaunchProfile.Clone(source); + var clone = LaunchProfile.Clone(source); - Assert.Same(clone, source); - } + Assert.Same(clone, source); + } - [Fact] - public void Clone_CopiesIfUnknownType() - { - var mock = new Mock(); - - var clone = LaunchProfile.Clone(mock.Object); + [Fact] + public void Clone_CopiesIfUnknownType() + { + var mock = new Mock(); + + var clone = LaunchProfile.Clone(mock.Object); - Assert.NotSame(clone, mock.Object); - } + Assert.NotSame(clone, mock.Object); + } - [Fact] - public void LaunchProfile_IsSameProfileNameTests() - { - Assert.True(LaunchProfile.IsSameProfileName("test", "test")); - Assert.False(LaunchProfile.IsSameProfileName("test", "Test")); - } + [Fact] + public void LaunchProfile_IsSameProfileNameTests() + { + Assert.True(LaunchProfile.IsSameProfileName("test", "test")); + Assert.False(LaunchProfile.IsSameProfileName("test", "Test")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsJsonEncodingTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsJsonEncodingTests.cs index ac028abc0e..2e91e0a601 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsJsonEncodingTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsJsonEncodingTests.cs @@ -2,69 +2,204 @@ using Newtonsoft.Json.Linq; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public sealed class LaunchSettingsJsonEncodingTests { - public sealed class LaunchSettingsJsonEncodingTests + private readonly OrderPrecedenceImportCollection _providers = new(projectCapabilityCheckProvider: (UnconfiguredProject?)null); + + [Theory] + [InlineData("""{ }""")] + [InlineData("""{ "profiles": {} }""")] + public void FromJson_Empty(string json) { - private readonly OrderPrecedenceImportCollection _providers = new(projectCapabilityCheckProvider: (UnconfiguredProject?)null); + var (profiles, globalSettings) = LaunchSettingsJsonEncoding.FromJson(new StringReader(json), null!); - [Theory] - [InlineData("""{ }""")] - [InlineData("""{ "profiles": {} }""")] - public void FromJson_Empty(string json) - { - var (profiles, globalSettings) = LaunchSettingsJsonEncoding.FromJson(new StringReader(json), null!); + Assert.Empty(profiles); + Assert.Empty(globalSettings); + } - Assert.Empty(profiles); - Assert.Empty(globalSettings); + [Fact] + public void RoundTripMultipleProfiles() + { + const string json = + """ + { + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "http://localhost:1234/test.html" + }, + "HasCustomValues": { + "executablePath": "c:\\test\\project\\bin\\project.exe", + "commandLineArgs": "--arg1 --arg2", + "workingDirectory": "c:\\test\\project", + "custom1": true, + "custom2": 124, + "custom3": "mycustomVal" + }, + "Docker": { + "commandName": "Docker", + "dockerOption1": "some option in docker", + "dockerOption2": "Another option in docker" + }, + "web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNET_ENVIRONMENT": "Development", + "ASPNET_APPLICATIONBASE": "c:\\Users\\billhie\\Documents\\projects\\WebApplication8\\src\\WebApplication8" + } + }, + "Docker Compose": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "composeProfile": { + "includes": [ + "web1" + ] + } + }, + "DateInEnvironmentVariableBugRepro": { + "commandName": "Project", + "environmentVariables": { + "DATE": "2019-07-11T12:00:00" + } + } + }, + "string": "hello", + "int": 123, + "bool": true, + "null": null, + "dictionary": { + "A": "1", + "B": "2" + } } + """; + + var (profiles, globalSettings) = LaunchSettingsJsonEncoding.FromJson(new StringReader(json), _providers); + + Assert.Equal(6, profiles.Length); + + var profile = profiles[0]; + Assert.Equal("IIS Express", profile.Name); + Assert.Equal("IISExpress", profile.CommandName); + Assert.Equal("http://localhost:1234/test.html", profile.LaunchUrl); + Assert.True(profile.LaunchBrowser); + Assert.False(profile.IsInMemoryObject()); + + profile = profiles[1]; + Assert.Equal("HasCustomValues", profile.Name); + Assert.Equal("c:\\test\\project", profile.WorkingDirectory); + Assert.Equal("c:\\test\\project\\bin\\project.exe", profile.ExecutablePath); + Assert.False(profile.LaunchBrowser); + Assert.Null(profile.LaunchUrl); + Assert.Equal(3, profile.OtherSettings.Length); + Assert.Equal(("custom1", true), profile.OtherSettings[0]); + Assert.Equal(("custom2", 124), profile.OtherSettings[1]); + Assert.Equal(("custom3", "mycustomVal"), profile.OtherSettings[2]); + Assert.False(profile.IsInMemoryObject()); + + profile = profiles[2]; + Assert.Equal("Docker", profile.Name); + Assert.Equal("Docker", profile.CommandName); + Assert.Null(profile.WorkingDirectory); + Assert.Null(profile.ExecutablePath); + Assert.False(profile.LaunchBrowser); + Assert.Null(profile.LaunchUrl); + Assert.Empty(profile.EnvironmentVariables); + Assert.Equal(2, profile.OtherSettings.Length); + Assert.Equal(("dockerOption1", "some option in docker"), profile.OtherSettings[0]); + Assert.Equal(("dockerOption2", "Another option in docker"), profile.OtherSettings[1]); + Assert.False(profile.IsInMemoryObject()); + + profile = profiles[3]; + Assert.Equal("web", profile.Name); + Assert.Equal("Project", profile.CommandName); + Assert.Null(profile.WorkingDirectory); + Assert.Null(profile.ExecutablePath); + Assert.True(profile.LaunchBrowser); + Assert.Null(profile.LaunchUrl); + Assert.Equal(2, profile.EnvironmentVariables.Length); + Assert.Equal(("ASPNET_ENVIRONMENT", "Development"), profile.EnvironmentVariables[0]); + Assert.Equal(("ASPNET_APPLICATIONBASE", @"c:\Users\billhie\Documents\projects\WebApplication8\src\WebApplication8"), profile.EnvironmentVariables[1]); + Assert.False(profile.IsInMemoryObject()); + + profile = profiles[4]; + Assert.Equal("Docker Compose", profile.Name); + Assert.Equal("DockerCompose", profile.CommandName); + Assert.Null(profile.WorkingDirectory); + Assert.Null(profile.ExecutablePath); + Assert.False(profile.LaunchBrowser); + Assert.Null(profile.LaunchUrl); + Assert.Empty(profile.EnvironmentVariables); + Assert.Equal(2, profile.OtherSettings.Length); + Assert.Equal("commandVersion", profile.OtherSettings[0].Key); + Assert.Equal("1.0", profile.OtherSettings[0].Value); + Assert.Equal("composeProfile", profile.OtherSettings[1].Key); + var composeProfiles = Assert.IsType>(profile.OtherSettings[1].Value); + var includes = Assert.IsType(composeProfiles["includes"]); + Assert.Single(includes); + Assert.Equal("web1", includes[0]); + Assert.False(profile.IsInMemoryObject()); + + Assert.Equal(5, globalSettings.Length); + Assert.Equal(("string", new JValue("hello")), globalSettings[0]); + Assert.Equal(("int", new JValue(123)), globalSettings[1]); + Assert.Equal(("bool", new JValue(true)), globalSettings[2]); + Assert.Equal(("null", JValue.CreateNull()), globalSettings[3]); + var dicObj = Assert.IsType(globalSettings[4].Value); + Assert.Equal(2, dicObj.Count); + Assert.Equal("1", dicObj["A"]); + Assert.Equal("2", dicObj["B"]); + + profile = profiles[5]; + Assert.Equal("DateInEnvironmentVariableBugRepro", profile.Name); + Assert.Equal("Project", profile.CommandName); + Assert.Null(profile.WorkingDirectory); + Assert.Null(profile.ExecutablePath); + Assert.False(profile.LaunchBrowser); + Assert.Null(profile.LaunchUrl); + Assert.Equal(("DATE", "2019-07-11T12:00:00"), Assert.Single(profile.EnvironmentVariables)); + Assert.False(profile.IsInMemoryObject()); + + var roundTrippedJson = LaunchSettingsJsonEncoding.ToJson(profiles, globalSettings); + + Assert.Equal(json, roundTrippedJson); + } - [Fact] - public void RoundTripMultipleProfiles() - { - const string json = + [Fact] + public void ToJson_HandlesComplexOtherAndGlobalSettings() + { + var settings = ImmutableArray.Create<(string Key, object Value)>( + ("string", "hello"), + ("int", 123), + ("bool", true), + ("null", null!), + ("dictionary", new Dictionary { ["A"] = "1", ["B"] = "2" })); + + var launchProfile = new LaunchProfile( + "Name", + "Command", + otherSettings: settings); + + var actual = LaunchSettingsJsonEncoding.ToJson(new[] { launchProfile }, globalSettings: settings); + + var expected = """ { "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "http://localhost:1234/test.html" - }, - "HasCustomValues": { - "executablePath": "c:\\test\\project\\bin\\project.exe", - "commandLineArgs": "--arg1 --arg2", - "workingDirectory": "c:\\test\\project", - "custom1": true, - "custom2": 124, - "custom3": "mycustomVal" - }, - "Docker": { - "commandName": "Docker", - "dockerOption1": "some option in docker", - "dockerOption2": "Another option in docker" - }, - "web": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNET_ENVIRONMENT": "Development", - "ASPNET_APPLICATIONBASE": "c:\\Users\\billhie\\Documents\\projects\\WebApplication8\\src\\WebApplication8" - } - }, - "Docker Compose": { - "commandName": "DockerCompose", - "commandVersion": "1.0", - "composeProfile": { - "includes": [ - "web1" - ] - } - }, - "DateInEnvironmentVariableBugRepro": { - "commandName": "Project", - "environmentVariables": { - "DATE": "2019-07-11T12:00:00" + "Name": { + "commandName": "Command", + "string": "hello", + "int": 123, + "bool": true, + "null": null, + "dictionary": { + "A": "1", + "B": "2" } } }, @@ -79,176 +214,40 @@ public void RoundTripMultipleProfiles() } """; - var (profiles, globalSettings) = LaunchSettingsJsonEncoding.FromJson(new StringReader(json), _providers); - - Assert.Equal(6, profiles.Length); - - var profile = profiles[0]; - Assert.Equal("IIS Express", profile.Name); - Assert.Equal("IISExpress", profile.CommandName); - Assert.Equal("http://localhost:1234/test.html", profile.LaunchUrl); - Assert.True(profile.LaunchBrowser); - Assert.False(profile.IsInMemoryObject()); - - profile = profiles[1]; - Assert.Equal("HasCustomValues", profile.Name); - Assert.Equal("c:\\test\\project", profile.WorkingDirectory); - Assert.Equal("c:\\test\\project\\bin\\project.exe", profile.ExecutablePath); - Assert.False(profile.LaunchBrowser); - Assert.Null(profile.LaunchUrl); - Assert.Equal(3, profile.OtherSettings.Length); - Assert.Equal(("custom1", true), profile.OtherSettings[0]); - Assert.Equal(("custom2", 124), profile.OtherSettings[1]); - Assert.Equal(("custom3", "mycustomVal"), profile.OtherSettings[2]); - Assert.False(profile.IsInMemoryObject()); - - profile = profiles[2]; - Assert.Equal("Docker", profile.Name); - Assert.Equal("Docker", profile.CommandName); - Assert.Null(profile.WorkingDirectory); - Assert.Null(profile.ExecutablePath); - Assert.False(profile.LaunchBrowser); - Assert.Null(profile.LaunchUrl); - Assert.Empty(profile.EnvironmentVariables); - Assert.Equal(2, profile.OtherSettings.Length); - Assert.Equal(("dockerOption1", "some option in docker"), profile.OtherSettings[0]); - Assert.Equal(("dockerOption2", "Another option in docker"), profile.OtherSettings[1]); - Assert.False(profile.IsInMemoryObject()); - - profile = profiles[3]; - Assert.Equal("web", profile.Name); - Assert.Equal("Project", profile.CommandName); - Assert.Null(profile.WorkingDirectory); - Assert.Null(profile.ExecutablePath); - Assert.True(profile.LaunchBrowser); - Assert.Null(profile.LaunchUrl); - Assert.Equal(2, profile.EnvironmentVariables.Length); - Assert.Equal(("ASPNET_ENVIRONMENT", "Development"), profile.EnvironmentVariables[0]); - Assert.Equal(("ASPNET_APPLICATIONBASE", @"c:\Users\billhie\Documents\projects\WebApplication8\src\WebApplication8"), profile.EnvironmentVariables[1]); - Assert.False(profile.IsInMemoryObject()); - - profile = profiles[4]; - Assert.Equal("Docker Compose", profile.Name); - Assert.Equal("DockerCompose", profile.CommandName); - Assert.Null(profile.WorkingDirectory); - Assert.Null(profile.ExecutablePath); - Assert.False(profile.LaunchBrowser); - Assert.Null(profile.LaunchUrl); - Assert.Empty(profile.EnvironmentVariables); - Assert.Equal(2, profile.OtherSettings.Length); - Assert.Equal("commandVersion", profile.OtherSettings[0].Key); - Assert.Equal("1.0", profile.OtherSettings[0].Value); - Assert.Equal("composeProfile", profile.OtherSettings[1].Key); - var composeProfiles = Assert.IsType>(profile.OtherSettings[1].Value); - var includes = Assert.IsType(composeProfiles["includes"]); - Assert.Single(includes); - Assert.Equal("web1", includes[0]); - Assert.False(profile.IsInMemoryObject()); - - Assert.Equal(5, globalSettings.Length); - Assert.Equal(("string", new JValue("hello")), globalSettings[0]); - Assert.Equal(("int", new JValue(123)), globalSettings[1]); - Assert.Equal(("bool", new JValue(true)), globalSettings[2]); - Assert.Equal(("null", JValue.CreateNull()), globalSettings[3]); - var dicObj = Assert.IsType(globalSettings[4].Value); - Assert.Equal(2, dicObj.Count); - Assert.Equal("1", dicObj["A"]); - Assert.Equal("2", dicObj["B"]); - - profile = profiles[5]; - Assert.Equal("DateInEnvironmentVariableBugRepro", profile.Name); - Assert.Equal("Project", profile.CommandName); - Assert.Null(profile.WorkingDirectory); - Assert.Null(profile.ExecutablePath); - Assert.False(profile.LaunchBrowser); - Assert.Null(profile.LaunchUrl); - Assert.Equal(("DATE", "2019-07-11T12:00:00"), Assert.Single(profile.EnvironmentVariables)); - Assert.False(profile.IsInMemoryObject()); - - var roundTrippedJson = LaunchSettingsJsonEncoding.ToJson(profiles, globalSettings); - - Assert.Equal(json, roundTrippedJson); - } - - [Fact] - public void ToJson_HandlesComplexOtherAndGlobalSettings() - { - var settings = ImmutableArray.Create<(string Key, object Value)>( - ("string", "hello"), - ("int", 123), - ("bool", true), - ("null", null!), - ("dictionary", new Dictionary { ["A"] = "1", ["B"] = "2" })); - - var launchProfile = new LaunchProfile( - "Name", - "Command", - otherSettings: settings); - - var actual = LaunchSettingsJsonEncoding.ToJson(new[] { launchProfile }, globalSettings: settings); - - var expected = - """ - { - "profiles": { - "Name": { - "commandName": "Command", - "string": "hello", - "int": 123, - "bool": true, - "null": null, - "dictionary": { - "A": "1", - "B": "2" - } - } - }, - "string": "hello", - "int": 123, - "bool": true, - "null": null, - "dictionary": { - "A": "1", - "B": "2" - } - } - """; - - Assert.Equal(expected, actual); - } + Assert.Equal(expected, actual); + } - [Fact] - public void FromJson_WithComments() + [Fact] + public void FromJson_WithComments() + { + // https://github.com/dotnet/project-system/issues/8168 + const string json = + """ { - // https://github.com/dotnet/project-system/issues/8168 - const string json = - """ - { - "profiles": { - "ConsoleApp1": { - "commandName": "Project" - //"commandLineArgs": "1111" - } - } + "profiles": { + "ConsoleApp1": { + "commandName": "Project" + //"commandLineArgs": "1111" } - """; + } + } + """; - var (profiles, globalSettings) = LaunchSettingsJsonEncoding.FromJson(new StringReader(json), _providers); + var (profiles, globalSettings) = LaunchSettingsJsonEncoding.FromJson(new StringReader(json), _providers); - var profile = Assert.Single(profiles); + var profile = Assert.Single(profiles); - Assert.Equal("ConsoleApp1", profile.Name); - Assert.Equal("Project", profile.CommandName); - Assert.False(profile.DoNotPersist); - Assert.Null(profile.ExecutablePath); - Assert.Null(profile.CommandLineArgs); - Assert.Null(profile.WorkingDirectory); - Assert.Null(profile.LaunchUrl); - Assert.False(profile.LaunchBrowser); - Assert.Empty(profile.OtherSettings); - Assert.False(profile.IsInMemoryObject()); + Assert.Equal("ConsoleApp1", profile.Name); + Assert.Equal("Project", profile.CommandName); + Assert.False(profile.DoNotPersist); + Assert.Null(profile.ExecutablePath); + Assert.Null(profile.CommandLineArgs); + Assert.Null(profile.WorkingDirectory); + Assert.Null(profile.LaunchUrl); + Assert.False(profile.LaunchBrowser); + Assert.Empty(profile.OtherSettings); + Assert.False(profile.IsInMemoryObject()); - Assert.Empty(globalSettings); - } + Assert.Empty(globalSettings); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsProviderTests.cs index 9a58fc0a3e..03cbcf172e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsProviderTests.cs @@ -10,1106 +10,1105 @@ using Newtonsoft.Json.Linq; using Task = System.Threading.Tasks.Task; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public class LaunchSettingsProviderTests { - public class LaunchSettingsProviderTests + internal static LaunchSettingsUnderTest GetLaunchSettingsProvider(IFileSystem? fileSystem, string? appDesignerFolder = @"c:\test\Project1\Properties", string activeProfile = "") { - internal static LaunchSettingsUnderTest GetLaunchSettingsProvider(IFileSystem? fileSystem, string? appDesignerFolder = @"c:\test\Project1\Properties", string activeProfile = "") - { - var activeProfileValue = new Mock(); - activeProfileValue.Setup(s => s.Name).Returns(activeProfile); - var debuggerData = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugProfileProperty, activeProfileValue.Object); - - var projectProperties = ProjectPropertiesFactory.Create(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, appDesignerFolder); - var activeConfigurationProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var project = UnconfiguredProjectFactory.Create(fullPath: @"c:\test\Project1\Project1.csproj"); - var properties = ProjectPropertiesFactory.Create(project, new[] { debuggerData }); - var threadingService = IProjectThreadingServiceFactory.Create(); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project, threadingService, null, properties); - var projectServices = IUnconfiguredProjectServicesFactory.Create( - IProjectAsynchronousTasksServiceFactory.Create(), - projectService: IProjectServiceFactory.Create( - services: ProjectServicesFactory.Create( - threadingService: threadingService))); - var projectFaultHandlerService = IProjectFaultHandlerServiceFactory.Create(); + var activeProfileValue = new Mock(); + activeProfileValue.Setup(s => s.Name).Returns(activeProfile); + var debuggerData = new PropertyPageData(ProjectDebugger.SchemaName, ProjectDebugger.ActiveDebugProfileProperty, activeProfileValue.Object); + + var projectProperties = ProjectPropertiesFactory.Create(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, appDesignerFolder); + var activeConfigurationProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var project = UnconfiguredProjectFactory.Create(fullPath: @"c:\test\Project1\Project1.csproj"); + var properties = ProjectPropertiesFactory.Create(project, new[] { debuggerData }); + var threadingService = IProjectThreadingServiceFactory.Create(); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project, threadingService, null, properties); + var projectServices = IUnconfiguredProjectServicesFactory.Create( + IProjectAsynchronousTasksServiceFactory.Create(), + projectService: IProjectServiceFactory.Create( + services: ProjectServicesFactory.Create( + threadingService: threadingService))); + var projectFaultHandlerService = IProjectFaultHandlerServiceFactory.Create(); #pragma warning disable VSSDK005 - var joinableTaskContext = new JoinableTaskContext(); + var joinableTaskContext = new JoinableTaskContext(); #pragma warning restore VSSDK005 - var provider = new LaunchSettingsUnderTest(project, projectServices, fileSystem ?? new IFileSystemMock(), commonServices, null, activeConfigurationProjectProperties, projectFaultHandlerService, new DefaultLaunchProfileProvider(project), joinableTaskContext); - return provider; - } + var provider = new LaunchSettingsUnderTest(project, projectServices, fileSystem ?? new IFileSystemMock(), commonServices, null, activeConfigurationProjectProperties, projectFaultHandlerService, new DefaultLaunchProfileProvider(project), joinableTaskContext); + return provider; + } - internal static void SetJsonSerializationProviders(LaunchSettingsUnderTest provider) + internal static void SetJsonSerializationProviders(LaunchSettingsUnderTest provider) + { + var mockIJsonSection = new Mock(); + mockIJsonSection.Setup(s => s.JsonSection).Returns("iisSettings"); + mockIJsonSection.Setup(s => s.SerializationType).Returns(typeof(IISSettingsData)); + var lazyProvider = new Lazy(() => { - var mockIJsonSection = new Mock(); - mockIJsonSection.Setup(s => s.JsonSection).Returns("iisSettings"); - mockIJsonSection.Setup(s => s.SerializationType).Returns(typeof(IISSettingsData)); - var lazyProvider = new Lazy(() => - { - var mockSerializer = new Mock(); - return mockSerializer.Object; - }, mockIJsonSection.Object, true); - var settingsProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) - { - new Lazy(() => lazyProvider.Value, mockIJsonSection.Object) - }; - provider.SetSettingsProviderCollection(settingsProviders); - } - - [Fact] - public void WhenNoAppDesignerFolder_LaunchSettingsIsInRoot() + var mockSerializer = new Mock(); + return mockSerializer.Object; + }, mockIJsonSection.Object, true); + var settingsProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) { - using var provider = GetLaunchSettingsProvider(null, appDesignerFolder: null); - Assert.Equal(@"c:\test\Project1\launchSettings.json", provider.LaunchSettingsFile); - } + new Lazy(() => lazyProvider.Value, mockIJsonSection.Object) + }; + provider.SetSettingsProviderCollection(settingsProviders); + } - [Theory] - [InlineData(@"C:\Properties", @"C:\Properties\launchSettings.json")] - [InlineData(@"C:\Project\Properties", @"C:\Project\Properties\launchSettings.json")] - [InlineData(@"C:\Project\My Project", @"C:\Project\My Project\launchSettings.json")] - public async Task WhenAppDesignerFolder_LaunchSettingsIsInAppDesignerFolder(string appDesignerFolder, string expected) - { - using var provider = GetLaunchSettingsProvider(null, appDesignerFolder: appDesignerFolder); - var result = await provider.GetLaunchSettingsFilePathNoCacheAsync(); + [Fact] + public void WhenNoAppDesignerFolder_LaunchSettingsIsInRoot() + { + using var provider = GetLaunchSettingsProvider(null, appDesignerFolder: null); + Assert.Equal(@"c:\test\Project1\launchSettings.json", provider.LaunchSettingsFile); + } - Assert.Equal(expected, result); - } + [Theory] + [InlineData(@"C:\Properties", @"C:\Properties\launchSettings.json")] + [InlineData(@"C:\Project\Properties", @"C:\Project\Properties\launchSettings.json")] + [InlineData(@"C:\Project\My Project", @"C:\Project\My Project\launchSettings.json")] + public async Task WhenAppDesignerFolder_LaunchSettingsIsInAppDesignerFolder(string appDesignerFolder, string expected) + { + using var provider = GetLaunchSettingsProvider(null, appDesignerFolder: appDesignerFolder); + var result = await provider.GetLaunchSettingsFilePathNoCacheAsync(); - [Fact] - public void ActiveProfileTests() - { - string activeProfile = "MyCommand"; - var testProfiles = new Mock(); - testProfiles.Setup(m => m.ActiveProfile).Returns(new LaunchProfile(activeProfile, null)); + Assert.Equal(expected, result); + } - using var provider = GetLaunchSettingsProvider(null); - Assert.Null(provider.ActiveProfile); + [Fact] + public void ActiveProfileTests() + { + string activeProfile = "MyCommand"; + var testProfiles = new Mock(); + testProfiles.Setup(m => m.ActiveProfile).Returns(new LaunchProfile(activeProfile, null)); - provider.SetCurrentSnapshot(testProfiles.Object); - Assert.NotNull(provider.ActiveProfile); - Assert.Equal(activeProfile, provider.ActiveProfile?.Name); - } + using var provider = GetLaunchSettingsProvider(null); + Assert.Null(provider.ActiveProfile); - [Fact] - public async Task UpdateProfiles_NoSettingsFile() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await provider.UpdateProfilesAsyncTest(null); - Assert.Single(provider.CurrentSnapshot.Profiles); - Assert.Equal("Project", provider.CurrentSnapshot.ActiveProfile!.CommandName); - } + provider.SetCurrentSnapshot(testProfiles.Object); + Assert.NotNull(provider.ActiveProfile); + Assert.Equal(activeProfile, provider.ActiveProfile?.Name); + } - [Fact] - public async Task UpdateProfilesBasicSettingsFile() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - - await provider.UpdateProfilesAsyncTest(null); - Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); - Assert.Empty(provider.CurrentSnapshot.GlobalSettings); - Assert.Equal("IIS Express", provider.CurrentSnapshot.ActiveProfile!.Name); - } + [Fact] + public async Task UpdateProfiles_NoSettingsFile() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await provider.UpdateProfilesAsyncTest(null); + Assert.Single(provider.CurrentSnapshot.Profiles); + Assert.Equal("Project", provider.CurrentSnapshot.ActiveProfile!.CommandName); + } - [Fact] - public async Task UpdateProfilesSetActiveProfileFromProperty() - { - var moqFS = new IFileSystemMock(); - using var provider1 = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider1.LaunchSettingsFile, JsonString1); - - // Change the value of activeDebugProfile to web it should be the active one. Simulates a change - // on disk doesn't affect active profile - using var provider2 = GetLaunchSettingsProvider(moqFS, activeProfile: "web"); - await provider2.UpdateProfilesAsyncTest(null); - Assert.Equal("web", provider2.CurrentSnapshot.ActiveProfile!.Name); - } + [Fact] + public async Task UpdateProfilesBasicSettingsFile() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + + await provider.UpdateProfilesAsyncTest(null); + Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); + Assert.Empty(provider.CurrentSnapshot.GlobalSettings); + Assert.Equal("IIS Express", provider.CurrentSnapshot.ActiveProfile!.Name); + } - [Fact] - public async Task UpdateProfiles_ChangeActiveProfileOnly() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - await provider.UpdateProfilesAsyncTest(null); - provider.SetNextVersionTest(123); - - // don't change file on disk, just active one - await provider.UpdateProfilesAsyncTest("Docker"); - Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); - Assert.Empty(provider.CurrentSnapshot.GlobalSettings); - Assert.Equal("Docker", provider.CurrentSnapshot.ActiveProfile!.Name); - Assert.Equal(123, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); - } + [Fact] + public async Task UpdateProfilesSetActiveProfileFromProperty() + { + var moqFS = new IFileSystemMock(); + using var provider1 = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider1.LaunchSettingsFile, JsonString1); + + // Change the value of activeDebugProfile to web it should be the active one. Simulates a change + // on disk doesn't affect active profile + using var provider2 = GetLaunchSettingsProvider(moqFS, activeProfile: "web"); + await provider2.UpdateProfilesAsyncTest(null); + Assert.Equal("web", provider2.CurrentSnapshot.ActiveProfile!.Name); + } - [Fact] - public async Task UpdateProfiles_BadJsonShouldLeaveProfilesStable() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - await provider.UpdateProfilesAsyncTest(null); - - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, BadJsonString); - await provider.UpdateProfilesAsyncTest("Docker"); - Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); - Assert.Empty(provider.CurrentSnapshot.GlobalSettings); - Assert.Equal("IIS Express", provider.CurrentSnapshot.ActiveProfile!.Name); - } + [Fact] + public async Task UpdateProfiles_ChangeActiveProfileOnly() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + await provider.UpdateProfilesAsyncTest(null); + provider.SetNextVersionTest(123); + + // don't change file on disk, just active one + await provider.UpdateProfilesAsyncTest("Docker"); + Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); + Assert.Empty(provider.CurrentSnapshot.GlobalSettings); + Assert.Equal("Docker", provider.CurrentSnapshot.ActiveProfile!.Name); + Assert.Equal(123, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); + } - [Fact] - public async Task UpdateProfiles_SetsErrorProfileTests() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, BadJsonString); - - await provider.UpdateProfilesAsyncTest("Docker"); - Assert.Single(provider.CurrentSnapshot.Profiles); - Assert.Equal(LaunchSettingsProvider.ErrorProfileCommandName, provider.CurrentSnapshot.ActiveProfile!.CommandName); - Assert.True(((IPersistOption)provider.CurrentSnapshot.ActiveProfile).DoNotPersist); - } + [Fact] + public async Task UpdateProfiles_BadJsonShouldLeaveProfilesStable() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + await provider.UpdateProfilesAsyncTest(null); + + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, BadJsonString); + await provider.UpdateProfilesAsyncTest("Docker"); + Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); + Assert.Empty(provider.CurrentSnapshot.GlobalSettings); + Assert.Equal("IIS Express", provider.CurrentSnapshot.ActiveProfile!.Name); + } - [Fact] - public async Task UpdateProfiles_MergeInMemoryProfiles() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + [Fact] + public async Task UpdateProfiles_SetsErrorProfileTests() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, BadJsonString); + + await provider.UpdateProfilesAsyncTest("Docker"); + Assert.Single(provider.CurrentSnapshot.Profiles); + Assert.Equal(LaunchSettingsProvider.ErrorProfileCommandName, provider.CurrentSnapshot.ActiveProfile!.CommandName); + Assert.True(((IPersistOption)provider.CurrentSnapshot.ActiveProfile).DoNotPersist); + } - var curProfiles = new Mock(); - curProfiles.Setup(m => m.Profiles).Returns(() => - { - return new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true, doNotPersist: true), - new LaunchProfile("InMemory1", null, doNotPersist: true), - new LaunchProfile("ShouldNotBeIncluded", LaunchSettingsProvider.ErrorProfileCommandName, doNotPersist: true) - }.ToImmutableList(); - }); - - provider.SetCurrentSnapshot(curProfiles.Object); - - await provider.UpdateProfilesAsyncTest(null); - Assert.Equal(5, provider.CurrentSnapshot.Profiles.Count); - Assert.Equal("InMemory1", provider.CurrentSnapshot.Profiles[1].Name); - Assert.True(provider.CurrentSnapshot.Profiles[1].IsInMemoryObject()); - Assert.False(provider.CurrentSnapshot.Profiles[0].IsInMemoryObject()); - } + [Fact] + public async Task UpdateProfiles_MergeInMemoryProfiles() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - [Fact] - public async Task UpdateProfiles_MergeInMemoryProfiles_AddProfileAtEnd() + var curProfiles = new Mock(); + curProfiles.Setup(m => m.Profiles).Returns(() => { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - - var curProfiles = new Mock(); - curProfiles.Setup(m => m.Profiles).Returns(() => + return new List() { - return new List - { - new LaunchProfile("profile1", "IISExpress", launchBrowser: true), - new LaunchProfile("profile2", "IISExpress", launchBrowser: true), - new LaunchProfile("profile3", "IISExpress", launchBrowser: true), - new LaunchProfile("profile4", "IISExpress", launchBrowser: true), - new LaunchProfile("profile5", "IISExpress", launchBrowser: true), - new LaunchProfile("InMemory1", null, doNotPersist: true) - }.ToImmutableList(); - }); - - provider.SetCurrentSnapshot(curProfiles.Object); - - await provider.UpdateProfilesAsyncTest(null); - Assert.Equal(5, provider.CurrentSnapshot.Profiles.Count); - Assert.Equal("InMemory1", provider.CurrentSnapshot.Profiles[provider.CurrentSnapshot.Profiles.Count - 1].Name); - Assert.True(provider.CurrentSnapshot.Profiles[provider.CurrentSnapshot.Profiles.Count - 1].IsInMemoryObject()); - } + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true, doNotPersist: true), + new LaunchProfile("InMemory1", null, doNotPersist: true), + new LaunchProfile("ShouldNotBeIncluded", LaunchSettingsProvider.ErrorProfileCommandName, doNotPersist: true) + }.ToImmutableList(); + }); + + provider.SetCurrentSnapshot(curProfiles.Object); + + await provider.UpdateProfilesAsyncTest(null); + Assert.Equal(5, provider.CurrentSnapshot.Profiles.Count); + Assert.Equal("InMemory1", provider.CurrentSnapshot.Profiles[1].Name); + Assert.True(provider.CurrentSnapshot.Profiles[1].IsInMemoryObject()); + Assert.False(provider.CurrentSnapshot.Profiles[0].IsInMemoryObject()); + } - [Fact] - public async Task UpdateProfiles_MergeInMemoryGlobalSettings() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); + [Fact] + public async Task UpdateProfiles_MergeInMemoryProfiles_AddProfileAtEnd() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - var curProfiles = new Mock(); - curProfiles.Setup(m => m.Profiles).Returns(() => - { - return new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true, doNotPersist: true), - new LaunchProfile("InMemory1", null, doNotPersist: true) - }.ToImmutableList(); - }); - curProfiles.Setup(m => m.GlobalSettings).Returns(() => + var curProfiles = new Mock(); + curProfiles.Setup(m => m.Profiles).Returns(() => + { + return new List { - return new Dictionary() - { - { "iisSettings", new IISSettingsData() { AnonymousAuthentication = true, DoNotPersist = true } }, - { "SomeSettings", new IISSettingsData() { AnonymousAuthentication = false, DoNotPersist = false } }, - { "InMemoryUnique", new IISSettingsData() { AnonymousAuthentication = false, DoNotPersist = true } }, - }.ToImmutableDictionary(); - }); - - provider.SetCurrentSnapshot(curProfiles.Object); - - await provider.UpdateProfilesAsyncTest(null); - Assert.Equal(2, provider.CurrentSnapshot.GlobalSettings.Count); - Assert.False(provider.CurrentSnapshot.GlobalSettings["iisSettings"].IsInMemoryObject()); - Assert.True(provider.CurrentSnapshot.GlobalSettings["InMemoryUnique"].IsInMemoryObject()); - } + new LaunchProfile("profile1", "IISExpress", launchBrowser: true), + new LaunchProfile("profile2", "IISExpress", launchBrowser: true), + new LaunchProfile("profile3", "IISExpress", launchBrowser: true), + new LaunchProfile("profile4", "IISExpress", launchBrowser: true), + new LaunchProfile("profile5", "IISExpress", launchBrowser: true), + new LaunchProfile("InMemory1", null, doNotPersist: true) + }.ToImmutableList(); + }); + + provider.SetCurrentSnapshot(curProfiles.Object); + + await provider.UpdateProfilesAsyncTest(null); + Assert.Equal(5, provider.CurrentSnapshot.Profiles.Count); + Assert.Equal("InMemory1", provider.CurrentSnapshot.Profiles[provider.CurrentSnapshot.Profiles.Count - 1].Name); + Assert.True(provider.CurrentSnapshot.Profiles[provider.CurrentSnapshot.Profiles.Count - 1].IsInMemoryObject()); + } - [Fact] - public async Task SettingsFileHasChangedTests() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - Assert.True(await provider.SettingsFileHasChangedAsyncTest()); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - - Assert.True(await provider.SettingsFileHasChangedAsyncTest()); - provider.LastSettingsFileSyncTimeTest = moqFS.GetLastFileWriteTimeOrMinValueUtc(provider.LaunchSettingsFile); - Assert.False(await provider.SettingsFileHasChangedAsyncTest()); - } + [Fact] + public async Task UpdateProfiles_MergeInMemoryGlobalSettings() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); - [Fact] - public async Task ReadProfilesFromDisk_NoFile() + var curProfiles = new Mock(); + curProfiles.Setup(m => m.Profiles).Returns(() => { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); - Assert.Empty(profiles); - Assert.Empty(globalSettings); - } - - [Fact] - public async Task ReadProfilesFromDisk_GoodFile() + return new List() + { + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true, doNotPersist: true), + new LaunchProfile("InMemory1", null, doNotPersist: true) + }.ToImmutableList(); + }); + curProfiles.Setup(m => m.GlobalSettings).Returns(() => { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + return new Dictionary() + { + { "iisSettings", new IISSettingsData() { AnonymousAuthentication = true, DoNotPersist = true } }, + { "SomeSettings", new IISSettingsData() { AnonymousAuthentication = false, DoNotPersist = false } }, + { "InMemoryUnique", new IISSettingsData() { AnonymousAuthentication = false, DoNotPersist = true } }, + }.ToImmutableDictionary(); + }); + + provider.SetCurrentSnapshot(curProfiles.Object); + + await provider.UpdateProfilesAsyncTest(null); + Assert.Equal(2, provider.CurrentSnapshot.GlobalSettings.Count); + Assert.False(provider.CurrentSnapshot.GlobalSettings["iisSettings"].IsInMemoryObject()); + Assert.True(provider.CurrentSnapshot.GlobalSettings["InMemoryUnique"].IsInMemoryObject()); + } - var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); - Assert.Equal(4, profiles.Length); - Assert.Empty(globalSettings); - } + [Fact] + public async Task SettingsFileHasChangedTests() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + Assert.True(await provider.SettingsFileHasChangedAsyncTest()); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + + Assert.True(await provider.SettingsFileHasChangedAsyncTest()); + provider.LastSettingsFileSyncTimeTest = moqFS.GetLastFileWriteTimeOrMinValueUtc(provider.LaunchSettingsFile); + Assert.False(await provider.SettingsFileHasChangedAsyncTest()); + } - [Fact] - public async Task ReadProfilesFromDisk_BadJsonFile() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, BadJsonString); + [Fact] + public async Task ReadProfilesFromDisk_NoFile() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); + Assert.Empty(profiles); + Assert.Empty(globalSettings); + } - await Assert.ThrowsAsync(provider.ReadSettingsFileFromDiskTestAsync); - } + [Fact] + public async Task ReadProfilesFromDisk_GoodFile() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - [Fact] - public async Task ReadProfilesFromDisk_JsonWithExtensionsNoProvider() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); + var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); + Assert.Equal(4, profiles.Length); + Assert.Empty(globalSettings); + } - var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); + [Fact] + public async Task ReadProfilesFromDisk_BadJsonFile() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, BadJsonString); - AssertEx.CollectionLength(profiles, 2); + await Assert.ThrowsAsync(provider.ReadSettingsFileFromDiskTestAsync); + } - var (name, value) = Assert.Single(globalSettings); - Assert.Equal("iisSettings", name); - var jObject = Assert.IsType(value); + [Fact] + public async Task ReadProfilesFromDisk_JsonWithExtensionsNoProvider() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); - Assert.True(jObject.GetValue("windowsAuthentication")?.Value()); - Assert.False(jObject.GetValue("anonymousAuthentication")?.Value()); - } + var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); - [Fact] - public async Task ReadProfilesFromDisk_JsonWithExtensionsWithProvider() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); - - // Set the serialization provider - SetJsonSerializationProviders(provider); - - var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); - - AssertEx.CollectionLength(profiles, 2); - - var (name, value) = Assert.Single(globalSettings); - Assert.Equal("iisSettings", name); - Assert.IsType(value); - } + AssertEx.CollectionLength(profiles, 2); - [Fact] - public async Task SaveProfilesToDiskTests() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") - }; + var (name, value) = Assert.Single(globalSettings); + Assert.Equal("iisSettings", name); + var jObject = Assert.IsType(value); - var testSettings = new Mock(); - testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); - testSettings.Setup(m => m.Profiles).Returns(() => + Assert.True(jObject.GetValue("windowsAuthentication")?.Value()); + Assert.False(jObject.GetValue("anonymousAuthentication")?.Value()); + } + + [Fact] + public async Task ReadProfilesFromDisk_JsonWithExtensionsWithProvider() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); + + // Set the serialization provider + SetJsonSerializationProviders(provider); + + var (profiles, globalSettings) = await provider.ReadSettingsFileFromDiskTestAsync(); + + AssertEx.CollectionLength(profiles, 2); + + var (name, value) = Assert.Single(globalSettings); + Assert.Equal("iisSettings", name); + Assert.IsType(value); + } + + [Fact] + public async Task SaveProfilesToDiskTests() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() { - return profiles.ToImmutableList(); - }); - testSettings.Setup(m => m.GlobalSettings).Returns(() => + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") + }; + + var testSettings = new Mock(); + testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); + testSettings.Setup(m => m.Profiles).Returns(() => + { + return profiles.ToImmutableList(); + }); + testSettings.Setup(m => m.GlobalSettings).Returns(() => + { + var iisSettings = new IISSettingsData() { - var iisSettings = new IISSettingsData() + AnonymousAuthentication = false, + WindowsAuthentication = true, + IISExpressBindingData = new ServerBindingData() { - AnonymousAuthentication = false, - WindowsAuthentication = true, - IISExpressBindingData = new ServerBindingData() - { - ApplicationUrl = "http://localhost:12345/", - SSLPort = 44301 - } - }; - return ImmutableStringDictionary.EmptyOrdinal.Add("iisSettings", iisSettings); - }); - - await provider.SaveSettingsToDiskAsyncTest(testSettings.Object); - - // Last Write time should be set - Assert.Equal(moqFS.GetLastFileWriteTimeOrMinValueUtc(provider.LaunchSettingsFile), provider.LastSettingsFileSyncTimeTest); - - // Check disk contents - Assert.Equal(JsonStringWithWebSettings, await moqFS.ReadAllTextAsync(provider.LaunchSettingsFile), ignoreLineEndingDifferences: true); - } + ApplicationUrl = "http://localhost:12345/", + SSLPort = 44301 + } + }; + return ImmutableStringDictionary.EmptyOrdinal.Add("iisSettings", iisSettings); + }); - [Fact] - public async Task LaunchSettingsFile_Changed() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - provider.SetNextVersionTest(123); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - // Wait for completion of task - await provider.LaunchSettingsFile_ChangedTest(); - - Assert.NotNull(provider.CurrentSnapshot); - Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); - Assert.Equal(123, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); - } + await provider.SaveSettingsToDiskAsyncTest(testSettings.Object); - [Fact] - public async Task LaunchSettingsFile_TestIgnoreFlag() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - string fileName = await provider.GetLaunchSettingsFilePathNoCacheAsync(); - // Write file and generate disk change - await moqFS.WriteAllTextAsync(fileName, JsonString1); - - // Set the ignore flag. It should be ignored. - provider.LastSettingsFileSyncTimeTest = DateTime.MinValue; - provider.SetIgnoreFileChanges(true); - Assert.Equal(provider.LaunchSettingsFile_ChangedTest(), Task.CompletedTask); - Assert.Null(provider.CurrentSnapshot); - - // Should run this time - provider.SetIgnoreFileChanges(false); - await provider.LaunchSettingsFile_ChangedTest(); - Assert.NotNull(provider.CurrentSnapshot); - Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); - } + // Last Write time should be set + Assert.Equal(moqFS.GetLastFileWriteTimeOrMinValueUtc(provider.LaunchSettingsFile), provider.LastSettingsFileSyncTimeTest); - [Fact] - public async Task LaunchSettingsFile_TestTimeStampFlag() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); - await provider.LaunchSettingsFile_ChangedTest(); - Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); - - // Write new file, but set the timestamp to match - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); - provider.LastSettingsFileSyncTimeTest = moqFS.GetLastFileWriteTimeOrMinValueUtc(provider.LaunchSettingsFile); - Assert.Equal(provider.LaunchSettingsFile_ChangedTest(), Task.CompletedTask); - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 4); - - await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); - await provider.LaunchSettingsFile_ChangedTest(); - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); - } + // Check disk contents + Assert.Equal(JsonStringWithWebSettings, await moqFS.ReadAllTextAsync(provider.LaunchSettingsFile), ignoreLineEndingDifferences: true); + } - [Fact] - public async Task Dispose_WhenNotActivated_DoesNotThrow() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); + [Fact] + public async Task LaunchSettingsFile_Changed() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + provider.SetNextVersionTest(123); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + // Wait for completion of task + await provider.LaunchSettingsFile_ChangedTest(); + + Assert.NotNull(provider.CurrentSnapshot); + Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); + Assert.Equal(123, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); + } + + [Fact] + public async Task LaunchSettingsFile_TestIgnoreFlag() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + string fileName = await provider.GetLaunchSettingsFilePathNoCacheAsync(); + // Write file and generate disk change + await moqFS.WriteAllTextAsync(fileName, JsonString1); + + // Set the ignore flag. It should be ignored. + provider.LastSettingsFileSyncTimeTest = DateTime.MinValue; + provider.SetIgnoreFileChanges(true); + Assert.Equal(provider.LaunchSettingsFile_ChangedTest(), Task.CompletedTask); + Assert.Null(provider.CurrentSnapshot); + + // Should run this time + provider.SetIgnoreFileChanges(false); + await provider.LaunchSettingsFile_ChangedTest(); + Assert.NotNull(provider.CurrentSnapshot); + Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); + } - await provider.DisposeAsync(); + [Fact] + public async Task LaunchSettingsFile_TestTimeStampFlag() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonString1); + await provider.LaunchSettingsFile_ChangedTest(); + Assert.Equal(4, provider.CurrentSnapshot.Profiles.Count); + + // Write new file, but set the timestamp to match + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); + provider.LastSettingsFileSyncTimeTest = moqFS.GetLastFileWriteTimeOrMinValueUtc(provider.LaunchSettingsFile); + Assert.Equal(provider.LaunchSettingsFile_ChangedTest(), Task.CompletedTask); + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 4); + + await moqFS.WriteAllTextAsync(provider.LaunchSettingsFile, JsonStringWithWebSettings); + await provider.LaunchSettingsFile_ChangedTest(); + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); + } - Assert.True(provider.IsDisposed); - } + [Fact] + public async Task Dispose_WhenNotActivated_DoesNotThrow() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); - [Fact] - public async Task UpdateAndSaveProfilesAsync() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") - }; + await provider.DisposeAsync(); - var testSettings = new Mock(); - testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); - testSettings.Setup(m => m.Profiles).Returns(() => + Assert.True(provider.IsDisposed); + } + + [Fact] + public async Task UpdateAndSaveProfilesAsync() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() { - return profiles.ToImmutableList(); - }); + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") + }; - testSettings.Setup(m => m.GlobalSettings).Returns(() => + var testSettings = new Mock(); + testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); + testSettings.Setup(m => m.Profiles).Returns(() => + { + return profiles.ToImmutableList(); + }); + + testSettings.Setup(m => m.GlobalSettings).Returns(() => + { + var iisSettings = new IISSettingsData() { - var iisSettings = new IISSettingsData() - { - AnonymousAuthentication = false, - WindowsAuthentication = true, - IISExpressBindingData = new ServerBindingData() - { - ApplicationUrl = "http://localhost:12345/", - SSLPort = 44301 - } - }; - return ImmutableStringDictionary.EmptyOrdinal.Add("iisSettings", iisSettings); - }); - - // Setup SCC to verify it is called before modifying the file - var mockScc = new Mock(MockBehavior.Strict); - mockScc.Setup(m => m.CanChangeProjectFilesAsync(It.IsAny>())).Returns(Task.FromResult(true)); - var sccProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) + AnonymousAuthentication = false, + WindowsAuthentication = true, + IISExpressBindingData = new ServerBindingData() { - mockScc.Object - }; - provider.SetSourceControlProviderCollection(sccProviders); - provider.SetNextVersionTest(123); + ApplicationUrl = "http://localhost:12345/", + SSLPort = 44301 + } + }; + return ImmutableStringDictionary.EmptyOrdinal.Add("iisSettings", iisSettings); + }); - await provider.UpdateAndSaveSettingsAsync(testSettings.Object); + // Setup SCC to verify it is called before modifying the file + var mockScc = new Mock(MockBehavior.Strict); + mockScc.Setup(m => m.CanChangeProjectFilesAsync(It.IsAny>())).Returns(Task.FromResult(true)); + var sccProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) + { + mockScc.Object + }; + provider.SetSourceControlProviderCollection(sccProviders); + provider.SetNextVersionTest(123); - // Check disk contents - Assert.Equal(JsonStringWithWebSettings, await moqFS.ReadAllTextAsync(provider.LaunchSettingsFile), ignoreLineEndingDifferences: true); + await provider.UpdateAndSaveSettingsAsync(testSettings.Object); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); - Assert.Single(provider.CurrentSnapshot.GlobalSettings); - Assert.Equal(123, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); + // Check disk contents + Assert.Equal(JsonStringWithWebSettings, await moqFS.ReadAllTextAsync(provider.LaunchSettingsFile), ignoreLineEndingDifferences: true); - // Verify the activeProfile is set to the first one since no existing snapshot - Assert.Equal("IIS Express", provider.CurrentSnapshot.ActiveProfile!.Name); + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); + Assert.Single(provider.CurrentSnapshot.GlobalSettings); + Assert.Equal(123, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); - mockScc.Verify(); - } + // Verify the activeProfile is set to the first one since no existing snapshot + Assert.Equal("IIS Express", provider.CurrentSnapshot.ActiveProfile!.Name); - [Fact] - public async Task UpdateAndSaveProfilesAsync_ActiveProfilePreserved() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS, "Properties", "bar"); - var existingSettings = new Mock(); - existingSettings.Setup(m => m.ActiveProfile).Returns(new LaunchProfile("bar", null)); - provider.SetCurrentSnapshot(existingSettings.Object); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") - }; + mockScc.Verify(); + } - var testSettings = new Mock(); - testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); - testSettings.Setup(m => m.Profiles).Returns(() => + [Fact] + public async Task UpdateAndSaveProfilesAsync_ActiveProfilePreserved() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS, "Properties", "bar"); + var existingSettings = new Mock(); + existingSettings.Setup(m => m.ActiveProfile).Returns(new LaunchProfile("bar", null)); + provider.SetCurrentSnapshot(existingSettings.Object); + var profiles = new List() { - return profiles.ToImmutableList(); - }); + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") + }; - testSettings.Setup(m => m.GlobalSettings).Returns(() => ImmutableStringDictionary.EmptyOrdinal); + var testSettings = new Mock(); + testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); + testSettings.Setup(m => m.Profiles).Returns(() => + { + return profiles.ToImmutableList(); + }); - await provider.UpdateAndSaveSettingsAsync(testSettings.Object); + testSettings.Setup(m => m.GlobalSettings).Returns(() => ImmutableStringDictionary.EmptyOrdinal); - // Verify the activeProfile hasn't changed - Assert.Equal("bar", provider.CurrentSnapshot.ActiveProfile!.Name); - } + await provider.UpdateAndSaveSettingsAsync(testSettings.Object); - [Fact] - public async Task UpdateAndSaveProfilesAsync_NoPersist() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS, "Properties", "bar"); - var existingSettings = new Mock(); - existingSettings.Setup(m => m.ActiveProfile).Returns(new LaunchProfile("bar", null)); - provider.SetCurrentSnapshot(existingSettings.Object); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") - }; + // Verify the activeProfile hasn't changed + Assert.Equal("bar", provider.CurrentSnapshot.ActiveProfile!.Name); + } - var testSettings = new Mock(); - testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); - testSettings.Setup(m => m.Profiles).Returns(() => + [Fact] + public async Task UpdateAndSaveProfilesAsync_NoPersist() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS, "Properties", "bar"); + var existingSettings = new Mock(); + existingSettings.Setup(m => m.ActiveProfile).Returns(new LaunchProfile("bar", null)); + provider.SetCurrentSnapshot(existingSettings.Object); + var profiles = new List() { - return profiles.ToImmutableList(); - }); + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") + }; - testSettings.Setup(m => m.GlobalSettings).Returns(() => ImmutableStringDictionary.EmptyOrdinal); + var testSettings = new Mock(); + testSettings.Setup(m => m.ActiveProfile).Returns(() => { return profiles[0]; }); + testSettings.Setup(m => m.Profiles).Returns(() => + { + return profiles.ToImmutableList(); + }); - var mockScc = new Mock(MockBehavior.Strict); - var sccProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) - { - mockScc.Object - }; - provider.SetSourceControlProviderCollection(sccProviders); + testSettings.Setup(m => m.GlobalSettings).Returns(() => ImmutableStringDictionary.EmptyOrdinal); - await provider.UpdateAndSaveSettingsInternalAsyncTest(testSettings.Object, persistToDisk: false); + var mockScc = new Mock(MockBehavior.Strict); + var sccProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) + { + mockScc.Object + }; + provider.SetSourceControlProviderCollection(sccProviders); - // Verifify the settings haven't been persisted and the sccProvider wasn't called to checkout the file - Assert.False(moqFS.FileExists(provider.LaunchSettingsFile)); - mockScc.Verify(); - } + await provider.UpdateAndSaveSettingsInternalAsyncTest(testSettings.Object, persistToDisk: false); - [Theory] - [InlineData(true, 0, false)] - [InlineData(false, 2, false)] - [InlineData(false, 2, true)] - public async Task AddOrUpdateProfileAsync_ProfileDoesntExist(bool addToFront, int expectedIndex, bool isInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") - }; + // Verifify the settings haven't been persisted and the sccProvider wasn't called to checkout the file + Assert.False(moqFS.FileExists(provider.LaunchSettingsFile)); + mockScc.Verify(); + } - var testSettings = new Mock(); - testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); + [Theory] + [InlineData(true, 0, false)] + [InlineData(false, 2, false)] + [InlineData(false, 2, true)] + public async Task AddOrUpdateProfileAsync_ProfileDoesntExist(bool addToFront, int expectedIndex, bool isInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() + { + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg") + }; - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + var testSettings = new Mock(); + testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); - var newProfile = new LaunchProfile("test", "Test", doNotPersist: isInMemory); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.AddOrUpdateProfileAsync(newProfile, addToFront); + var newProfile = new LaunchProfile("test", "Test", doNotPersist: isInMemory); - // Check disk file was written unless not in memory - Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); + await provider.AddOrUpdateProfileAsync(newProfile, addToFront); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 3); - Assert.Equal("Test", provider.CurrentSnapshot.Profiles[expectedIndex].CommandName); - Assert.Null(provider.CurrentSnapshot.Profiles[expectedIndex].ExecutablePath); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check disk file was written unless not in memory + Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); - [Theory] - [InlineData(true, 0, false, false)] - [InlineData(false, 1, false, false)] - [InlineData(false, 1, true, false)] - [InlineData(false, 1, true, true)] - public async Task AddOrUpdateProfileAsync_ProfileExists(bool addToFront, int expectedIndex, bool isInMemory, bool existingIsInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("test", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg", doNotPersist: existingIsInMemory), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") - }; + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 3); + Assert.Equal("Test", provider.CurrentSnapshot.Profiles[expectedIndex].CommandName); + Assert.Null(provider.CurrentSnapshot.Profiles[expectedIndex].ExecutablePath); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - var testSettings = new Mock(); - testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); + [Theory] + [InlineData(true, 0, false, false)] + [InlineData(false, 1, false, false)] + [InlineData(false, 1, true, false)] + [InlineData(false, 1, true, true)] + public async Task AddOrUpdateProfileAsync_ProfileExists(bool addToFront, int expectedIndex, bool isInMemory, bool existingIsInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() + { + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("test", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg", doNotPersist: existingIsInMemory), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") + }; - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + var testSettings = new Mock(); + testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); - var newProfile = new LaunchProfile("test", "Test", doNotPersist: isInMemory); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.AddOrUpdateProfileAsync(newProfile, addToFront); + var newProfile = new LaunchProfile("test", "Test", doNotPersist: isInMemory); - // Check disk file was written unless in memory profile - Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); + await provider.AddOrUpdateProfileAsync(newProfile, addToFront); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 3); - Assert.Equal("test", provider.CurrentSnapshot.Profiles[expectedIndex].Name); - Assert.Equal("Test", provider.CurrentSnapshot.Profiles[expectedIndex].CommandName); - Assert.Null(provider.CurrentSnapshot.Profiles[expectedIndex].ExecutablePath); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check disk file was written unless in memory profile + Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); - [Theory] - [InlineData(1, false, false)] - [InlineData(1, true, false)] - [InlineData(1, true, true)] - public async Task UpdateProfileAsync_ProfileExists(int expectedIndex, bool isInMemory, bool existingIsInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("test", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg", doNotPersist: existingIsInMemory), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") - }; + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 3); + Assert.Equal("test", provider.CurrentSnapshot.Profiles[expectedIndex].Name); + Assert.Equal("Test", provider.CurrentSnapshot.Profiles[expectedIndex].CommandName); + Assert.Null(provider.CurrentSnapshot.Profiles[expectedIndex].ExecutablePath); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - var testSettings = new Mock(); - testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); + [Theory] + [InlineData(1, false, false)] + [InlineData(1, true, false)] + [InlineData(1, true, true)] + public async Task UpdateProfileAsync_ProfileExists(int expectedIndex, bool isInMemory, bool existingIsInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() + { + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("test", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg", doNotPersist: existingIsInMemory), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") + }; - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + var testSettings = new Mock(); + testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); - var newProfile = new LaunchProfile("test", "Test", doNotPersist: isInMemory); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.TryUpdateProfileAsync("test", p => - { - p.CommandName = "Test"; - var persist = (IWritablePersistOption)p; - persist.DoNotPersist = isInMemory; - }); - - // Check disk file was written unless in memory profile - Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); - - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 3); - Assert.Equal("test", provider.CurrentSnapshot.Profiles[expectedIndex].Name); - Assert.Equal("Test", provider.CurrentSnapshot.Profiles[expectedIndex].CommandName); - Assert.Equal("c:\\test\\project\\bin\\test.exe", provider.CurrentSnapshot.Profiles[expectedIndex].ExecutablePath); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + var newProfile = new LaunchProfile("test", "Test", doNotPersist: isInMemory); - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task RemoveProfileAsync_ProfileExists(bool isInMemory) + await provider.TryUpdateProfileAsync("test", p => { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("test", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg", doNotPersist: isInMemory), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") - }; + p.CommandName = "Test"; + var persist = (IWritablePersistOption)p; + persist.DoNotPersist = isInMemory; + }); + + // Check disk file was written unless in memory profile + Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); + + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 3); + Assert.Equal("test", provider.CurrentSnapshot.Profiles[expectedIndex].Name); + Assert.Equal("Test", provider.CurrentSnapshot.Profiles[expectedIndex].CommandName); + Assert.Equal("c:\\test\\project\\bin\\test.exe", provider.CurrentSnapshot.Profiles[expectedIndex].ExecutablePath); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task RemoveProfileAsync_ProfileExists(bool isInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() + { + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("test", null, executablePath: "c:\\test\\project\\bin\\test.exe", commandLineArgs: "-someArg", doNotPersist: isInMemory), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") + }; - var testSettings = new Mock(); - testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); + var testSettings = new Mock(); + testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.RemoveProfileAsync("test"); + await provider.RemoveProfileAsync("test"); - // Check disk file was written - Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); + // Check disk file was written + Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); - Assert.Null(provider.CurrentSnapshot.Profiles.FirstOrDefault(p => p.Name!.Equals("test"))); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); + Assert.Null(provider.CurrentSnapshot.Profiles.FirstOrDefault(p => p.Name!.Equals("test"))); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - [Fact] - public async Task RemoveProfileAsync_ProfileDoesntExists() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - var profiles = new List() - { - new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), - new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") - }; + [Fact] + public async Task RemoveProfileAsync_ProfileDoesntExists() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + var profiles = new List() + { + new LaunchProfile("IIS Express", "IISExpress", launchBrowser: true), + new LaunchProfile("bar", null, executablePath: "c:\\test\\project\\bin\\bar.exe") + }; - var testSettings = new Mock(); - testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); - var versionedTestSettings = testSettings.As(); - versionedTestSettings.Setup(m => m.Version).Returns(42); + var testSettings = new Mock(); + testSettings.Setup(m => m.Profiles).Returns(profiles.ToImmutableList()); + var versionedTestSettings = testSettings.As(); + versionedTestSettings.Setup(m => m.Version).Returns(42); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.RemoveProfileAsync("test"); + await provider.RemoveProfileAsync("test"); - // Check disk file was not written - Assert.False(moqFS.FileExists(provider.LaunchSettingsFile)); + // Check disk file was not written + Assert.False(moqFS.FileExists(provider.LaunchSettingsFile)); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); - Assert.Equal(42, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); - } + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.Profiles, 2); + Assert.Equal(42, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); + } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task AddOrUpdateGlobalSettingAsync_SettingDoesntExist(bool isInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - SetJsonSerializationProviders(provider); + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task AddOrUpdateGlobalSettingAsync_SettingDoesntExist(bool isInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + SetJsonSerializationProviders(provider); - var globalSettings = ImmutableStringDictionary.EmptyOrdinal.Add("test", new LaunchProfile(null, null)); + var globalSettings = ImmutableStringDictionary.EmptyOrdinal.Add("test", new LaunchProfile(null, null)); - var testSettings = new Mock(); - testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); - testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); + var testSettings = new Mock(); + testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); + testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; + var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; - await provider.AddOrUpdateGlobalSettingAsync("iisSettings", newSettings); + await provider.AddOrUpdateGlobalSettingAsync("iisSettings", newSettings); - // Check disk file was written - Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); - AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); + // Check disk file was written + Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); + AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); - // Check snapshot - Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); - Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check snapshot + Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); + Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task UpdateGlobalSettingAsync_SettingDoesntExist(bool isInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - SetJsonSerializationProviders(provider); + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task UpdateGlobalSettingAsync_SettingDoesntExist(bool isInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + SetJsonSerializationProviders(provider); - var globalSettings = ImmutableStringDictionary.EmptyOrdinal.Add("test", new LaunchProfile(null, null)); + var globalSettings = ImmutableStringDictionary.EmptyOrdinal.Add("test", new LaunchProfile(null, null)); - var testSettings = new Mock(); - testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); - testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); + var testSettings = new Mock(); + testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); + testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; + var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; - await provider.UpdateGlobalSettingsAsync(existing => { - var updates = ImmutableDictionary.Empty - .Add("iisSettings", newSettings); - return updates; - }); + await provider.UpdateGlobalSettingsAsync(existing => { + var updates = ImmutableDictionary.Empty + .Add("iisSettings", newSettings); + return updates; + }); - // Check disk file was written - Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); - AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); + // Check disk file was written + Assert.Equal(!isInMemory, moqFS.FileExists(provider.LaunchSettingsFile)); + AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); - // Check snapshot - Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); - Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check snapshot + Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); + Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - [Theory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - public async Task AddOrUpdateGlobalSettingAsync_SettingExists(bool isInMemory, bool existingIsInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - SetJsonSerializationProviders(provider); + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public async Task AddOrUpdateGlobalSettingAsync_SettingExists(bool isInMemory, bool existingIsInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + SetJsonSerializationProviders(provider); - var globalSettings = ImmutableStringDictionary.EmptyOrdinal - .Add("test", new LaunchProfile(null, null)) - .Add("iisSettings", new IISSettingsData() { DoNotPersist = existingIsInMemory }); + var globalSettings = ImmutableStringDictionary.EmptyOrdinal + .Add("test", new LaunchProfile(null, null)) + .Add("iisSettings", new IISSettingsData() { DoNotPersist = existingIsInMemory }); - var testSettings = new Mock(); - testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); - testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); + var testSettings = new Mock(); + testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); + testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; + var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; - await provider.AddOrUpdateGlobalSettingAsync("iisSettings", newSettings); + await provider.AddOrUpdateGlobalSettingAsync("iisSettings", newSettings); - // Check disk file was written - Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); + // Check disk file was written + Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); - Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); - Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); + Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); + Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - [Theory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - public async Task UpdateGlobalSettingAsync_SettingExists(bool isInMemory, bool existingIsInMemory) - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - SetJsonSerializationProviders(provider); + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public async Task UpdateGlobalSettingAsync_SettingExists(bool isInMemory, bool existingIsInMemory) + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + SetJsonSerializationProviders(provider); - var globalSettings = ImmutableStringDictionary.EmptyOrdinal - .Add("test", new LaunchProfile(null, null)) - .Add("iisSettings", new IISSettingsData() { DoNotPersist = existingIsInMemory }); + var globalSettings = ImmutableStringDictionary.EmptyOrdinal + .Add("test", new LaunchProfile(null, null)) + .Add("iisSettings", new IISSettingsData() { DoNotPersist = existingIsInMemory }); - var testSettings = new Mock(); - testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); - testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); + var testSettings = new Mock(); + testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); + testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; + var newSettings = new IISSettingsData() { WindowsAuthentication = true, DoNotPersist = isInMemory }; - await provider.UpdateGlobalSettingsAsync(existing => { - var updates = ImmutableDictionary.Empty - .Add("iisSettings", newSettings); - return updates; - }); + await provider.UpdateGlobalSettingsAsync(existing => { + var updates = ImmutableDictionary.Empty + .Add("iisSettings", newSettings); + return updates; + }); - // Check disk file was written - Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); + // Check disk file was written + Assert.Equal(!isInMemory || (isInMemory && !existingIsInMemory), moqFS.FileExists(provider.LaunchSettingsFile)); - // Check snapshot - AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); - Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); - Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check snapshot + AssertEx.CollectionLength(provider.CurrentSnapshot.GlobalSettings, 2); + Assert.True(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out object? updatedSettings)); + Assert.True(((IISSettingsData)updatedSettings).WindowsAuthentication); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - [Fact] - public async Task RemoveGlobalSettingAsync_SettingDoesntExist() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - SetJsonSerializationProviders(provider); + [Fact] + public async Task RemoveGlobalSettingAsync_SettingDoesntExist() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + SetJsonSerializationProviders(provider); - var globalSettings = ImmutableStringDictionary.EmptyOrdinal.Add("test", new LaunchProfile(null, null)); + var globalSettings = ImmutableStringDictionary.EmptyOrdinal.Add("test", new LaunchProfile(null, null)); - var testSettings = new Mock(); - testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); - testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); - var versionedTestSettings = testSettings.As(); - versionedTestSettings.Setup(m => m.Version).Returns(42); + var testSettings = new Mock(); + testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); + testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); + var versionedTestSettings = testSettings.As(); + versionedTestSettings.Setup(m => m.Version).Returns(42); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.RemoveGlobalSettingAsync("iisSettings"); + await provider.RemoveGlobalSettingAsync("iisSettings"); - // Check disk file was not written - Assert.False(moqFS.FileExists(provider.LaunchSettingsFile)); + // Check disk file was not written + Assert.False(moqFS.FileExists(provider.LaunchSettingsFile)); - // Check snapshot - Assert.Single(provider.CurrentSnapshot.GlobalSettings); - Assert.Equal(42, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); - } + // Check snapshot + Assert.Single(provider.CurrentSnapshot.GlobalSettings); + Assert.Equal(42, ((IVersionedLaunchSettings)provider.CurrentSnapshot).Version); + } - [Fact] - public async Task RemoveGlobalSettingAsync_SettingExists() - { - var moqFS = new IFileSystemMock(); - using var provider = GetLaunchSettingsProvider(moqFS); - SetJsonSerializationProviders(provider); + [Fact] + public async Task RemoveGlobalSettingAsync_SettingExists() + { + var moqFS = new IFileSystemMock(); + using var provider = GetLaunchSettingsProvider(moqFS); + SetJsonSerializationProviders(provider); - var globalSettings = ImmutableStringDictionary.EmptyOrdinal - .Add("test", new LaunchProfile(null, null)) - .Add("iisSettings", new IISSettingsData()); + var globalSettings = ImmutableStringDictionary.EmptyOrdinal + .Add("test", new LaunchProfile(null, null)) + .Add("iisSettings", new IISSettingsData()); - var testSettings = new Mock(); - testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); - testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); + var testSettings = new Mock(); + testSettings.Setup(m => m.GlobalSettings).Returns(globalSettings); + testSettings.Setup(m => m.Profiles).Returns(ImmutableList.Empty); - provider.SetCurrentSnapshot(testSettings.Object); - provider.SetNextVersionTest(123); + provider.SetCurrentSnapshot(testSettings.Object); + provider.SetNextVersionTest(123); - await provider.RemoveGlobalSettingAsync("iisSettings"); + await provider.RemoveGlobalSettingAsync("iisSettings"); - // Check disk file was written - Assert.True(moqFS.FileExists(provider.LaunchSettingsFile)); + // Check disk file was written + Assert.True(moqFS.FileExists(provider.LaunchSettingsFile)); - // Check snapshot - Assert.Single(provider.CurrentSnapshot.GlobalSettings); - Assert.False(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out _)); - Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); - } + // Check snapshot + Assert.Single(provider.CurrentSnapshot.GlobalSettings); + Assert.False(provider.CurrentSnapshot.GlobalSettings.TryGetValue("iisSettings", out _)); + Assert.True(((IVersionedLaunchSettings)provider.CurrentSnapshot).Version >= 123); + } - private readonly string JsonString1 = - """ + private readonly string JsonString1 = + """ + { + "profiles": { + "IIS Express": { - "profiles": { - "IIS Express": - { - "commandName": "IISExpress", - "launchUrl": "http://localhost:1234:/test.html", - "launchBrowser": true - }, - "HasCustomValues": - { - "executablePath": "c:\\test\\project\\bin\\project.exe", - "workingDirectory": "c:\\test\\project", - "commandLineArgs": "--arg1 --arg2", - "custom1": true, - "custom2": 124, - "custom3": "mycustomVal" - }, - "Docker": - { - "commandName": "Docker", - "launchBrowser": false, - "dockerOption1": "some option in docker", - "dockerOption2": "Another option in docker" - }, - "web": - { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNET_ENVIRONMENT": "Development", - "ASPNET_APPLICATIONBASE": "c:\\Users\\billhie\\Documents\\projects\\WebApplication8\\src\\WebApplication8" - } - } - } - } - """; - - private readonly string JsonStringWithWebSettings = - """ + "commandName": "IISExpress", + "launchUrl": "http://localhost:1234:/test.html", + "launchBrowser": true + }, + "HasCustomValues": { - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true - }, - "bar": { - "executablePath": "c:\\test\\project\\bin\\test.exe", - "commandLineArgs": "-someArg" - } - }, - "iisSettings": { - "windowsAuthentication": true, - "anonymousAuthentication": false, - "iisExpress": { - "applicationUrl": "http://localhost:12345/", - "sslPort": 44301 - } - } - } - """; - - private readonly string BadJsonString = - """ + "executablePath": "c:\\test\\project\\bin\\project.exe", + "workingDirectory": "c:\\test\\project", + "commandLineArgs": "--arg1 --arg2", + "custom1": true, + "custom2": 124, + "custom3": "mycustomVal" + }, + "Docker": { - "profiles": { - { - "name": "IIS Express", - "launchBrowser": "True" - }, - }, - { - "Name": "bar", - "launchBrowser": "False" - } + "commandName": "Docker", + "launchBrowser": false, + "dockerOption1": "some option in docker", + "dockerOption2": "Another option in docker" + }, + "web": + { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNET_ENVIRONMENT": "Development", + "ASPNET_APPLICATIONBASE": "c:\\Users\\billhie\\Documents\\projects\\WebApplication8\\src\\WebApplication8" } } - """; - } - - // Derives from base class to be able to set protected members - internal class LaunchSettingsUnderTest : LaunchSettingsProvider - { - public LaunchSettingsUnderTest( - UnconfiguredProject project, - IUnconfiguredProjectServices projectServices, - IFileSystem fileSystem, - IUnconfiguredProjectCommonServices commonProjectServices, - IActiveConfiguredProjectSubscriptionService? projectSubscriptionService, - IActiveConfiguredValue projectProperties, - IProjectFaultHandlerService projectFaultHandler, - IDefaultLaunchProfileProvider defaultLaunchProfileProvider, - JoinableTaskContext joinableTaskContext) - : base(project, projectServices, fileSystem, commonProjectServices, projectSubscriptionService, projectProperties, projectFaultHandler, joinableTaskContext) - { - // Block the code from setting up one on the real file system. Since we block, it we need to set up the fileChange scheduler manually - FileWatcher = new SimpleFileWatcher(); - // Make the unit tests run faster - FileChangeProcessingDelay = TimeSpan.FromMilliseconds(50); - FileChangeScheduler = new TaskDelayScheduler(FileChangeProcessingDelay, commonProjectServices.ThreadingService, - CancellationToken.None); - DefaultLaunchProfileProviders.Add(defaultLaunchProfileProvider); + } } + """; - // Wrappers to call protected members - public void SetCurrentSnapshot(ILaunchSettings profiles) { CurrentSnapshot = profiles; } - public Task<(ImmutableArray Profiles, ImmutableArray<(string Name, object Value)> GlobalSettings)> ReadSettingsFileFromDiskTestAsync() { return ReadSettingsFileFromDiskAsync(); } - public Task SaveSettingsToDiskAsyncTest(ILaunchSettings curSettings) { return SaveSettingsToDiskAsync(curSettings); } - public Task UpdateAndSaveSettingsInternalAsyncTest(ILaunchSettings curSettings, bool persistToDisk) { return UpdateAndSaveSettingsInternalAsync(curSettings, persistToDisk); } - public void SetNextVersionTest(long nextVersion) { SetNextVersion(nextVersion); } - - public DateTime LastSettingsFileSyncTimeTest { get { return LastSettingsFileSyncTimeUtc; } set { LastSettingsFileSyncTimeUtc = value; } } - public Task UpdateProfilesAsyncTest(string? activeProfile) { return UpdateProfilesAsync(activeProfile); } - public void SetIgnoreFileChanges(bool value) { IgnoreFileChanges = value; } - public Task SettingsFileHasChangedAsyncTest() { return SettingsFileHasChangedAsync(); } - public Task LaunchSettingsFile_ChangedTest() => HandleLaunchSettingsFileChangedAsync(); - - internal void SetSettingsProviderCollection(OrderPrecedenceImportCollection settingsProviders) + private readonly string JsonStringWithWebSettings = + """ { - JsonSerializationProviders = settingsProviders; + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true + }, + "bar": { + "executablePath": "c:\\test\\project\\bin\\test.exe", + "commandLineArgs": "-someArg" + } + }, + "iisSettings": { + "windowsAuthentication": true, + "anonymousAuthentication": false, + "iisExpress": { + "applicationUrl": "http://localhost:12345/", + "sslPort": 44301 + } + } } + """; - internal void SetSourceControlProviderCollection(OrderPrecedenceImportCollection sccProviders) + private readonly string BadJsonString = + """ { - SourceControlIntegrations = sccProviders; + "profiles": { + { + "name": "IIS Express", + "launchBrowser": "True" + }, + }, + { + "Name": "bar", + "launchBrowser": "False" + } + } } - } + """; +} - // Used to test global settings - [JsonObject(MemberSerialization.OptIn)] - internal class ServerBindingData +// Derives from base class to be able to set protected members +internal class LaunchSettingsUnderTest : LaunchSettingsProvider +{ + public LaunchSettingsUnderTest( + UnconfiguredProject project, + IUnconfiguredProjectServices projectServices, + IFileSystem fileSystem, + IUnconfiguredProjectCommonServices commonProjectServices, + IActiveConfiguredProjectSubscriptionService? projectSubscriptionService, + IActiveConfiguredValue projectProperties, + IProjectFaultHandlerService projectFaultHandler, + IDefaultLaunchProfileProvider defaultLaunchProfileProvider, + JoinableTaskContext joinableTaskContext) + : base(project, projectServices, fileSystem, commonProjectServices, projectSubscriptionService, projectProperties, projectFaultHandler, joinableTaskContext) { - [JsonProperty(PropertyName = "applicationUrl")] - public string? ApplicationUrl { get; set; } + // Block the code from setting up one on the real file system. Since we block, it we need to set up the fileChange scheduler manually + FileWatcher = new SimpleFileWatcher(); + // Make the unit tests run faster + FileChangeProcessingDelay = TimeSpan.FromMilliseconds(50); + FileChangeScheduler = new TaskDelayScheduler(FileChangeProcessingDelay, commonProjectServices.ThreadingService, + CancellationToken.None); + DefaultLaunchProfileProviders.Add(defaultLaunchProfileProvider); + } - [JsonProperty(PropertyName = "sslPort")] - public int SSLPort { get; set; } + // Wrappers to call protected members + public void SetCurrentSnapshot(ILaunchSettings profiles) { CurrentSnapshot = profiles; } + public Task<(ImmutableArray Profiles, ImmutableArray<(string Name, object Value)> GlobalSettings)> ReadSettingsFileFromDiskTestAsync() { return ReadSettingsFileFromDiskAsync(); } + public Task SaveSettingsToDiskAsyncTest(ILaunchSettings curSettings) { return SaveSettingsToDiskAsync(curSettings); } + public Task UpdateAndSaveSettingsInternalAsyncTest(ILaunchSettings curSettings, bool persistToDisk) { return UpdateAndSaveSettingsInternalAsync(curSettings, persistToDisk); } + public void SetNextVersionTest(long nextVersion) { SetNextVersion(nextVersion); } + + public DateTime LastSettingsFileSyncTimeTest { get { return LastSettingsFileSyncTimeUtc; } set { LastSettingsFileSyncTimeUtc = value; } } + public Task UpdateProfilesAsyncTest(string? activeProfile) { return UpdateProfilesAsync(activeProfile); } + public void SetIgnoreFileChanges(bool value) { IgnoreFileChanges = value; } + public Task SettingsFileHasChangedAsyncTest() { return SettingsFileHasChangedAsync(); } + public Task LaunchSettingsFile_ChangedTest() => HandleLaunchSettingsFileChangedAsync(); + + internal void SetSettingsProviderCollection(OrderPrecedenceImportCollection settingsProviders) + { + JsonSerializationProviders = settingsProviders; } - [JsonObject(MemberSerialization.OptIn)] - internal class IISSettingsData : IPersistOption + internal void SetSourceControlProviderCollection(OrderPrecedenceImportCollection sccProviders) { - public const bool DefaultAnonymousAuth = true; - public const bool DefaultWindowsAuth = false; + SourceControlIntegrations = sccProviders; + } +} + +// Used to test global settings +[JsonObject(MemberSerialization.OptIn)] +internal class ServerBindingData +{ + [JsonProperty(PropertyName = "applicationUrl")] + public string? ApplicationUrl { get; set; } + + [JsonProperty(PropertyName = "sslPort")] + public int SSLPort { get; set; } +} + +[JsonObject(MemberSerialization.OptIn)] +internal class IISSettingsData : IPersistOption +{ + public const bool DefaultAnonymousAuth = true; + public const bool DefaultWindowsAuth = false; - [JsonProperty(PropertyName = "windowsAuthentication")] - public bool WindowsAuthentication { get; set; } = DefaultWindowsAuth; + [JsonProperty(PropertyName = "windowsAuthentication")] + public bool WindowsAuthentication { get; set; } = DefaultWindowsAuth; - [JsonProperty(PropertyName = "anonymousAuthentication")] - public bool AnonymousAuthentication { get; set; } = DefaultAnonymousAuth; + [JsonProperty(PropertyName = "anonymousAuthentication")] + public bool AnonymousAuthentication { get; set; } = DefaultAnonymousAuth; - [JsonProperty(PropertyName = "iis")] - public ServerBindingData? IISBindingData { get; set; } + [JsonProperty(PropertyName = "iis")] + public ServerBindingData? IISBindingData { get; set; } - [JsonProperty(PropertyName = "iisExpress")] - public ServerBindingData? IISExpressBindingData { get; set; } + [JsonProperty(PropertyName = "iisExpress")] + public ServerBindingData? IISExpressBindingData { get; set; } - public bool DoNotPersist { get; set; } - } + public bool DoNotPersist { get; set; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsTests.cs index 70aaeaebc8..d7f0555f67 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/LaunchSettingsTests.cs @@ -1,37 +1,36 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public class LaunchSettingsTests { - public class LaunchSettingsTests + [Fact] + public void LaunchSettings_CtorTests() { - [Fact] - public void LaunchSettings_CtorTests() + var profiles = new[] { - var profiles = new[] - { - new LaunchProfile("abc", null, commandLineArgs: "test"), - new LaunchProfile("def", null), - new LaunchProfile("ghi", null), - new LaunchProfile("foo", null), - }; + new LaunchProfile("abc", null, commandLineArgs: "test"), + new LaunchProfile("def", null), + new LaunchProfile("ghi", null), + new LaunchProfile("foo", null), + }; - var globals = ImmutableDictionary.Empty - .Add("var1", true) - .Add("var2", "some string"); + var globals = ImmutableDictionary.Empty + .Add("var1", true) + .Add("var2", "some string"); - var settings = new LaunchSettings(profiles); - Assert.NotNull(settings.ActiveProfile); - Assert.True(settings.ActiveProfile.Name == "abc"); - Assert.Equal(profiles.Length, settings.Profiles.Count); - Assert.Empty(settings.GlobalSettings); + var settings = new LaunchSettings(profiles); + Assert.NotNull(settings.ActiveProfile); + Assert.True(settings.ActiveProfile.Name == "abc"); + Assert.Equal(profiles.Length, settings.Profiles.Count); + Assert.Empty(settings.GlobalSettings); - settings = new LaunchSettings(profiles, activeProfileName: "ghi"); - Assert.NotNull(settings.ActiveProfile); - Assert.True(settings.ActiveProfile.Name == "ghi"); + settings = new LaunchSettings(profiles, activeProfileName: "ghi"); + Assert.NotNull(settings.ActiveProfile); + Assert.True(settings.ActiveProfile.Name == "ghi"); - // Test - settings = new LaunchSettings(profiles, globals, activeProfileName: "foo"); - Assert.Equal(globals.Count, settings.GlobalSettings.Count); - } + // Test + settings = new LaunchSettings(profiles, globals, activeProfileName: "foo"); + Assert.Equal(globals.Count, settings.GlobalSettings.Count); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/OutputTypeCheckerTest.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/OutputTypeCheckerTest.cs index 9e3f0b77da..58d464f220 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/OutputTypeCheckerTest.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Debug/OutputTypeCheckerTest.cs @@ -3,81 +3,80 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public class OutputTypeCheckerTest { - public class OutputTypeCheckerTest + private readonly string _ProjectFile = @"c:\test\project\project.csproj"; + + [Fact] + public async Task OutputTypeChecker_False_IsLibraryAsyncWhenEvaluationFails() { - private readonly string _ProjectFile = @"c:\test\project\project.csproj"; + OutputTypeChecker outputTypeChecker = CreateFailedOutputTypeChecker(); - [Fact] - public async Task OutputTypeChecker_False_IsLibraryAsyncWhenEvaluationFails() - { - OutputTypeChecker outputTypeChecker = CreateFailedOutputTypeChecker(); + Assert.False(await outputTypeChecker.IsLibraryAsync()); + } - Assert.False(await outputTypeChecker.IsLibraryAsync()); - } + [Fact] + public async Task OutputTypeChecker_True_IsLibraryAsync() + { + OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Library); - [Fact] - public async Task OutputTypeChecker_True_IsLibraryAsync() - { - OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Library); + Assert.True(await outputTypeChecker.IsLibraryAsync()); + } - Assert.True(await outputTypeChecker.IsLibraryAsync()); - } + [Fact] + public async Task OutputTypeChecker_False_IsLibraryAsync() + { + OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Exe); - [Fact] - public async Task OutputTypeChecker_False_IsLibraryAsync() - { - OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Exe); + Assert.False(await outputTypeChecker.IsLibraryAsync()); + } - Assert.False(await outputTypeChecker.IsLibraryAsync()); - } + [Fact] + public async Task OutputTypeChecker_True_IsConsoleAsync() + { + OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Exe); - [Fact] - public async Task OutputTypeChecker_True_IsConsoleAsync() - { - OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Exe); + Assert.True(await outputTypeChecker.IsConsoleAsync()); + } - Assert.True(await outputTypeChecker.IsConsoleAsync()); - } + [Fact] + public async Task OutputTypeChecker_False_IsConsoleAsync() + { + OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Library); - [Fact] - public async Task OutputTypeChecker_False_IsConsoleAsync() - { - OutputTypeChecker outputTypeChecker = CreateOutputTypeChecker(ConfigurationGeneral.OutputTypeValues.Library); + Assert.False(await outputTypeChecker.IsConsoleAsync()); + } - Assert.False(await outputTypeChecker.IsConsoleAsync()); - } + private OutputTypeChecker CreateFailedOutputTypeChecker() + { + var projectProperties = ProjectPropertiesFactory.CreateEmpty(); - private OutputTypeChecker CreateFailedOutputTypeChecker() - { - var projectProperties = ProjectPropertiesFactory.CreateEmpty(); + return new OutputTypeChecker2(projectProperties); + } - return new OutputTypeChecker2(projectProperties); - } + private OutputTypeChecker CreateOutputTypeChecker(string outputType) + { + var project = UnconfiguredProjectFactory.Create(fullPath: _ProjectFile); - private OutputTypeChecker CreateOutputTypeChecker(string outputType) - { - var project = UnconfiguredProjectFactory.Create(fullPath: _ProjectFile); + var outputTypeEnum = new PageEnumValue(new EnumValue { Name = outputType }); + var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, outputTypeEnum); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var outputTypeEnum = new PageEnumValue(new EnumValue { Name = outputType }); - var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, outputTypeEnum); - var projectProperties = ProjectPropertiesFactory.Create(project, data); + return new OutputTypeChecker(projectProperties); + } - return new OutputTypeChecker(projectProperties); + internal class OutputTypeChecker2 : OutputTypeChecker + { + public OutputTypeChecker2(ProjectProperties properties) : base(properties) + { } - internal class OutputTypeChecker2 : OutputTypeChecker + public override Task GetEvaluatedOutputTypeAsync() { - public OutputTypeChecker2(ProjectProperties properties) : base(properties) - { - } - - public override Task GetEvaluatedOutputTypeAsync() - { - // Evaluation fails - return Task.FromResult(null); - } + // Evaluation fails + return Task.FromResult(null); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FaultExtensionTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FaultExtensionTests.cs index fa386b0e7e..667fc0daa4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FaultExtensionTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FaultExtensionTests.cs @@ -2,37 +2,36 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class FaultExtensionTests { - public class FaultExtensionTests + [Fact] + public async Task RegisterFaultHandler_WhenBlockThrows_ReportsFault() { - [Fact] - public async Task RegisterFaultHandler_WhenBlockThrows_ReportsFault() - { - Exception? result = null; - var faultHandlerService = IProjectFaultHandlerServiceFactory.ImplementHandleFaultAsync((ex, reportSettings, severity, project) => { result = ex; }); - var thrownException = new Exception(message: "Test"); + Exception? result = null; + var faultHandlerService = IProjectFaultHandlerServiceFactory.ImplementHandleFaultAsync((ex, reportSettings, severity, project) => { result = ex; }); + var thrownException = new Exception(message: "Test"); - var block = DataflowBlockSlim.CreateActionBlock(value => - { - throw thrownException; - }); + var block = DataflowBlockSlim.CreateActionBlock(value => + { + throw thrownException; + }); - var faultTask = faultHandlerService.RegisterFaultHandlerAsync(block, project: null); + var faultTask = faultHandlerService.RegisterFaultHandlerAsync(block, project: null); - await block.SendAsync("Hello"); + await block.SendAsync("Hello"); - await faultTask; + await faultTask; - Assert.NotNull(result); + Assert.NotNull(result); - // We don't want to assert the exact exception message as an AggregateException may append further text - // to the end. - Assert.StartsWith( - $"Project system data flow 'DataflowBlockSlim (ActionBlockSlimAsync`1 : {block.GetHashCode()})' closed because of an exception: Test.", - result.Message); + // We don't want to assert the exact exception message as an AggregateException may append further text + // to the end. + Assert.StartsWith( + $"Project system data flow 'DataflowBlockSlim (ActionBlockSlimAsync`1 : {block.GetHashCode()})' closed because of an exception: Test.", + result.Message); - Assert.Same(thrownException, result.GetBaseException()); - } + Assert.Same(thrownException, result.GetBaseException()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FileItemServicesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FileItemServicesTests.cs index 1a5be0fd80..57309bb10b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FileItemServicesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/FileItemServicesTests.cs @@ -1,91 +1,90 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class FileItemServicesTests { - public class FileItemServicesTests + [Fact] + public void GetLogicalFolderNames_NullAsBasePath_ThrowsArgumentNull() { - [Fact] - public void GetLogicalFolderNames_NullAsBasePath_ThrowsArgumentNull() + Assert.Throws("basePath", () => { - Assert.Throws("basePath", () => - { - FileItemServices.GetLogicalFolderNames(null!, "fullPath", ImmutableDictionary.Empty); - }); - } + FileItemServices.GetLogicalFolderNames(null!, "fullPath", ImmutableDictionary.Empty); + }); + } - [Fact] - public void GetLogicalFolderNames_NullAsFullPath_ThrowsArgumentNull() + [Fact] + public void GetLogicalFolderNames_NullAsFullPath_ThrowsArgumentNull() + { + Assert.Throws("fullPath", () => { - Assert.Throws("fullPath", () => - { - FileItemServices.GetLogicalFolderNames("basePath", null!, ImmutableDictionary.Empty); - }); - } + FileItemServices.GetLogicalFolderNames("basePath", null!, ImmutableDictionary.Empty); + }); + } - [Fact] - public void GetLogicalFolderNames_NullAsMetadata_ThrowsArgumentNull() + [Fact] + public void GetLogicalFolderNames_NullAsMetadata_ThrowsArgumentNull() + { + Assert.Throws("metadata", () => { - Assert.Throws("metadata", () => - { - FileItemServices.GetLogicalFolderNames("basePath", "fullPath", null!); - }); - } + FileItemServices.GetLogicalFolderNames("basePath", "fullPath", null!); + }); + } - [Fact] - public void GetLogicalFolderNames_EmptyAsBasePath_ThrowsArgument() + [Fact] + public void GetLogicalFolderNames_EmptyAsBasePath_ThrowsArgument() + { + Assert.Throws("basePath", () => { - Assert.Throws("basePath", () => - { - FileItemServices.GetLogicalFolderNames(string.Empty, "fullPath", ImmutableDictionary.Empty); - }); - } + FileItemServices.GetLogicalFolderNames(string.Empty, "fullPath", ImmutableDictionary.Empty); + }); + } - [Fact] - public void GetLogicalFolderNames_EmptyAsFullPath_ThrowsArgument() + [Fact] + public void GetLogicalFolderNames_EmptyAsFullPath_ThrowsArgument() + { + Assert.Throws("fullPath", () => { - Assert.Throws("fullPath", () => - { - FileItemServices.GetLogicalFolderNames("basePath", string.Empty, ImmutableDictionary.Empty); - }); - } + FileItemServices.GetLogicalFolderNames("basePath", string.Empty, ImmutableDictionary.Empty); + }); + } - [Theory] // BasePath FullPath Link Expected - [InlineData("C:\\Project", "C:\\Project\\Source.cs", null, null)] - [InlineData("C:\\Project", "C:\\Project\\Source.cs", "", null)] - [InlineData("C:\\Project", "C:\\Project\\Folder\\Source.cs", "", "Folder")] - [InlineData("C:\\Project", "C:\\Project\\Folder\\Source.cs", " ", "Folder")] - [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\Source.cs", "Folder")] - [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\SubFolder\\Source.cs", "Folder", "SubFolder")] - [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\Source.cs ", "Folder")] - [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\SubFolder\\Source.cs ", "Folder", "SubFolder")] - [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\SubFolder\\Source.cs ", "Folder", "SubFolder")] - [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\SubFolder\\..\\Source.cs ", "Folder")] - [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\SubFolder\\Child\\..\\Source.cs ", "Folder", "SubFolder")] - [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\..\\Source.cs ", null)] - [InlineData("C:\\Folder\\Project" , "C:\\Folder\\Project\\Source.cs", null, null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Source.cs", null, null)] - [InlineData("C:\\Folder\\Project", "D:\\Source.cs", null, null)] - [InlineData("C:\\Folder\\Project", "\\Source.cs", null, null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "..\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Source.cs", "..\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "..\\Folder\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Source.cs", "..\\Folder\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "\\Folder\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "Folder\\..\\..\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "D:\\Folder\\Source.cs", null)] - [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "C:\\Folder\\Project\\Source.cs", null)] - public void GetLogicalFolderNames_Returns(string basePath, string fullPath, string link, params string[] expected) + [Theory] // BasePath FullPath Link Expected + [InlineData("C:\\Project", "C:\\Project\\Source.cs", null, null)] + [InlineData("C:\\Project", "C:\\Project\\Source.cs", "", null)] + [InlineData("C:\\Project", "C:\\Project\\Folder\\Source.cs", "", "Folder")] + [InlineData("C:\\Project", "C:\\Project\\Folder\\Source.cs", " ", "Folder")] + [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\Source.cs", "Folder")] + [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\SubFolder\\Source.cs", "Folder", "SubFolder")] + [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\Source.cs ", "Folder")] + [InlineData("C:\\Project", "C:\\Project\\Source.cs", "Folder\\SubFolder\\Source.cs ", "Folder", "SubFolder")] + [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\SubFolder\\Source.cs ", "Folder", "SubFolder")] + [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\SubFolder\\..\\Source.cs ", "Folder")] + [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\SubFolder\\Child\\..\\Source.cs ", "Folder", "SubFolder")] + [InlineData("C:\\Project", "C:\\Source.cs", "Folder\\..\\Source.cs ", null)] + [InlineData("C:\\Folder\\Project" , "C:\\Folder\\Project\\Source.cs", null, null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Source.cs", null, null)] + [InlineData("C:\\Folder\\Project", "D:\\Source.cs", null, null)] + [InlineData("C:\\Folder\\Project", "\\Source.cs", null, null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "..\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Source.cs", "..\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "..\\Folder\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Source.cs", "..\\Folder\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "\\Folder\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "Folder\\..\\..\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "D:\\Folder\\Source.cs", null)] + [InlineData("C:\\Folder\\Project", "C:\\Folder\\Project\\Source.cs", "C:\\Folder\\Project\\Source.cs", null)] + public void GetLogicalFolderNames_Returns(string basePath, string fullPath, string link, params string[] expected) + { + var metadata = ImmutableDictionary.Empty; + if (link is not null) { - var metadata = ImmutableDictionary.Empty; - if (link is not null) - { - metadata = metadata.SetItem(Compile.LinkProperty, link); - } + metadata = metadata.SetItem(Compile.LinkProperty, link); + } - var result = FileItemServices.GetLogicalFolderNames(basePath, fullPath, metadata); + var result = FileItemServices.GetLogicalFolderNames(basePath, fullPath, metadata); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/CSharp/CSharpProjectImageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/CSharp/CSharpProjectImageProviderTests.cs index 5e7fcd39f1..f893d3f510 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/CSharp/CSharpProjectImageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/CSharp/CSharpProjectImageProviderTests.cs @@ -1,63 +1,62 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.CSharp; + +public class CSharpProjectImageProviderTests { - public class CSharpProjectImageProviderTests + [Fact] + public void Constructor_DoesNotThrow() { - [Fact] - public void Constructor_DoesNotThrow() - { - new CSharpProjectImageProvider(); - } - - [Fact] - public void GetProjectImage_NullAsKey_ThrowsArgumentNull() - { - var provider = CreateInstance(); + new CSharpProjectImageProvider(); + } - Assert.Throws("key", () => - { - provider.GetProjectImage(null!); - }); - } + [Fact] + public void GetProjectImage_NullAsKey_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public void GetProjectImage_EmptyAsKey_ThrowsArgument() + Assert.Throws("key", () => { - var provider = CreateInstance(); + provider.GetProjectImage(null!); + }); + } - Assert.Throws("key", () => - { - provider.GetProjectImage(string.Empty); - }); - } + [Fact] + public void GetProjectImage_EmptyAsKey_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public void GetProjectImage_UnrecognizedKeyAsKey_ReturnsNull() + Assert.Throws("key", () => { - var provider = CreateInstance(); + provider.GetProjectImage(string.Empty); + }); + } - var result = provider.GetProjectImage("Unrecognized"); + [Fact] + public void GetProjectImage_UnrecognizedKeyAsKey_ReturnsNull() + { + var provider = CreateInstance(); - Assert.Null(result); - } + var result = provider.GetProjectImage("Unrecognized"); - [Theory] - [InlineData(ProjectImageKey.ProjectRoot)] - [InlineData(ProjectImageKey.SharedProjectRoot)] - [InlineData(ProjectImageKey.SharedItemsImportFile)] - public void GetProjectImage_RecognizedKeyAsKey_ReturnsNonNull(string key) - { - var provider = CreateInstance(); + Assert.Null(result); + } + + [Theory] + [InlineData(ProjectImageKey.ProjectRoot)] + [InlineData(ProjectImageKey.SharedProjectRoot)] + [InlineData(ProjectImageKey.SharedItemsImportFile)] + public void GetProjectImage_RecognizedKeyAsKey_ReturnsNonNull(string key) + { + var provider = CreateInstance(); - var result = provider.GetProjectImage(key); + var result = provider.GetProjectImage(key); - Assert.NotNull(result); - } + Assert.NotNull(result); + } - private static CSharpProjectImageProvider CreateInstance() - { - return new CSharpProjectImageProvider(); - } + private static CSharpProjectImageProvider CreateInstance() + { + return new CSharpProjectImageProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/FSharp/FSharpProjectImageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/FSharp/FSharpProjectImageProviderTests.cs index c5ca94985a..65a2a57330 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/FSharp/FSharpProjectImageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/FSharp/FSharpProjectImageProviderTests.cs @@ -1,61 +1,60 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.FSharp; + +public class FSharpProjectImageProviderTests { - public class FSharpProjectImageProviderTests + [Fact] + public void Constructor_DoesNotThrow() { - [Fact] - public void Constructor_DoesNotThrow() - { - new FSharpProjectImageProvider(); - } - - [Fact] - public void GetProjectImage_NullAsKey_ThrowsArgumentNull() - { - var provider = CreateInstance(); + new FSharpProjectImageProvider(); + } - Assert.Throws("key", () => - { - provider.GetProjectImage(null!); - }); - } + [Fact] + public void GetProjectImage_NullAsKey_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public void GetProjectImage_EmptyAsKey_ThrowsArgument() + Assert.Throws("key", () => { - var provider = CreateInstance(); + provider.GetProjectImage(null!); + }); + } - Assert.Throws("key", () => - { - provider.GetProjectImage(string.Empty); - }); - } + [Fact] + public void GetProjectImage_EmptyAsKey_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public void GetProjectImage_UnrecognizedKeyAsKey_ReturnsNull() + Assert.Throws("key", () => { - var provider = CreateInstance(); + provider.GetProjectImage(string.Empty); + }); + } - var result = provider.GetProjectImage("Unrecognized"); + [Fact] + public void GetProjectImage_UnrecognizedKeyAsKey_ReturnsNull() + { + var provider = CreateInstance(); - Assert.Null(result); - } + var result = provider.GetProjectImage("Unrecognized"); - [Theory] - [InlineData(ProjectImageKey.ProjectRoot)] - public void GetProjectImage_RecognizedKeyAsKey_ReturnsNonNull(string key) - { - var provider = CreateInstance(); + Assert.Null(result); + } + + [Theory] + [InlineData(ProjectImageKey.ProjectRoot)] + public void GetProjectImage_RecognizedKeyAsKey_ReturnsNonNull(string key) + { + var provider = CreateInstance(); - var result = provider.GetProjectImage(key); + var result = provider.GetProjectImage(key); - Assert.NotNull(result); - } + Assert.NotNull(result); + } - private static FSharpProjectImageProvider CreateInstance() - { - return new FSharpProjectImageProvider(); - } + private static FSharpProjectImageProvider CreateInstance() + { + return new FSharpProjectImageProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/ProjectImageProviderAggregatorTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/ProjectImageProviderAggregatorTests.cs index a8c844e050..fa7700b6b5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/ProjectImageProviderAggregatorTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/ProjectImageProviderAggregatorTests.cs @@ -1,113 +1,112 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging +namespace Microsoft.VisualStudio.ProjectSystem.Imaging; + +public class ProjectImageProviderAggregatorTests { - public class ProjectImageProviderAggregatorTests + [Fact] + public void GetImageKey_NullAsKey_ThrowsArgumentNull() { - [Fact] - public void GetImageKey_NullAsKey_ThrowsArgumentNull() - { - var aggregator = CreateInstance(); - - Assert.Throws("key", () => - { - aggregator.GetProjectImage(null!); - }); - } + var aggregator = CreateInstance(); - [Fact] - public void GetImageKey_EmptyAsKey_ThrowsArgument() + Assert.Throws("key", () => { - var aggregator = CreateInstance(); + aggregator.GetProjectImage(null!); + }); + } - Assert.Throws("key", () => - { - aggregator.GetProjectImage(string.Empty); - }); - } + [Fact] + public void GetImageKey_EmptyAsKey_ThrowsArgument() + { + var aggregator = CreateInstance(); - [Fact] - public void GetImageKey_WhenNoImageProviders_ReturnsNull() + Assert.Throws("key", () => { - var aggregator = CreateInstance(); + aggregator.GetProjectImage(string.Empty); + }); + } - var result = aggregator.GetProjectImage("key"); + [Fact] + public void GetImageKey_WhenNoImageProviders_ReturnsNull() + { + var aggregator = CreateInstance(); - Assert.Null(result); - } + var result = aggregator.GetProjectImage("key"); - [Fact] - public void GetImageKey_SingleImageProviderReturningNull_ReturnsNull() - { - var project = UnconfiguredProjectFactory.Create(); - var provider = IProjectImageProviderFactory.ImplementGetProjectImage((key) => null); - var aggregator = CreateInstance(project); + Assert.Null(result); + } - aggregator.ImageProviders.Add(provider); + [Fact] + public void GetImageKey_SingleImageProviderReturningNull_ReturnsNull() + { + var project = UnconfiguredProjectFactory.Create(); + var provider = IProjectImageProviderFactory.ImplementGetProjectImage((key) => null); + var aggregator = CreateInstance(project); - var result = aggregator.GetProjectImage("key"); + aggregator.ImageProviders.Add(provider); - Assert.Null(result); - } + var result = aggregator.GetProjectImage("key"); - [Fact] - public void GetImageKey_SingleImageProviderReturningKey_ReturnsKey() - { - var moniker = new ProjectImageMoniker(Guid.NewGuid(), 0); + Assert.Null(result); + } - var project = UnconfiguredProjectFactory.Create(); - var provider = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker); - var aggregator = CreateInstance(project); + [Fact] + public void GetImageKey_SingleImageProviderReturningKey_ReturnsKey() + { + var moniker = new ProjectImageMoniker(Guid.NewGuid(), 0); - aggregator.ImageProviders.Add(provider); + var project = UnconfiguredProjectFactory.Create(); + var provider = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker); + var aggregator = CreateInstance(project); - var result = aggregator.GetProjectImage("key"); + aggregator.ImageProviders.Add(provider); - Assert.Same(moniker, result); - } + var result = aggregator.GetProjectImage("key"); - [Fact] - public void GetImageKey_ManyImageProviderReturningKey_ReturnsFirstByOrder() - { - var moniker1 = new ProjectImageMoniker(Guid.NewGuid(), 0); - var moniker2 = new ProjectImageMoniker(Guid.NewGuid(), 0); + Assert.Same(moniker, result); + } - var project = UnconfiguredProjectFactory.Create(); - var provider1 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker1); - var provider2 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker2); - var aggregator = CreateInstance(project); + [Fact] + public void GetImageKey_ManyImageProviderReturningKey_ReturnsFirstByOrder() + { + var moniker1 = new ProjectImageMoniker(Guid.NewGuid(), 0); + var moniker2 = new ProjectImageMoniker(Guid.NewGuid(), 0); - aggregator.ImageProviders.Add(provider2, orderPrecedence: 0); // Lowest - aggregator.ImageProviders.Add(provider1, orderPrecedence: 10); // Highest + var project = UnconfiguredProjectFactory.Create(); + var provider1 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker1); + var provider2 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker2); + var aggregator = CreateInstance(project); - var result = aggregator.GetProjectImage("key"); + aggregator.ImageProviders.Add(provider2, orderPrecedence: 0); // Lowest + aggregator.ImageProviders.Add(provider1, orderPrecedence: 10); // Highest - Assert.Same(moniker1, result); - } + var result = aggregator.GetProjectImage("key"); - [Fact] - public void GetImageKey_ManyImageProviders_ReturnsFirstThatReturnsKey() - { - var moniker = new ProjectImageMoniker(Guid.NewGuid(), 0); + Assert.Same(moniker1, result); + } - var project = UnconfiguredProjectFactory.Create(); - var provider1 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => null); - var provider2 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker); - var aggregator = CreateInstance(project); + [Fact] + public void GetImageKey_ManyImageProviders_ReturnsFirstThatReturnsKey() + { + var moniker = new ProjectImageMoniker(Guid.NewGuid(), 0); - aggregator.ImageProviders.Add(provider1, orderPrecedence: 0); - aggregator.ImageProviders.Add(provider2, orderPrecedence: 10); + var project = UnconfiguredProjectFactory.Create(); + var provider1 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => null); + var provider2 = IProjectImageProviderFactory.ImplementGetProjectImage((key) => moniker); + var aggregator = CreateInstance(project); - var result = aggregator.GetProjectImage("key"); + aggregator.ImageProviders.Add(provider1, orderPrecedence: 0); + aggregator.ImageProviders.Add(provider2, orderPrecedence: 10); - Assert.Same(moniker, result); - } + var result = aggregator.GetProjectImage("key"); - private static ProjectImageProviderAggregator CreateInstance(UnconfiguredProject? project = null) - { - project ??= UnconfiguredProjectFactory.Create(); + Assert.Same(moniker, result); + } + + private static ProjectImageProviderAggregator CreateInstance(UnconfiguredProject? project = null) + { + project ??= UnconfiguredProjectFactory.Create(); - return new ProjectImageProviderAggregator(project); - } + return new ProjectImageProviderAggregator(project); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProviderTests.cs index e44e1d6588..51161cb700 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Imaging/VisualBasic/VisualBasicProjectImageProviderTests.cs @@ -1,63 +1,62 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Imaging.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.Imaging.VisualBasic; + +public class VisualBasicProjectImageProviderTests { - public class VisualBasicProjectImageProviderTests + [Fact] + public void Constructor_DoesNotThrow() { - [Fact] - public void Constructor_DoesNotThrow() - { - new VisualBasicProjectImageProvider(); - } - - [Fact] - public void GetProjectImage_NullAsKey_ThrowsArgumentNull() - { - var provider = CreateInstance(); + new VisualBasicProjectImageProvider(); + } - Assert.Throws("key", () => - { - provider.GetProjectImage(null!); - }); - } + [Fact] + public void GetProjectImage_NullAsKey_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public void GetProjectImage_EmptyAsKey_ThrowsArgument() + Assert.Throws("key", () => { - var provider = CreateInstance(); + provider.GetProjectImage(null!); + }); + } - Assert.Throws("key", () => - { - provider.GetProjectImage(string.Empty); - }); - } + [Fact] + public void GetProjectImage_EmptyAsKey_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public void GetProjectImage_UnrecognizedKeyAsKey_ReturnsNull() + Assert.Throws("key", () => { - var provider = CreateInstance(); + provider.GetProjectImage(string.Empty); + }); + } - var result = provider.GetProjectImage("Unrecognized"); + [Fact] + public void GetProjectImage_UnrecognizedKeyAsKey_ReturnsNull() + { + var provider = CreateInstance(); - Assert.Null(result); - } + var result = provider.GetProjectImage("Unrecognized"); - [Theory] - [InlineData(ProjectImageKey.ProjectRoot)] - [InlineData(ProjectImageKey.SharedProjectRoot)] - [InlineData(ProjectImageKey.SharedItemsImportFile)] - public void GetProjectImage_RecognizedKeyAsKey_ReturnsNonNull(string key) - { - var provider = CreateInstance(); + Assert.Null(result); + } + + [Theory] + [InlineData(ProjectImageKey.ProjectRoot)] + [InlineData(ProjectImageKey.SharedProjectRoot)] + [InlineData(ProjectImageKey.SharedItemsImportFile)] + public void GetProjectImage_RecognizedKeyAsKey_ReturnsNonNull(string key) + { + var provider = CreateInstance(); - var result = provider.GetProjectImage(key); + var result = provider.GetProjectImage(key); - Assert.NotNull(result); - } + Assert.NotNull(result); + } - private static VisualBasicProjectImageProvider CreateInstance() - { - return new VisualBasicProjectImageProvider(); - } + private static VisualBasicProjectImageProvider CreateInstance() + { + return new VisualBasicProjectImageProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserServiceTests.cs index dc5858e96d..54f2101e16 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpCommandLineParserServiceTests.cs @@ -1,66 +1,65 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp; + +public class CSharpCommandLineParserServiceTests : CommandLineParserServiceTestBase { - public class CSharpCommandLineParserServiceTests : CommandLineParserServiceTestBase - { - // This isn't supposed to be a exhaustive set of tests as we assume that Roslyn has tested their - // command-line parsing code, just enough to make sure we're passing the data through correctly. + // This isn't supposed to be a exhaustive set of tests as we assume that Roslyn has tested their + // command-line parsing code, just enough to make sure we're passing the data through correctly. - [Theory] - [InlineData("/r:Foo.dll", "Foo.dll")] - [InlineData("/r:Foo.dll|/r:Bar.dll", "Foo.dll|Bar.dll")] - [InlineData("/r:Foo.dll|Foo.cs|/r:Bar.dll", "Foo.dll|Bar.dll")] - public void Parse_SetsMetadataReferences(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData("/r:Foo.dll", "Foo.dll")] + [InlineData("/r:Foo.dll|/r:Bar.dll", "Foo.dll|Bar.dll")] + [InlineData("/r:Foo.dll|Foo.cs|/r:Bar.dll", "Foo.dll|Bar.dll")] + public void Parse_SetsMetadataReferences(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.MetadataReferences.Select(r => r.Reference)); - } + Assert.Equal(expected.Split('|'), results.MetadataReferences.Select(r => r.Reference)); + } - [Theory] - [InlineData(@"Foo.cs", @"C:\Project\Foo.cs")] - [InlineData(@"Foo.cs|Bar.cs", @"C:\Project\Foo.cs|C:\Project\Bar.cs")] - [InlineData(@"C:\Foo\Foo.cs", @"C:\Foo\Foo.cs")] - [InlineData(@"..\Foo.cs", @"C:\Project\..\Foo.cs")] - public void Parse_SetsSourceFiles(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData(@"Foo.cs", @"C:\Project\Foo.cs")] + [InlineData(@"Foo.cs|Bar.cs", @"C:\Project\Foo.cs|C:\Project\Bar.cs")] + [InlineData(@"C:\Foo\Foo.cs", @"C:\Foo\Foo.cs")] + [InlineData(@"..\Foo.cs", @"C:\Project\..\Foo.cs")] + public void Parse_SetsSourceFiles(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.SourceFiles.Select(r => r.Path)); - } + Assert.Equal(expected.Split('|'), results.SourceFiles.Select(r => r.Path)); + } - [Theory] - [InlineData(@"/a:Foo.dll", @"Foo.dll")] - [InlineData(@"/analyzer:C:\Foo.dll", @"C:\Foo.dll")] - public void Parse_SetsAnalyzerReferences(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData(@"/a:Foo.dll", @"Foo.dll")] + [InlineData(@"/analyzer:C:\Foo.dll", @"C:\Foo.dll")] + public void Parse_SetsAnalyzerReferences(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.AnalyzerReferences.Select(r => r.FilePath)); - } + Assert.Equal(expected.Split('|'), results.AnalyzerReferences.Select(r => r.FilePath)); + } - [Theory] - [InlineData(@"/additionalfile:Foo.txt", @"C:\Project\Foo.txt")] - [InlineData(@"/additionalfile:C:\Foo.txt", @"C:\Foo.txt")] - public void Parse_SetsAdditionalFiles(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData(@"/additionalfile:Foo.txt", @"C:\Project\Foo.txt")] + [InlineData(@"/additionalfile:C:\Foo.txt", @"C:\Foo.txt")] + public void Parse_SetsAdditionalFiles(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.AdditionalFiles.Select(r => r.Path)); - } + Assert.Equal(expected.Split('|'), results.AdditionalFiles.Select(r => r.Path)); + } - internal override ICommandLineParserService CreateInstance() - { - return new CSharpCommandLineParserService(); - } + internal override ICommandLineParserService CreateInstance() + { + return new CSharpCommandLineParserService(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsServiceTests.cs index b9fe0f3d5c..5eacefb83a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CSharp/CSharpSyntaxFactsServiceTests.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp; + +public class CSharpSyntaxFactsServiceTests { - public class CSharpSyntaxFactsServiceTests - { - private static readonly ISyntaxFactsService s_service = new CSharpSyntaxFactsService(); + private static readonly ISyntaxFactsService s_service = new CSharpSyntaxFactsService(); - [Fact] - public void TestIsValidIdentifier() - { - Assert.True(s_service.IsValidIdentifier("Foo")); - Assert.False(s_service.IsValidIdentifier("Foo`")); - } + [Fact] + public void TestIsValidIdentifier() + { + Assert.True(s_service.IsValidIdentifier("Foo")); + Assert.False(s_service.IsValidIdentifier("Foo`")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CommandLineParserServiceTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CommandLineParserServiceTestBase.cs index 4c647c55c1..1516c4e4b4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CommandLineParserServiceTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/CommandLineParserServiceTestBase.cs @@ -1,46 +1,45 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +public abstract class CommandLineParserServiceTestBase { - public abstract class CommandLineParserServiceTestBase + [Fact] + public void Parse_NullAsArguments_ThrowsArgumentNull() { - [Fact] - public void Parse_NullAsArguments_ThrowsArgumentNull() - { - var service = CreateInstance(); - - Assert.Throws("arguments", () => - { - service.Parse(null!, @"C:\Project"); - }); - } + var service = CreateInstance(); - [Fact] - public void Parse_NullAsBaseDirectory_ThrowsArgumentNull() + Assert.Throws("arguments", () => { - var service = CreateInstance(); + service.Parse(null!, @"C:\Project"); + }); + } - var arguments = Enumerable.Empty(); + [Fact] + public void Parse_NullAsBaseDirectory_ThrowsArgumentNull() + { + var service = CreateInstance(); - Assert.Throws("baseDirectory", () => - { - service.Parse(arguments, null!); - }); - } + var arguments = Enumerable.Empty(); - [Fact] - public void Parse_EmptyAsBaseDirectory_ThrowsArgument() + Assert.Throws("baseDirectory", () => { - var service = CreateInstance(); + service.Parse(arguments, null!); + }); + } - var arguments = Enumerable.Empty(); + [Fact] + public void Parse_EmptyAsBaseDirectory_ThrowsArgument() + { + var service = CreateInstance(); - Assert.Throws("baseDirectory", () => - { - service.Parse(arguments, ""); - }); - } + var arguments = Enumerable.Empty(); - internal abstract ICommandLineParserService CreateInstance(); + Assert.Throws("baseDirectory", () => + { + service.Parse(arguments, ""); + }); } + + internal abstract ICommandLineParserService CreateInstance(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserServiceTests.cs index c1c6481b19..67bde2116f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/FSharp/FSharpCommandLineParserServiceTests.cs @@ -1,109 +1,108 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.FSharp; + +public class FSharpCommandLineParserServiceTests : CommandLineParserServiceTestBase { - public class FSharpCommandLineParserServiceTests : CommandLineParserServiceTestBase + [Theory] + [InlineData("/r:Foo.dll", "Foo.dll")] + [InlineData("/r:Foo.dll|/r:Bar.dll", "Foo.dll|Bar.dll")] + [InlineData("-r:Foo.dll", "Foo.dll")] + [InlineData("--reference:Foo.dll", "Foo.dll")] + [InlineData("/r:Foo.dll;/r:Bar.dll", "Foo.dll|Bar.dll")] + [InlineData("/r:Foo.dll;--reference:Bar.dll", "Foo.dll|Bar.dll")] + [InlineData("/r:Foo.dll;--reference:Bar.dll;-r:Baz.dll", "Foo.dll|Bar.dll|Baz.dll")] + [InlineData("/r:Foo.dll;--reference:Bar.dll;-r:Baz.dll|/r:System.dll", "Foo.dll|Bar.dll|Baz.dll|System.dll")] + public void Parse_SetsMetadataReferences(string arguments, string expected) + { + var service = CreateInstance(); + + var results = service.Parse(arguments.Split('|'), @"C:\Project"); + + Assert.Equal(expected.Split('|'), results.MetadataReferences.Select(r => r.Reference)); + } + + [Theory] + [InlineData("Foo.fs", "Foo.fs")] + [InlineData("Foo.fs;Bar.fs", "Foo.fs|Bar.fs")] + [InlineData("Foo.fs;Bar.fs|Baz.fs", "Foo.fs|Bar.fs|Baz.fs")] + [InlineData("/r:Foo.dll|Foo.fs;Bar.fs|Baz.fs", "Foo.fs|Bar.fs|Baz.fs")] + [InlineData("Foo.fs;Bar.fs|/r:Foo.dll|Baz.fs", "Foo.fs|Bar.fs|Baz.fs")] + [InlineData("Foo.fs;Bar.fs|Baz.fs|/r:Foo.dll", "Foo.fs|Bar.fs|Baz.fs")] + public void Parse_SetsSourceFiles(string arguments, string expected) + { + var service = CreateInstance(); + + var results = service.Parse(arguments.Split('|'), @"C:\Project"); + + Assert.Equal(expected.Split('|'), results.SourceFiles.Select(r => r.Path)); + } + + [Theory] + [InlineData("Foo.unknown")] + [InlineData("Foo.cs")] + [InlineData("Foo.vb")] + public void Parse_IgnoresUnrecognizedExtensions(string arguments) + { + var service = CreateInstance(); + + var results = service.Parse(arguments.Split('|'), @"C:\Project"); + + Assert.Empty(results.SourceFiles); + } + + [Theory] + [InlineData("Foo.fsx", true)] + [InlineData("Foo.fsscript", true)] + [InlineData("Foo.fsi", false)] + [InlineData("Foo.fs", false)] + [InlineData("Foo.ml", false)] + [InlineData("Foo.mli", false)] + public void Parse_SetsIsScriptOnScriptFiles(string arguments, bool isScript) + { + var service = CreateInstance(); + + var results = service.Parse(arguments.Split('|'), @"C:\Project"); + + Assert.Single(results.SourceFiles.Select(r => r.IsScript), isScript); + } + + [Theory] + [InlineData("-switch", "-switch")] + [InlineData("-switch:bar", "-switch:bar")] + [InlineData("--switch", "--switch")] + [InlineData("--switch:bar", "--switch:bar")] + [InlineData("/switch", "/switch")] + [InlineData("/switch:bar", "/switch:bar")] + [InlineData("/switch:bar|foo.fsi|-switch", "/switch:bar|-switch")] + [InlineData("/switch:bar|/r:foo.dll|-switch", "/switch:bar|-switch")] + public void Parse_SetsCompileOptionsToUnrecognizedSwitches(string arguments, string expected) + { + var service = CreateInstance(); + + var results = (FSharpBuildOptions)service.Parse(arguments.Split('|'), @"C:\Project"); + + Assert.Equal(expected.Split('|'), results.CompileOptions); + } + + [Theory] + [InlineData("/r:Foo.dll")] + [InlineData("Foo.fs")] + [InlineData("-a:Foo.dll")] + [InlineData("-analyzer:Foo.dll")] + [InlineData("-additionalfile:Foo.dll")] + public void Parse_SetsAdditionalFilesAndAnalyzerReferencesToEmpty(string arguments) + { + var service = CreateInstance(); + + var results = service.Parse(arguments.Split('|'), @"C:\Project"); + + Assert.Empty(results.AdditionalFiles); + Assert.Empty(results.AnalyzerReferences); + } + + internal override ICommandLineParserService CreateInstance() { - [Theory] - [InlineData("/r:Foo.dll", "Foo.dll")] - [InlineData("/r:Foo.dll|/r:Bar.dll", "Foo.dll|Bar.dll")] - [InlineData("-r:Foo.dll", "Foo.dll")] - [InlineData("--reference:Foo.dll", "Foo.dll")] - [InlineData("/r:Foo.dll;/r:Bar.dll", "Foo.dll|Bar.dll")] - [InlineData("/r:Foo.dll;--reference:Bar.dll", "Foo.dll|Bar.dll")] - [InlineData("/r:Foo.dll;--reference:Bar.dll;-r:Baz.dll", "Foo.dll|Bar.dll|Baz.dll")] - [InlineData("/r:Foo.dll;--reference:Bar.dll;-r:Baz.dll|/r:System.dll", "Foo.dll|Bar.dll|Baz.dll|System.dll")] - public void Parse_SetsMetadataReferences(string arguments, string expected) - { - var service = CreateInstance(); - - var results = service.Parse(arguments.Split('|'), @"C:\Project"); - - Assert.Equal(expected.Split('|'), results.MetadataReferences.Select(r => r.Reference)); - } - - [Theory] - [InlineData("Foo.fs", "Foo.fs")] - [InlineData("Foo.fs;Bar.fs", "Foo.fs|Bar.fs")] - [InlineData("Foo.fs;Bar.fs|Baz.fs", "Foo.fs|Bar.fs|Baz.fs")] - [InlineData("/r:Foo.dll|Foo.fs;Bar.fs|Baz.fs", "Foo.fs|Bar.fs|Baz.fs")] - [InlineData("Foo.fs;Bar.fs|/r:Foo.dll|Baz.fs", "Foo.fs|Bar.fs|Baz.fs")] - [InlineData("Foo.fs;Bar.fs|Baz.fs|/r:Foo.dll", "Foo.fs|Bar.fs|Baz.fs")] - public void Parse_SetsSourceFiles(string arguments, string expected) - { - var service = CreateInstance(); - - var results = service.Parse(arguments.Split('|'), @"C:\Project"); - - Assert.Equal(expected.Split('|'), results.SourceFiles.Select(r => r.Path)); - } - - [Theory] - [InlineData("Foo.unknown")] - [InlineData("Foo.cs")] - [InlineData("Foo.vb")] - public void Parse_IgnoresUnrecognizedExtensions(string arguments) - { - var service = CreateInstance(); - - var results = service.Parse(arguments.Split('|'), @"C:\Project"); - - Assert.Empty(results.SourceFiles); - } - - [Theory] - [InlineData("Foo.fsx", true)] - [InlineData("Foo.fsscript", true)] - [InlineData("Foo.fsi", false)] - [InlineData("Foo.fs", false)] - [InlineData("Foo.ml", false)] - [InlineData("Foo.mli", false)] - public void Parse_SetsIsScriptOnScriptFiles(string arguments, bool isScript) - { - var service = CreateInstance(); - - var results = service.Parse(arguments.Split('|'), @"C:\Project"); - - Assert.Single(results.SourceFiles.Select(r => r.IsScript), isScript); - } - - [Theory] - [InlineData("-switch", "-switch")] - [InlineData("-switch:bar", "-switch:bar")] - [InlineData("--switch", "--switch")] - [InlineData("--switch:bar", "--switch:bar")] - [InlineData("/switch", "/switch")] - [InlineData("/switch:bar", "/switch:bar")] - [InlineData("/switch:bar|foo.fsi|-switch", "/switch:bar|-switch")] - [InlineData("/switch:bar|/r:foo.dll|-switch", "/switch:bar|-switch")] - public void Parse_SetsCompileOptionsToUnrecognizedSwitches(string arguments, string expected) - { - var service = CreateInstance(); - - var results = (FSharpBuildOptions)service.Parse(arguments.Split('|'), @"C:\Project"); - - Assert.Equal(expected.Split('|'), results.CompileOptions); - } - - [Theory] - [InlineData("/r:Foo.dll")] - [InlineData("Foo.fs")] - [InlineData("-a:Foo.dll")] - [InlineData("-analyzer:Foo.dll")] - [InlineData("-additionalfile:Foo.dll")] - public void Parse_SetsAdditionalFilesAndAnalyzerReferencesToEmpty(string arguments) - { - var service = CreateInstance(); - - var results = service.Parse(arguments.Split('|'), @"C:\Project"); - - Assert.Empty(results.AdditionalFiles); - Assert.Empty(results.AnalyzerReferences); - } - - internal override ICommandLineParserService CreateInstance() - { - return new FSharpCommandLineParserService(); - } + return new FSharpCommandLineParserService(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserServiceTests.cs index 5249b3dc8a..267f2c5fdf 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicCommandLineParserServiceTests.cs @@ -1,66 +1,65 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic; + +public class VisualBasicCommandLineParserServiceTests : CommandLineParserServiceTestBase { - public class VisualBasicCommandLineParserServiceTests : CommandLineParserServiceTestBase - { - // This isn't supposed to be a exhaustive set of tests as we assume that Roslyn has tested their - // command-line parsing code, just enough to make sure we're passing the data through correctly. + // This isn't supposed to be a exhaustive set of tests as we assume that Roslyn has tested their + // command-line parsing code, just enough to make sure we're passing the data through correctly. - [Theory] - [InlineData("/r:Foo.dll", "Foo.dll")] - [InlineData("/r:Foo.dll|/r:Bar.dll", "Foo.dll|Bar.dll")] - [InlineData("/r:Foo.dll|Foo.vb|/r:Bar.dll", "Foo.dll|Bar.dll")] - public void Parse_SetsMetadataReferences(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData("/r:Foo.dll", "Foo.dll")] + [InlineData("/r:Foo.dll|/r:Bar.dll", "Foo.dll|Bar.dll")] + [InlineData("/r:Foo.dll|Foo.vb|/r:Bar.dll", "Foo.dll|Bar.dll")] + public void Parse_SetsMetadataReferences(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.MetadataReferences.Select(r => r.Reference)); - } + Assert.Equal(expected.Split('|'), results.MetadataReferences.Select(r => r.Reference)); + } - [Theory] - [InlineData(@"Foo.vb", @"C:\Project\Foo.vb")] - [InlineData(@"Foo.vb|Bar.cs", @"C:\Project\Foo.vb|C:\Project\Bar.cs")] - [InlineData(@"C:\Foo\Foo.vb", @"C:\Foo\Foo.vb")] - [InlineData(@"..\Foo.vb", @"C:\Project\..\Foo.vb")] - public void Parse_SetsSourceFiles(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData(@"Foo.vb", @"C:\Project\Foo.vb")] + [InlineData(@"Foo.vb|Bar.cs", @"C:\Project\Foo.vb|C:\Project\Bar.cs")] + [InlineData(@"C:\Foo\Foo.vb", @"C:\Foo\Foo.vb")] + [InlineData(@"..\Foo.vb", @"C:\Project\..\Foo.vb")] + public void Parse_SetsSourceFiles(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.SourceFiles.Select(r => r.Path)); - } + Assert.Equal(expected.Split('|'), results.SourceFiles.Select(r => r.Path)); + } - [Theory] - [InlineData(@"/a:Foo.dll", @"Foo.dll")] - [InlineData(@"/analyzer:C:\Foo.dll", @"C:\Foo.dll")] - public void Parse_SetsAnalyzerReferences(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData(@"/a:Foo.dll", @"Foo.dll")] + [InlineData(@"/analyzer:C:\Foo.dll", @"C:\Foo.dll")] + public void Parse_SetsAnalyzerReferences(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.AnalyzerReferences.Select(r => r.FilePath)); - } + Assert.Equal(expected.Split('|'), results.AnalyzerReferences.Select(r => r.FilePath)); + } - [Theory] - [InlineData(@"/additionalfile:Foo.txt", @"C:\Project\Foo.txt")] - [InlineData(@"/additionalfile:C:\Foo.txt", @"C:\Foo.txt")] - public void Parse_SetsAdditionalFiles(string arguments, string expected) - { - var service = CreateInstance(); + [Theory] + [InlineData(@"/additionalfile:Foo.txt", @"C:\Project\Foo.txt")] + [InlineData(@"/additionalfile:C:\Foo.txt", @"C:\Foo.txt")] + public void Parse_SetsAdditionalFiles(string arguments, string expected) + { + var service = CreateInstance(); - var results = service.Parse(arguments.Split('|'), @"C:\Project"); + var results = service.Parse(arguments.Split('|'), @"C:\Project"); - Assert.Equal(expected.Split('|'), results.AdditionalFiles.Select(r => r.Path)); - } + Assert.Equal(expected.Split('|'), results.AdditionalFiles.Select(r => r.Path)); + } - internal override ICommandLineParserService CreateInstance() - { - return new VisualBasicCommandLineParserService(); - } + internal override ICommandLineParserService CreateInstance() + { + return new VisualBasicCommandLineParserService(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsServiceTests.cs index 3b1b430ef4..7bd11d063c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/LanguageServices/VisualBasic/VisualBasicSyntaxFactsServiceTests.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic; + +public class VisualBasicSyntaxFactsServiceTests { - public class VisualBasicSyntaxFactsServiceTests - { - private static readonly ISyntaxFactsService s_service = new VisualBasicSyntaxFactsService(); + private static readonly ISyntaxFactsService s_service = new VisualBasicSyntaxFactsService(); - [Fact] - public void TestIsValidIdentifier() - { - Assert.True(s_service.IsValidIdentifier("Foo")); - Assert.False(s_service.IsValidIdentifier("Foo`")); - } + [Fact] + public void TestIsValidIdentifier() + { + Assert.True(s_service.IsValidIdentifier("Foo")); + Assert.False(s_service.IsValidIdentifier("Foo`")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Logging/ProjectLoggingExtensionsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Logging/ProjectLoggingExtensionsTests.cs index 8731392fad..e95d0735c1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Logging/ProjectLoggingExtensionsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Logging/ProjectLoggingExtensionsTests.cs @@ -1,117 +1,116 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public class ProjectLoggingExtensionsTests { - public class ProjectLoggingExtensionsTests + [Fact] + public void BeginBatch_NullAsLogger_ThrowsArgumentNull() { - [Fact] - public void BeginBatch_NullAsLogger_ThrowsArgumentNull() + Assert.Throws("outputService", () => { - Assert.Throws("outputService", () => - { - new BatchLogger(null!); - }); - } + new BatchLogger(null!); + }); + } + + [Theory] + [InlineData(int.MinValue)] + [InlineData(-2)] + [InlineData(-1)] + public void BeginBatch_SetIndentLevelToLessThanZero_ThrowsArgumentOutOfRange(int indentLevel) + { + var logger = new MockOutputService(); + var batch = new BatchLogger(logger); - [Theory] - [InlineData(int.MinValue)] - [InlineData(-2)] - [InlineData(-1)] - public void BeginBatch_SetIndentLevelToLessThanZero_ThrowsArgumentOutOfRange(int indentLevel) + Assert.Throws("value", () => { - var logger = new MockOutputService(); - var batch = new BatchLogger(logger); + batch.IndentLevel = indentLevel; + }); + } - Assert.Throws("value", () => - { - batch.IndentLevel = indentLevel; - }); - } + [Theory] + [InlineData(0, "")] + [InlineData(1, " ")] + [InlineData(2, " ")] + [InlineData(4, " ")] + public void BeginBatch_IndentLevel_AppendsIndentToWriteLine(int indentLevel, string expected) + { + var logger = new MockOutputService(); - [Theory] - [InlineData(0, "")] - [InlineData(1, " ")] - [InlineData(2, " ")] - [InlineData(4, " ")] - public void BeginBatch_IndentLevel_AppendsIndentToWriteLine(int indentLevel, string expected) + using (var batch = new BatchLogger(logger)) { - var logger = new MockOutputService(); + batch.IndentLevel = indentLevel; + batch.WriteLine(string.Empty); + } - using (var batch = new BatchLogger(logger)) - { - batch.IndentLevel = indentLevel; - batch.WriteLine(string.Empty); - } + Assert.Equal(expected, logger.Text); + } - Assert.Equal(expected, logger.Text); - } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void BeginBatch_IsEnabled_ReturnsLoggerIsEnabled(bool isEnabled) + { + var logger = new MockOutputService() { IsEnabled = isEnabled }; - [Theory] - [InlineData(true)] - [InlineData(false)] - public void BeginBatch_IsEnabled_ReturnsLoggerIsEnabled(bool isEnabled) - { - var logger = new MockOutputService() { IsEnabled = isEnabled }; + var batch = new BatchLogger(logger); - var batch = new BatchLogger(logger); + Assert.Equal(batch.IsEnabled, logger.IsEnabled); + } - Assert.Equal(batch.IsEnabled, logger.IsEnabled); - } + [Fact] + public void BeginBatch_WhenUnderlyingLoggerIsNotEnabled_DoesNotLog() + { + var logger = new MockOutputService() { IsEnabled = false }; - [Fact] - public void BeginBatch_WhenUnderlyingLoggerIsNotEnabled_DoesNotLog() + using (var batch = new BatchLogger(logger)) { - var logger = new MockOutputService() { IsEnabled = false }; + batch.WriteLine("Hello World!"); + } - using (var batch = new BatchLogger(logger)) - { - batch.WriteLine("Hello World!"); - } + Assert.Null(logger.Text); + } - Assert.Null(logger.Text); - } + [Fact] + public void BeginBatch_WhenUnderlyingLoggerIsEnabled_Logs() + { + var logger = new MockOutputService() { IsEnabled = true }; - [Fact] - public void BeginBatch_WhenUnderlyingLoggerIsEnabled_Logs() + using (var batch = new BatchLogger(logger)) { - var logger = new MockOutputService() { IsEnabled = true }; + batch.WriteLine("Hello World!"); + } - using (var batch = new BatchLogger(logger)) - { - batch.WriteLine("Hello World!"); - } + Assert.Equal("Hello World!", logger.Text); + } - Assert.Equal("Hello World!", logger.Text); - } + [Fact] + public void BeginBatch_CanLogMultipleWriteLines() + { + var logger = new MockOutputService() { IsEnabled = true }; - [Fact] - public void BeginBatch_CanLogMultipleWriteLines() + using (var batch = new BatchLogger(logger)) { - var logger = new MockOutputService() { IsEnabled = true }; - - using (var batch = new BatchLogger(logger)) - { - batch.WriteLine("Line1"); - batch.IndentLevel = 1; - batch.WriteLine("Line2"); - batch.IndentLevel = 0; - batch.WriteLine("Line3"); - } - - // NOTE: No trailing new line, as the logger itself should be adding it - Assert.Equal("Line1\r\n Line2\r\nLine3", logger.Text, ignoreLineEndingDifferences: true); + batch.WriteLine("Line1"); + batch.IndentLevel = 1; + batch.WriteLine("Line2"); + batch.IndentLevel = 0; + batch.WriteLine("Line3"); } - private class MockOutputService : IManagedProjectDiagnosticOutputService - { - public bool IsEnabled { get; set; } = true; + // NOTE: No trailing new line, as the logger itself should be adding it + Assert.Equal("Line1\r\n Line2\r\nLine3", logger.Text, ignoreLineEndingDifferences: true); + } - public string? Text { get; set; } + private class MockOutputService : IManagedProjectDiagnosticOutputService + { + public bool IsEnabled { get; set; } = true; + + public string? Text { get; set; } - public void WriteLine(string outputMessage) - { - Text = outputMessage; - } + public void WriteLine(string outputMessage) + { + Text = outputMessage; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsyncTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsyncTests.cs index 8be8b0a67e..4b8069c5a1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsyncTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/OnceInitializedOnceDisposedUnderLockAsyncTests.cs @@ -2,425 +2,424 @@ using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class OnceInitializedOnceDisposedUnderLockAsyncTests { - public class OnceInitializedOnceDisposedUnderLockAsyncTests + [Fact] + public void ExecuteUnderLockAsync_NullAsAction_ThrowsArgumentNullException() { - [Fact] - public void ExecuteUnderLockAsync_NullAsAction_ThrowsArgumentNullException() + var instance = CreateInstance(); + + Assert.ThrowsAsync(() => { - var instance = CreateInstance(); + return instance.ExecuteUnderLockAsync(null!, CancellationToken.None); + }); + } - Assert.ThrowsAsync(() => - { - return instance.ExecuteUnderLockAsync(null!, CancellationToken.None); - }); - } + [Fact] + public void ExecuteUnderLockAsyncOfT_NullAsAction_ThrowsArgumentNullException() + { + var instance = CreateInstance(); - [Fact] - public void ExecuteUnderLockAsyncOfT_NullAsAction_ThrowsArgumentNullException() + Assert.ThrowsAsync(() => { - var instance = CreateInstance(); + return instance.ExecuteUnderLockAsync(null!, CancellationToken.None); + }); + } - Assert.ThrowsAsync(() => - { - return instance.ExecuteUnderLockAsync(null!, CancellationToken.None); - }); - } + [Fact] + public async Task ExecuteUnderLockAsync_PassesCancellationTokenToAction() + { + var cancellationTokenSource = new CancellationTokenSource(); + + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsync_PassesCancellationTokenToAction() + bool result = false; + await instance.ExecuteUnderLockAsync(ct => { - var cancellationTokenSource = new CancellationTokenSource(); + cancellationTokenSource.Cancel(); - var instance = CreateInstance(); + result = ct.IsCancellationRequested; - bool result = false; - await instance.ExecuteUnderLockAsync(ct => - { - cancellationTokenSource.Cancel(); + return Task.CompletedTask; + }, cancellationTokenSource.Token); - result = ct.IsCancellationRequested; + Assert.True(result); + } - return Task.CompletedTask; - }, cancellationTokenSource.Token); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_PassesCancellationTokenToAction() + { + var cancellationTokenSource = new CancellationTokenSource(); - Assert.True(result); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsyncOfT_PassesCancellationTokenToAction() + bool result = false; + await instance.ExecuteUnderLockAsync(ct => { - var cancellationTokenSource = new CancellationTokenSource(); + cancellationTokenSource.Cancel(); - var instance = CreateInstance(); + result = ct.IsCancellationRequested; - bool result = false; - await instance.ExecuteUnderLockAsync(ct => - { - cancellationTokenSource.Cancel(); + return TaskResult.Null(); + }, cancellationTokenSource.Token); - result = ct.IsCancellationRequested; + Assert.True(result); + } - return TaskResult.Null(); - }, cancellationTokenSource.Token); + [Fact] + public async Task ExecuteUnderLockAsync_WhenPassedCancelledToken_DoesNotExecuteAction() + { + var cancellationToken = new CancellationToken(canceled: true); - Assert.True(result); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsync_WhenPassedCancelledToken_DoesNotExecuteAction() - { - var cancellationToken = new CancellationToken(canceled: true); + bool called = false; + var result = instance.ExecuteUnderLockAsync(ct => { called = true; return Task.CompletedTask; }, cancellationToken); - var instance = CreateInstance(); + var exception = await Assert.ThrowsAnyAsync(() => result); + Assert.False(called); + Assert.Equal(cancellationToken, exception.CancellationToken); + } - bool called = false; - var result = instance.ExecuteUnderLockAsync(ct => { called = true; return Task.CompletedTask; }, cancellationToken); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_WhenPassedCancelledToken_DoesNotExecuteAction() + { + var cancellationToken = new CancellationToken(canceled: true); - var exception = await Assert.ThrowsAnyAsync(() => result); - Assert.False(called); - Assert.Equal(cancellationToken, exception.CancellationToken); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsyncOfT_WhenPassedCancelledToken_DoesNotExecuteAction() - { - var cancellationToken = new CancellationToken(canceled: true); + bool called = false; + var result = instance.ExecuteUnderLockAsync(ct => { called = true; return TaskResult.Null(); }, cancellationToken); - var instance = CreateInstance(); + var exception = await Assert.ThrowsAnyAsync(() => result); + Assert.False(called); + Assert.Equal(cancellationToken, exception.CancellationToken); + } - bool called = false; - var result = instance.ExecuteUnderLockAsync(ct => { called = true; return TaskResult.Null(); }, cancellationToken); + [Fact] + public async Task ExecuteUnderLockAsync_WithNoContention_ExecutesAction() + { + var instance = CreateInstance(); - var exception = await Assert.ThrowsAnyAsync(() => result); - Assert.False(called); - Assert.Equal(cancellationToken, exception.CancellationToken); - } + int callCount = 0; + await instance.ExecuteUnderLockAsync((ct) => { callCount++; return Task.CompletedTask; }, CancellationToken.None); - [Fact] - public async Task ExecuteUnderLockAsync_WithNoContention_ExecutesAction() - { - var instance = CreateInstance(); + Assert.Equal(1, callCount); + } - int callCount = 0; - await instance.ExecuteUnderLockAsync((ct) => { callCount++; return Task.CompletedTask; }, CancellationToken.None); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_WithNoContention_ExecutesAction() + { + var instance = CreateInstance(); - Assert.Equal(1, callCount); - } + int callCount = 0; + await instance.ExecuteUnderLockAsync((ct) => { callCount++; return TaskResult.Null(); }, CancellationToken.None); - [Fact] - public async Task ExecuteUnderLockAsyncOfT_WithNoContention_ExecutesAction() - { - var instance = CreateInstance(); + Assert.Equal(1, callCount); + } - int callCount = 0; - await instance.ExecuteUnderLockAsync((ct) => { callCount++; return TaskResult.Null(); }, CancellationToken.None); + [Fact] + public async Task ExecuteUnderLockAsync_AvoidsOverlappingActions() + { + var firstEntered = new AsyncManualResetEvent(); + var firstRelease = new AsyncManualResetEvent(); + var secondEntered = new AsyncManualResetEvent(); - Assert.Equal(1, callCount); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsync_AvoidsOverlappingActions() + Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => { - var firstEntered = new AsyncManualResetEvent(); - var firstRelease = new AsyncManualResetEvent(); - var secondEntered = new AsyncManualResetEvent(); + firstEntered.Set(); + await firstRelease; + }, CancellationToken.None); - var instance = CreateInstance(); + Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => + { + secondEntered.Set(); + return Task.CompletedTask; + }, CancellationToken.None)); - Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => - { - firstEntered.Set(); - await firstRelease; - }, CancellationToken.None); + await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); + } - Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => - { - secondEntered.Set(); - return Task.CompletedTask; - }, CancellationToken.None)); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_AvoidsOverlappingActions() + { + var firstEntered = new AsyncManualResetEvent(); + var firstRelease = new AsyncManualResetEvent(); + var secondEntered = new AsyncManualResetEvent(); - await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsyncOfT_AvoidsOverlappingActions() + Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => { - var firstEntered = new AsyncManualResetEvent(); - var firstRelease = new AsyncManualResetEvent(); - var secondEntered = new AsyncManualResetEvent(); + firstEntered.Set(); + await firstRelease; - var instance = CreateInstance(); + return string.Empty; + }, CancellationToken.None); - Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => - { - firstEntered.Set(); - await firstRelease; + Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => + { + secondEntered.Set(); + return TaskResult.Null(); + }, CancellationToken.None)); - return string.Empty; - }, CancellationToken.None); + await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); + } - Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => - { - secondEntered.Set(); - return TaskResult.Null(); - }, CancellationToken.None)); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_AvoidsOverlappingActionsWithExecuteUnderLockAsync() + { + var firstEntered = new AsyncManualResetEvent(); + var firstRelease = new AsyncManualResetEvent(); + var secondEntered = new AsyncManualResetEvent(); - await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsyncOfT_AvoidsOverlappingActionsWithExecuteUnderLockAsync() + Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => { - var firstEntered = new AsyncManualResetEvent(); - var firstRelease = new AsyncManualResetEvent(); - var secondEntered = new AsyncManualResetEvent(); + firstEntered.Set(); + await firstRelease; + }, CancellationToken.None); - var instance = CreateInstance(); + Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => + { + secondEntered.Set(); + return TaskResult.Null(); + }, CancellationToken.None)); - Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => - { - firstEntered.Set(); - await firstRelease; - }, CancellationToken.None); + await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); + } - Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => - { - secondEntered.Set(); - return TaskResult.Null(); - }, CancellationToken.None)); + [Fact] + public async Task ExecuteUnderLockAsync_AvoidsOverlappingActionsWithExecuteUnderLockAsyncOfT() + { + var firstEntered = new AsyncManualResetEvent(); + var firstRelease = new AsyncManualResetEvent(); + var secondEntered = new AsyncManualResetEvent(); - await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); - } + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsync_AvoidsOverlappingActionsWithExecuteUnderLockAsyncOfT() + Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => { - var firstEntered = new AsyncManualResetEvent(); - var firstRelease = new AsyncManualResetEvent(); - var secondEntered = new AsyncManualResetEvent(); - - var instance = CreateInstance(); + firstEntered.Set(); + await firstRelease; - Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => - { - firstEntered.Set(); - await firstRelease; + return string.Empty; + }, CancellationToken.None); - return string.Empty; - }, CancellationToken.None); + Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => + { + secondEntered.Set(); + return Task.CompletedTask; + }, CancellationToken.None)); - Task secondAction() => Task.Run(() => instance.ExecuteUnderLockAsync((ct) => - { - secondEntered.Set(); - return Task.CompletedTask; - }, CancellationToken.None)); + await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); + } - await AssertNoOverlap(firstAction, secondAction, firstEntered, firstRelease, secondEntered); - } + [Fact] + public async Task ExecuteUnderLockAsync_CanBeNested() + { + var instance = CreateInstance(); - [Fact] - public async Task ExecuteUnderLockAsync_CanBeNested() + int callCount = 0; + await instance.ExecuteUnderLockAsync(async (_) => { - var instance = CreateInstance(); - - int callCount = 0; await instance.ExecuteUnderLockAsync(async (_) => { - await instance.ExecuteUnderLockAsync(async (_) => + await instance.ExecuteUnderLockAsync((_) => { - await instance.ExecuteUnderLockAsync((_) => - { - callCount++; - return Task.CompletedTask; - }); + callCount++; + return Task.CompletedTask; }); }); + }); - Assert.Equal(1, callCount); - } + Assert.Equal(1, callCount); + } - [Fact] - public async Task ExecuteUnderLockAsyncOfT_CanBeNested() - { - var instance = CreateInstance(); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_CanBeNested() + { + var instance = CreateInstance(); - int callCount = 0; + int callCount = 0; + await instance.ExecuteUnderLockAsync(async (_) => + { await instance.ExecuteUnderLockAsync(async (_) => { - await instance.ExecuteUnderLockAsync(async (_) => + await instance.ExecuteUnderLockAsync((_) => { - await instance.ExecuteUnderLockAsync((_) => - { - callCount++; - return TaskResult.Null(); - }); - - return string.Empty; + callCount++; + return TaskResult.Null(); }); return string.Empty; }); - Assert.Equal(1, callCount); - } + return string.Empty; + }); + + Assert.Equal(1, callCount); + } - [Fact] - public async Task ExecuteUnderLockAsync_AvoidsOverlappingWithDispose() + [Fact] + public async Task ExecuteUnderLockAsync_AvoidsOverlappingWithDispose() + { + var firstEntered = new AsyncManualResetEvent(); + var firstRelease = new AsyncManualResetEvent(); + var disposeEntered = new AsyncManualResetEvent(); + + ConcreteOnceInitializedOnceDisposedUnderLockAsync? instance; + + Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => { - var firstEntered = new AsyncManualResetEvent(); - var firstRelease = new AsyncManualResetEvent(); - var disposeEntered = new AsyncManualResetEvent(); + firstEntered.Set(); + await firstRelease.WaitAsync(); + }, CancellationToken.None); - ConcreteOnceInitializedOnceDisposedUnderLockAsync? instance; + instance = CreateInstance(() => + { + disposeEntered.Set(); + return Task.CompletedTask; + }); - Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => - { - firstEntered.Set(); - await firstRelease.WaitAsync(); - }, CancellationToken.None); + Task disposeAction() => Task.Run(instance.DisposeAsync); - instance = CreateInstance(() => - { - disposeEntered.Set(); - return Task.CompletedTask; - }); + await AssertNoOverlap(firstAction, disposeAction, firstEntered, firstRelease, disposeEntered); + } - Task disposeAction() => Task.Run(instance.DisposeAsync); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_AvoidsOverlappingWithDispose() + { + var firstEntered = new AsyncManualResetEvent(); + var firstRelease = new AsyncManualResetEvent(); + var disposeEntered = new AsyncManualResetEvent(); - await AssertNoOverlap(firstAction, disposeAction, firstEntered, firstRelease, disposeEntered); - } + ConcreteOnceInitializedOnceDisposedUnderLockAsync? instance; - [Fact] - public async Task ExecuteUnderLockAsyncOfT_AvoidsOverlappingWithDispose() + Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => { - var firstEntered = new AsyncManualResetEvent(); - var firstRelease = new AsyncManualResetEvent(); - var disposeEntered = new AsyncManualResetEvent(); + firstEntered.Set(); + await firstRelease.WaitAsync(); - ConcreteOnceInitializedOnceDisposedUnderLockAsync? instance; + return string.Empty; + }, CancellationToken.None); - Task firstAction() => instance.ExecuteUnderLockAsync(async (ct) => - { - firstEntered.Set(); - await firstRelease.WaitAsync(); + instance = CreateInstance(() => + { + disposeEntered.Set(); + return Task.CompletedTask; + }); - return string.Empty; - }, CancellationToken.None); + Task disposeAction() => Task.Run(instance.DisposeAsync); - instance = CreateInstance(() => - { - disposeEntered.Set(); - return Task.CompletedTask; - }); + await AssertNoOverlap(firstAction, disposeAction, firstEntered, firstRelease, disposeEntered); + } - Task disposeAction() => Task.Run(instance.DisposeAsync); + [Fact] + public async Task DisposeAsync_DoesNotThrow() + { + var instance = CreateInstance(); - await AssertNoOverlap(firstAction, disposeAction, firstEntered, firstRelease, disposeEntered); - } + await instance.DisposeAsync(); - [Fact] - public async Task DisposeAsync_DoesNotThrow() - { - var instance = CreateInstance(); + Assert.True(instance.IsDisposed); + } - await instance.DisposeAsync(); + [Fact] + public async Task ExecuteUnderLockAsync_WhenDisposed_ThrowsOperationCanceled() + { + var instance = CreateInstance(); - Assert.True(instance.IsDisposed); - } + await instance.DisposeAsync(); - [Fact] - public async Task ExecuteUnderLockAsync_WhenDisposed_ThrowsOperationCanceled() + var result = await Assert.ThrowsAnyAsync(() => { - var instance = CreateInstance(); + return instance.ExecuteUnderLockAsync((ct) => { return Task.CompletedTask; }, CancellationToken.None); + }); - await instance.DisposeAsync(); + Assert.Equal(instance.DisposalToken, result.CancellationToken); + } - var result = await Assert.ThrowsAnyAsync(() => - { - return instance.ExecuteUnderLockAsync((ct) => { return Task.CompletedTask; }, CancellationToken.None); - }); + [Fact] + public async Task ExecuteUnderLockAsyncOfT_WhenDisposed_ThrowsOperationCancelled() + { + var instance = CreateInstance(); - Assert.Equal(instance.DisposalToken, result.CancellationToken); - } + await instance.DisposeAsync(); - [Fact] - public async Task ExecuteUnderLockAsyncOfT_WhenDisposed_ThrowsOperationCancelled() + var result = await Assert.ThrowsAnyAsync(() => { - var instance = CreateInstance(); + return instance.ExecuteUnderLockAsync((ct) => { return TaskResult.Null(); }, CancellationToken.None); + }); - await instance.DisposeAsync(); + Assert.Equal(instance.DisposalToken, result.CancellationToken); + } - var result = await Assert.ThrowsAnyAsync(() => - { - return instance.ExecuteUnderLockAsync((ct) => { return TaskResult.Null(); }, CancellationToken.None); - }); + private static async Task AssertNoOverlap(Func firstAction, Func secondAction, AsyncManualResetEvent firstEntered, AsyncManualResetEvent firstRelease, AsyncManualResetEvent secondEntered) + { + // Run first task and wait until we've entered it + var firstTask = firstAction(); + await firstEntered.WaitAsync(); - Assert.Equal(instance.DisposalToken, result.CancellationToken); - } + // Run second task, we should never enter it + var secondTask = secondAction(); + await Assert.ThrowsAsync(() => secondEntered.WaitAsync().WithTimeout(TimeSpan.FromMilliseconds(50))); - private static async Task AssertNoOverlap(Func firstAction, Func secondAction, AsyncManualResetEvent firstEntered, AsyncManualResetEvent firstRelease, AsyncManualResetEvent secondEntered) - { - // Run first task and wait until we've entered it - var firstTask = firstAction(); - await firstEntered.WaitAsync(); + // Now release first + firstRelease.Set(); - // Run second task, we should never enter it - var secondTask = secondAction(); - await Assert.ThrowsAsync(() => secondEntered.WaitAsync().WithTimeout(TimeSpan.FromMilliseconds(50))); + // Now we should enter first one + await secondEntered.WaitAsync(); + await Task.WhenAll(firstTask, secondTask); + } - // Now release first - firstRelease.Set(); + private static ConcreteOnceInitializedOnceDisposedUnderLockAsync CreateInstance(Func? disposed = null) + { + var threadingService = IProjectThreadingServiceFactory.Create(); - // Now we should enter first one - await secondEntered.WaitAsync(); - await Task.WhenAll(firstTask, secondTask); - } + return new ConcreteOnceInitializedOnceDisposedUnderLockAsync(threadingService.JoinableTaskContext, disposed); + } + + private class ConcreteOnceInitializedOnceDisposedUnderLockAsync : OnceInitializedOnceDisposedUnderLockAsync + { + private readonly Func _disposed; - private static ConcreteOnceInitializedOnceDisposedUnderLockAsync CreateInstance(Func? disposed = null) + public ConcreteOnceInitializedOnceDisposedUnderLockAsync(JoinableTaskContextNode joinableTaskContextNode, Func? disposed) + : base(joinableTaskContextNode) { - var threadingService = IProjectThreadingServiceFactory.Create(); + disposed ??= () => Task.CompletedTask; - return new ConcreteOnceInitializedOnceDisposedUnderLockAsync(threadingService.JoinableTaskContext, disposed); + _disposed = disposed; } - private class ConcreteOnceInitializedOnceDisposedUnderLockAsync : OnceInitializedOnceDisposedUnderLockAsync + public new CancellationToken DisposalToken { - private readonly Func _disposed; - - public ConcreteOnceInitializedOnceDisposedUnderLockAsync(JoinableTaskContextNode joinableTaskContextNode, Func? disposed) - : base(joinableTaskContextNode) - { - disposed ??= () => Task.CompletedTask; - - _disposed = disposed; - } - - public new CancellationToken DisposalToken - { - get { return base.DisposalToken; } - } + get { return base.DisposalToken; } + } - public new Task ExecuteUnderLockAsync(Func action, CancellationToken cancellationToken = default) - { - return base.ExecuteUnderLockAsync(action, cancellationToken); - } + public new Task ExecuteUnderLockAsync(Func action, CancellationToken cancellationToken = default) + { + return base.ExecuteUnderLockAsync(action, cancellationToken); + } - public new Task ExecuteUnderLockAsync(Func> action, CancellationToken cancellationToken = default) - { - return base.ExecuteUnderLockAsync(action, cancellationToken); - } + public new Task ExecuteUnderLockAsync(Func> action, CancellationToken cancellationToken = default) + { + return base.ExecuteUnderLockAsync(action, cancellationToken); + } - protected override Task DisposeCoreUnderLockAsync(bool initialized) - { - return _disposed(); - } + protected override Task DisposeCoreUnderLockAsync(bool initialized) + { + return _disposed(); + } - protected override Task InitializeCoreAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } + protected override Task InitializeCoreAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceMocked.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceMocked.cs index 1a26012ea2..4522ed3727 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceMocked.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceMocked.cs @@ -2,31 +2,30 @@ using Microsoft.VisualStudio.IO; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +internal class PackageRestoreDataSourceMocked : PackageRestoreDataSource { - internal class PackageRestoreDataSourceMocked : PackageRestoreDataSource + public PackageRestoreDataSourceMocked( + UnconfiguredProject project, + PackageRestoreSharedJoinableTaskCollection sharedJoinableTaskCollection, + IPackageRestoreUnconfiguredInputDataSource dataSource, + IProjectAsynchronousTasksService projectAsynchronousTasksService, + IFileSystem fileSystem, + IManagedProjectDiagnosticOutputService logger, + INuGetRestoreService nuGetRestoreService, + IPackageRestoreCycleDetector cycleDetector) + : base(project, sharedJoinableTaskCollection, dataSource, projectAsynchronousTasksService, fileSystem, logger, nuGetRestoreService, cycleDetector) { - public PackageRestoreDataSourceMocked( - UnconfiguredProject project, - PackageRestoreSharedJoinableTaskCollection sharedJoinableTaskCollection, - IPackageRestoreUnconfiguredInputDataSource dataSource, - IProjectAsynchronousTasksService projectAsynchronousTasksService, - IFileSystem fileSystem, - IManagedProjectDiagnosticOutputService logger, - INuGetRestoreService nuGetRestoreService, - IPackageRestoreCycleDetector cycleDetector) - : base(project, sharedJoinableTaskCollection, dataSource, projectAsynchronousTasksService, fileSystem, logger, nuGetRestoreService, cycleDetector) - { - } + } - protected override bool IsRestoreDataVersionOutOfDate(IImmutableDictionary dataVersions) - { - return false; - } + protected override bool IsRestoreDataVersionOutOfDate(IImmutableDictionary dataVersions) + { + return false; + } - protected override bool IsProjectConfigurationVersionOutOfDate(IReadOnlyCollection? configuredInputs) - { - return false; - } + protected override bool IsProjectConfigurationVersionOutOfDate(IReadOnlyCollection? configuredInputs) + { + return false; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceTests.cs index 9f11b836bb..aa3df07c96 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreDataSourceTests.cs @@ -3,120 +3,119 @@ using Microsoft.VisualStudio.IO; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.PackageRestore; + +public class PackageRestoreDataSourceTests { - public class PackageRestoreDataSourceTests + [Fact] + public async Task Dispose_WhenNotInitialized_DoesNotThrow() { - [Fact] - public async Task Dispose_WhenNotInitialized_DoesNotThrow() - { - var instance = CreateInstance(); - - await instance.DisposeAsync(); - - Assert.True(instance.IsDisposed); - } - - [Fact] - public async Task Dispose_WhenInitialized_DoesNotThrow() - { - var instance = CreateInitializedInstance(); + var instance = CreateInstance(); - await instance.DisposeAsync(); + await instance.DisposeAsync(); - Assert.True(instance.IsDisposed); - } + Assert.True(instance.IsDisposed); + } - [Fact] - public async Task RestoreAsync_PushesRestoreInfoToRestoreService() - { - ProjectRestoreInfo? result = null; - var nugetRestoreService = INuGetRestoreServiceFactory.ImplementNominateProjectAsync((restoreInfo, versionInfo, cancellationToken) => { result = restoreInfo; }); + [Fact] + public async Task Dispose_WhenInitialized_DoesNotThrow() + { + var instance = CreateInitializedInstance(); - var instance = CreateInitializedInstance(nugetRestoreService: nugetRestoreService); + await instance.DisposeAsync(); - var restoreInfo = ProjectRestoreInfoFactory.Create(msbuildProjectExtensionsPath: @"C:\Alpha\Beta"); - var configuredInputs = PackageRestoreConfiguredInputFactory.Create(restoreInfo); - var activeConfiguration = ProjectConfigurationFactory.Create("Debug|AnyCPU"); + Assert.True(instance.IsDisposed); + } - var value = IProjectVersionedValueFactory.Create(new PackageRestoreUnconfiguredInput(restoreInfo, configuredInputs, activeConfiguration)); + [Fact] + public async Task RestoreAsync_PushesRestoreInfoToRestoreService() + { + ProjectRestoreInfo? result = null; + var nugetRestoreService = INuGetRestoreServiceFactory.ImplementNominateProjectAsync((restoreInfo, versionInfo, cancellationToken) => { result = restoreInfo; }); - await instance.RestoreAsync(value); + var instance = CreateInitializedInstance(nugetRestoreService: nugetRestoreService); - Assert.NotNull(result); - Assert.Equal(expected: restoreInfo.MSBuildProjectExtensionsPath, actual: result.MSBuildProjectExtensionsPath); - } + var restoreInfo = ProjectRestoreInfoFactory.Create(msbuildProjectExtensionsPath: @"C:\Alpha\Beta"); + var configuredInputs = PackageRestoreConfiguredInputFactory.Create(restoreInfo); + var activeConfiguration = ProjectConfigurationFactory.Create("Debug|AnyCPU"); - [Fact] - public async Task RestoreAsync_NullAsRestoreInfo_DoesNotPushToRestoreService() - { - int callCount = 0; - var nugetRestoreService = INuGetRestoreServiceFactory.ImplementNominateProjectAsync((restoreInfo, versionInfo, cancellationToken) => { callCount++; }); + var value = IProjectVersionedValueFactory.Create(new PackageRestoreUnconfiguredInput(restoreInfo, configuredInputs, activeConfiguration)); - var instance = CreateInitializedInstance(nugetRestoreService: nugetRestoreService); - var activeConfiguration = ProjectConfigurationFactory.Create("Debug|AnyCPU"); + await instance.RestoreAsync(value); - var value = IProjectVersionedValueFactory.Create(new PackageRestoreUnconfiguredInput(null, [], activeConfiguration)); + Assert.NotNull(result); + Assert.Equal(expected: restoreInfo.MSBuildProjectExtensionsPath, actual: result.MSBuildProjectExtensionsPath); + } - await instance.RestoreAsync(value); + [Fact] + public async Task RestoreAsync_NullAsRestoreInfo_DoesNotPushToRestoreService() + { + int callCount = 0; + var nugetRestoreService = INuGetRestoreServiceFactory.ImplementNominateProjectAsync((restoreInfo, versionInfo, cancellationToken) => { callCount++; }); - Assert.Equal(0, callCount); - } + var instance = CreateInitializedInstance(nugetRestoreService: nugetRestoreService); + var activeConfiguration = ProjectConfigurationFactory.Create("Debug|AnyCPU"); - [Fact] - public async Task RestoreAsync_UnchangedValueAsValue_DoesNotPushToRestoreService() - { - int callCount = 0; - var nugetRestoreService = INuGetRestoreServiceFactory.ImplementNominateProjectAsync((restoreInfo, versionInfo, cancellationToken) => { callCount++; }); + var value = IProjectVersionedValueFactory.Create(new PackageRestoreUnconfiguredInput(null, [], activeConfiguration)); - var instance = CreateInitializedInstance(nugetRestoreService: nugetRestoreService); + await instance.RestoreAsync(value); - var restoreInfo = ProjectRestoreInfoFactory.Create(); - var activeConfiguration = ProjectConfigurationFactory.Create("Debug|AnyCPU"); + Assert.Equal(0, callCount); + } - var value = IProjectVersionedValueFactory.Create(new PackageRestoreUnconfiguredInput(restoreInfo, [], activeConfiguration)); + [Fact] + public async Task RestoreAsync_UnchangedValueAsValue_DoesNotPushToRestoreService() + { + int callCount = 0; + var nugetRestoreService = INuGetRestoreServiceFactory.ImplementNominateProjectAsync((restoreInfo, versionInfo, cancellationToken) => { callCount++; }); - await instance.RestoreAsync(value); + var instance = CreateInitializedInstance(nugetRestoreService: nugetRestoreService); - Assert.Equal(1, callCount); // Should have only been called once - } + var restoreInfo = ProjectRestoreInfoFactory.Create(); + var activeConfiguration = ProjectConfigurationFactory.Create("Debug|AnyCPU"); - private static PackageRestoreDataSource CreateInitializedInstance(UnconfiguredProject? project = null, IPackageRestoreUnconfiguredInputDataSource? dataSource = null, INuGetRestoreService? nugetRestoreService = null) - { - var instance = CreateInstance(project, dataSource, nugetRestoreService); - instance.LoadAsync(); + var value = IProjectVersionedValueFactory.Create(new PackageRestoreUnconfiguredInput(restoreInfo, [], activeConfiguration)); - return instance; - } + await instance.RestoreAsync(value); - private static PackageRestoreDataSource CreateInstance( - UnconfiguredProject? project = null, - IPackageRestoreUnconfiguredInputDataSource? dataSource = null, - INuGetRestoreService? nuGetRestoreService = null, - bool featureFlagEnabled = false, - bool isCycleDetected = false) - { - project ??= UnconfiguredProjectFactory.CreateWithActiveConfiguredProjectProvider(IProjectThreadingServiceFactory.Create()); - var sharedJoinableTaskCollection = new PackageRestoreSharedJoinableTaskCollection(IProjectThreadingServiceFactory.Create()); + Assert.Equal(1, callCount); // Should have only been called once + } - dataSource ??= IPackageRestoreUnconfiguredInputDataSourceFactory.Create(); - IProjectAsynchronousTasksService projectAsynchronousTasksService = IProjectAsynchronousTasksServiceFactory.Create(); - IFileSystem fileSystem = IFileSystemFactory.Create(); - IManagedProjectDiagnosticOutputService logger = IManagedProjectDiagnosticOutputServiceFactory.Create(); - nuGetRestoreService ??= INuGetRestoreServiceFactory.Create(); + private static PackageRestoreDataSource CreateInitializedInstance(UnconfiguredProject? project = null, IPackageRestoreUnconfiguredInputDataSource? dataSource = null, INuGetRestoreService? nugetRestoreService = null) + { + var instance = CreateInstance(project, dataSource, nugetRestoreService); + instance.LoadAsync(); - var cycleDetector = new Mock(); - cycleDetector.Setup(o => o.IsCycleDetectedAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(isCycleDetected); + return instance; + } - return new PackageRestoreDataSourceMocked( - project, - sharedJoinableTaskCollection, - dataSource, - projectAsynchronousTasksService, - fileSystem, - logger, - nuGetRestoreService, - cycleDetector.Object); - } + private static PackageRestoreDataSource CreateInstance( + UnconfiguredProject? project = null, + IPackageRestoreUnconfiguredInputDataSource? dataSource = null, + INuGetRestoreService? nuGetRestoreService = null, + bool featureFlagEnabled = false, + bool isCycleDetected = false) + { + project ??= UnconfiguredProjectFactory.CreateWithActiveConfiguredProjectProvider(IProjectThreadingServiceFactory.Create()); + var sharedJoinableTaskCollection = new PackageRestoreSharedJoinableTaskCollection(IProjectThreadingServiceFactory.Create()); + + dataSource ??= IPackageRestoreUnconfiguredInputDataSourceFactory.Create(); + IProjectAsynchronousTasksService projectAsynchronousTasksService = IProjectAsynchronousTasksServiceFactory.Create(); + IFileSystem fileSystem = IFileSystemFactory.Create(); + IManagedProjectDiagnosticOutputService logger = IManagedProjectDiagnosticOutputServiceFactory.Create(); + nuGetRestoreService ??= INuGetRestoreServiceFactory.Create(); + + var cycleDetector = new Mock(); + cycleDetector.Setup(o => o.IsCycleDetectedAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(isCycleDetected); + + return new PackageRestoreDataSourceMocked( + project, + sharedJoinableTaskCollection, + dataSource, + projectAsynchronousTasksService, + fileSystem, + logger, + nuGetRestoreService, + cycleDetector.Object); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreProgressTrackerInstanceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreProgressTrackerInstanceTests.cs index a8d95e520a..8bd5eaeaac 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreProgressTrackerInstanceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PackageRestore/PackageRestoreProgressTrackerInstanceTests.cs @@ -3,151 +3,150 @@ using Microsoft.VisualStudio.ProjectSystem.PackageRestore; using static Microsoft.VisualStudio.ProjectSystem.PackageRestore.PackageRestoreProgressTracker; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PackageRestore +namespace Microsoft.VisualStudio.ProjectSystem.VS.PackageRestore; + +public class PackageRestoreProgressTrackerInstanceTests { - public class PackageRestoreProgressTrackerInstanceTests + [Fact] + public async Task Dispose_WhenNotInitialized_DoesNotThrow() { - [Fact] - public async Task Dispose_WhenNotInitialized_DoesNotThrow() - { - var instance = CreateInstance(); - - await instance.DisposeAsync(); - - Assert.True(instance.IsDisposed); - } + var instance = CreateInstance(); - [Fact] - public async Task Dispose_WhenInitialized_DoesNotThrow() - { - var instance = await CreateInitializedInstance(); + await instance.DisposeAsync(); - await instance.DisposeAsync(); + Assert.True(instance.IsDisposed); + } - Assert.True(instance.IsDisposed); - } + [Fact] + public async Task Dispose_WhenInitialized_DoesNotThrow() + { + var instance = await CreateInitializedInstance(); - [Fact] - public async Task OnRestoreCompleted_WhenEvaluationRanWithOlderAssetsFile_IsNotUpToDate() - { - string projectAssetsFile = @"C:\Project\obj\project.assets.json"; + await instance.DisposeAsync(); - var currentTimestamp = DateTime.Now; - var evaluationTimestamp = DateTime.Now.AddDays(-1); + Assert.True(instance.IsDisposed); + } - var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); - var snapshot = IProjectSnapshot2Factory.WithAdditionalDependentFileTime( - projectAssetsFile, - evaluationTimestamp); + [Fact] + public async Task OnRestoreCompleted_WhenEvaluationRanWithOlderAssetsFile_IsNotUpToDate() + { + string projectAssetsFile = @"C:\Project\obj\project.assets.json"; - var result = await OnRestoreCompleted(snapshot, restoreData); + var currentTimestamp = DateTime.Now; + var evaluationTimestamp = DateTime.Now.AddDays(-1); - Assert.False(result); - } + var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); + var snapshot = IProjectSnapshot2Factory.WithAdditionalDependentFileTime( + projectAssetsFile, + evaluationTimestamp); - [Fact] - public async Task OnRestoreCompleted_WhenEvaluationRanWithNewerAssetsFile_IsUpToDate() - { - string projectAssetsFile = @"C:\Project\obj\project.assets.json"; + var result = await OnRestoreCompleted(snapshot, restoreData); - var currentTimestamp = DateTime.Now; - var evaluationTimestamp = DateTime.Now.AddDays(1); + Assert.False(result); + } - var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); - var snapshot = IProjectSnapshot2Factory.WithAdditionalDependentFileTime( - projectAssetsFile, - evaluationTimestamp); + [Fact] + public async Task OnRestoreCompleted_WhenEvaluationRanWithNewerAssetsFile_IsUpToDate() + { + string projectAssetsFile = @"C:\Project\obj\project.assets.json"; - var result = await OnRestoreCompleted(snapshot, restoreData); + var currentTimestamp = DateTime.Now; + var evaluationTimestamp = DateTime.Now.AddDays(1); - Assert.True(result); - } + var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); + var snapshot = IProjectSnapshot2Factory.WithAdditionalDependentFileTime( + projectAssetsFile, + evaluationTimestamp); - [Fact] - public async Task OnRestoreCompleted_WhenEvaluationRanSameAssetsFile_IsUpToDate() - { - string projectAssetsFile = @"C:\Project\obj\project.assets.json"; + var result = await OnRestoreCompleted(snapshot, restoreData); - var currentTimestamp = DateTime.Now; - var evaluationTimestamp = currentTimestamp; + Assert.True(result); + } - var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); - var snapshot = IProjectSnapshot2Factory.WithAdditionalDependentFileTime( - projectAssetsFile, - evaluationTimestamp); + [Fact] + public async Task OnRestoreCompleted_WhenEvaluationRanSameAssetsFile_IsUpToDate() + { + string projectAssetsFile = @"C:\Project\obj\project.assets.json"; - var result = await OnRestoreCompleted(snapshot, restoreData); + var currentTimestamp = DateTime.Now; + var evaluationTimestamp = currentTimestamp; - Assert.True(result); - } + var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); + var snapshot = IProjectSnapshot2Factory.WithAdditionalDependentFileTime( + projectAssetsFile, + evaluationTimestamp); - [Fact] - public async Task OnRestoreCompleted_WhenEvaluationIsMissingProjectAssetsFile_IsUpToDate() - { - string projectAssetsFile = @"C:\Project\obj\project.assets.json"; + var result = await OnRestoreCompleted(snapshot, restoreData); - var currentTimestamp = DateTime.Now; + Assert.True(result); + } - var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); - var snapshot = IProjectSnapshot2Factory.Create(); + [Fact] + public async Task OnRestoreCompleted_WhenEvaluationIsMissingProjectAssetsFile_IsUpToDate() + { + string projectAssetsFile = @"C:\Project\obj\project.assets.json"; - var result = await OnRestoreCompleted(snapshot, restoreData); + var currentTimestamp = DateTime.Now; - Assert.True(result); - } + var restoreData = new RestoreData(projectAssetsFile, currentTimestamp); + var snapshot = IProjectSnapshot2Factory.Create(); - [Fact] - public async Task OnRestoreCompleted_WhenRestoreFailed_IsUpToDate() - { - var restoreData = new RestoreData(string.Empty, DateTime.MinValue, succeeded: false); - var snapshot = IProjectSnapshot2Factory.Create(); + var result = await OnRestoreCompleted(snapshot, restoreData); - var result = await OnRestoreCompleted(snapshot, restoreData); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task OnRestoreCompleted_WhenRestoreFailed_IsUpToDate() + { + var restoreData = new RestoreData(string.Empty, DateTime.MinValue, succeeded: false); + var snapshot = IProjectSnapshot2Factory.Create(); - private async Task OnRestoreCompleted(IProjectSnapshot projectSnapshot, RestoreData restoreData) - { - bool result = false; - var dataProgressTrackerService = IDataProgressTrackerServiceFactory.ImplementNotifyOutputDataCalculated(_ => { result = true; }); + var result = await OnRestoreCompleted(snapshot, restoreData); - var instance = await CreateInitializedInstance(dataProgressTrackerService: dataProgressTrackerService); + Assert.True(result); + } - var tuple = ValueTuple.Create(projectSnapshot, restoreData); - var value = IProjectVersionedValueFactory.Create(tuple); + private async Task OnRestoreCompleted(IProjectSnapshot projectSnapshot, RestoreData restoreData) + { + bool result = false; + var dataProgressTrackerService = IDataProgressTrackerServiceFactory.ImplementNotifyOutputDataCalculated(_ => { result = true; }); - instance.OnRestoreCompleted(value); + var instance = await CreateInitializedInstance(dataProgressTrackerService: dataProgressTrackerService); - return result; - } + var tuple = ValueTuple.Create(projectSnapshot, restoreData); + var value = IProjectVersionedValueFactory.Create(tuple); - private async Task CreateInitializedInstance(ConfiguredProject? project = null, IDataProgressTrackerService? dataProgressTrackerService = null, IPackageRestoreDataSource? packageRestoreService = null, IProjectSubscriptionService? projectSubscriptionService = null) - { - var instance = CreateInstance(project, dataProgressTrackerService, packageRestoreService, projectSubscriptionService); + instance.OnRestoreCompleted(value); - await instance.InitializeAsync(); + return result; + } - return instance; - } + private async Task CreateInitializedInstance(ConfiguredProject? project = null, IDataProgressTrackerService? dataProgressTrackerService = null, IPackageRestoreDataSource? packageRestoreService = null, IProjectSubscriptionService? projectSubscriptionService = null) + { + var instance = CreateInstance(project, dataProgressTrackerService, packageRestoreService, projectSubscriptionService); - private PackageRestoreProgressTrackerInstance CreateInstance(ConfiguredProject? project = null, IDataProgressTrackerService? dataProgressTrackerService = null, IPackageRestoreDataSource? packageRestoreDataSource = null, IProjectSubscriptionService? projectSubscriptionService = null) - { - project ??= ConfiguredProjectFactory.Create(); - dataProgressTrackerService ??= IDataProgressTrackerServiceFactory.Create(); - packageRestoreDataSource ??= IPackageRestoreServiceFactory.Create(); - projectSubscriptionService ??= IProjectSubscriptionServiceFactory.Create(); + await instance.InitializeAsync(); - IProjectThreadingService threadingService = IProjectThreadingServiceFactory.Create(); - IProjectFaultHandlerService projectFaultHandlerService = IProjectFaultHandlerServiceFactory.Create(); + return instance; + } - return new PackageRestoreProgressTrackerInstance( - project, - threadingService, - projectFaultHandlerService, - dataProgressTrackerService, - packageRestoreDataSource, - projectSubscriptionService); - } + private PackageRestoreProgressTrackerInstance CreateInstance(ConfiguredProject? project = null, IDataProgressTrackerService? dataProgressTrackerService = null, IPackageRestoreDataSource? packageRestoreDataSource = null, IProjectSubscriptionService? projectSubscriptionService = null) + { + project ??= ConfiguredProjectFactory.Create(); + dataProgressTrackerService ??= IDataProgressTrackerServiceFactory.Create(); + packageRestoreDataSource ??= IPackageRestoreServiceFactory.Create(); + projectSubscriptionService ??= IProjectSubscriptionServiceFactory.Create(); + + IProjectThreadingService threadingService = IProjectThreadingServiceFactory.Create(); + IProjectFaultHandlerService projectFaultHandlerService = IProjectFaultHandlerServiceFactory.Create(); + + return new PackageRestoreProgressTrackerInstance( + project, + threadingService, + projectFaultHandlerService, + dataProgressTrackerService, + packageRestoreDataSource, + projectSubscriptionService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeStorageTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeStorageTests.cs index c74685295d..d8b4d0127f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeStorageTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeStorageTests.cs @@ -2,324 +2,323 @@ using Microsoft.VisualStudio.IO; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class PhysicalProjectTreeStorageTests { - public class PhysicalProjectTreeStorageTests + [Fact] + public async Task AddFileAsync_NullAsPath_ThrowsArgumentNull() { - [Fact] - public async Task AddFileAsync_NullAsPath_ThrowsArgumentNull() + var storage = CreateInstance(); + + await Assert.ThrowsAsync("path", () => { - var storage = CreateInstance(); + return storage.AddFileAsync(null!); + }); + } - await Assert.ThrowsAsync("path", () => - { - return storage.AddFileAsync(null!); - }); - } + [Fact] + public async Task AddFileAsync_EmptyAsPath_ThrowsArgument() + { + var storage = CreateInstance(); - [Fact] - public async Task AddFileAsync_EmptyAsPath_ThrowsArgument() + await Assert.ThrowsAsync("path", () => { - var storage = CreateInstance(); + return storage.AddFileAsync(string.Empty); + }); + } - await Assert.ThrowsAsync("path", () => - { - return storage.AddFileAsync(string.Empty); - }); - } + [Fact] + public async Task CreateEmptyFileAsync_NullAsPath_ThrowsArgumentNull() + { + var storage = CreateInstance(); - [Fact] - public async Task CreateEmptyFileAsync_NullAsPath_ThrowsArgumentNull() + await Assert.ThrowsAsync("path", () => { - var storage = CreateInstance(); + return storage.CreateEmptyFileAsync(null!); + }); + } - await Assert.ThrowsAsync("path", () => - { - return storage.CreateEmptyFileAsync(null!); - }); - } + [Fact] + public async Task CreateEmptyFileAsync_EmptyAsPath_ThrowsArgument() + { + var storage = CreateInstance(); - [Fact] - public async Task CreateEmptyFileAsync_EmptyAsPath_ThrowsArgument() + await Assert.ThrowsAsync("path", () => { - var storage = CreateInstance(); + return storage.CreateEmptyFileAsync(string.Empty); + }); + } - await Assert.ThrowsAsync("path", () => - { - return storage.CreateEmptyFileAsync(string.Empty); - }); - } + [Fact] + public async Task CreateFolderAsync_NullAsPath_ThrowsArgumentNull() + { + var storage = CreateInstance(); - [Fact] - public async Task CreateFolderAsync_NullAsPath_ThrowsArgumentNull() + await Assert.ThrowsAsync("path", async () => { - var storage = CreateInstance(); + await storage.CreateFolderAsync(null!); + }); + } - await Assert.ThrowsAsync("path", async () => - { - await storage.CreateFolderAsync(null!); - }); - } + [Fact] + public async Task CreateFolderAsync_EmptyAsPath_ThrowsArgument() + { + var storage = CreateInstance(); - [Fact] - public async Task CreateFolderAsync_EmptyAsPath_ThrowsArgument() + await Assert.ThrowsAsync("path", async () => { - var storage = CreateInstance(); + await storage.CreateFolderAsync(string.Empty); + }); + } - await Assert.ThrowsAsync("path", async () => - { - await storage.CreateFolderAsync(string.Empty); - }); - } + [Fact] + public async Task AddFolderAsync_NullAsPath_ThrowsArgumentNull() + { + var storage = CreateInstance(); - [Fact] - public async Task AddFolderAsync_NullAsPath_ThrowsArgumentNull() + await Assert.ThrowsAsync("path", async () => { - var storage = CreateInstance(); + await storage.AddFolderAsync(null!); + }); + } - await Assert.ThrowsAsync("path", async () => - { - await storage.AddFolderAsync(null!); - }); - } + [Fact] + public async Task AddFolderAsync_EmptyAsPath_ThrowsArgument() + { + var storage = CreateInstance(); - [Fact] - public async Task AddFolderAsync_EmptyAsPath_ThrowsArgument() + await Assert.ThrowsAsync("path", async () => { - var storage = CreateInstance(); + await storage.AddFolderAsync(string.Empty); + }); + } - await Assert.ThrowsAsync("path", async () => - { - await storage.AddFolderAsync(string.Empty); - }); - } + [Fact] + public async Task CreateEmptyFileAsync_CreatesFileOnDisk() + { + string? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Project\Project.csproj"); + var fileSystem = IFileSystemFactory.ImplementCreate((path) => { result = path; }); - [Fact] - public async Task CreateEmptyFileAsync_CreatesFileOnDisk() - { - string? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Project\Project.csproj"); - var fileSystem = IFileSystemFactory.ImplementCreate((path) => { result = path; }); + var storage = CreateInstance(fileSystem: fileSystem, project: project); - var storage = CreateInstance(fileSystem: fileSystem, project: project); + await storage.CreateEmptyFileAsync(@"Properties\File.cs"); - await storage.CreateEmptyFileAsync(@"Properties\File.cs"); + Assert.Equal(@"C:\Project\Properties\File.cs", result); + } - Assert.Equal(@"C:\Project\Properties\File.cs", result); - } + [Fact] + public async Task CreateFolderAsync_CreatesFolderOnDisk() + { + string? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); + var fileSystem = IFileSystemFactory.ImplementCreateDirectory((path) => { result = path; }); - [Fact] - public async Task CreateFolderAsync_CreatesFolderOnDisk() - { - string? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); - var fileSystem = IFileSystemFactory.ImplementCreateDirectory((path) => { result = path; }); + var storage = CreateInstance(fileSystem: fileSystem, project: project); - var storage = CreateInstance(fileSystem: fileSystem, project: project); + await storage.CreateFolderAsync("Folder"); - await storage.CreateFolderAsync("Folder"); + Assert.Equal(@"C:\Folder", result); + } - Assert.Equal(@"C:\Folder", result); - } + [Fact] + public async Task CreateEmptyFileAsync_AddsFileToProject() + { + string? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Project.csproj"); - [Fact] - public async Task CreateEmptyFileAsync_AddsFileToProject() - { - string? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Project.csproj"); + var sourceItemsProvider = IProjectItemProviderFactory.AddItemAsync(path => { result = path; return null!; }); + var storage = CreateInstance(sourceItemsProvider: sourceItemsProvider, project: project); - var sourceItemsProvider = IProjectItemProviderFactory.AddItemAsync(path => { result = path; return null!; }); - var storage = CreateInstance(sourceItemsProvider: sourceItemsProvider, project: project); + await storage.CreateEmptyFileAsync("File.cs"); - await storage.CreateEmptyFileAsync("File.cs"); + Assert.Equal(@"C:\File.cs", result); + } + + [Fact] + public async Task AddFileAsync_AddsFileToProject() + { + string? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Project.csproj"); - Assert.Equal(@"C:\File.cs", result); - } + var sourceItemsProvider = IProjectItemProviderFactory.AddItemAsync(path => { result = path; return null!; }); + var storage = CreateInstance(sourceItemsProvider: sourceItemsProvider, project: project); - [Fact] - public async Task AddFileAsync_AddsFileToProject() - { - string? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Project.csproj"); + await storage.AddFileAsync("File.cs"); - var sourceItemsProvider = IProjectItemProviderFactory.AddItemAsync(path => { result = path; return null!; }); - var storage = CreateInstance(sourceItemsProvider: sourceItemsProvider, project: project); + Assert.Equal(@"C:\File.cs", result); + } + + [Fact] + public async Task CreateFolderAsync_IncludesFolderInProject() + { + string? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); + var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, recursive) => { result = path; return Task.CompletedTask; }); - await storage.AddFileAsync("File.cs"); + var storage = CreateInstance(folderManager: folderManager, project: project); - Assert.Equal(@"C:\File.cs", result); - } + await storage.CreateFolderAsync("Folder"); - [Fact] - public async Task CreateFolderAsync_IncludesFolderInProject() - { - string? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); - var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, recursive) => { result = path; return Task.CompletedTask; }); + Assert.Equal(@"C:\Folder", result); + } - var storage = CreateInstance(folderManager: folderManager, project: project); + [Fact] + public async Task AddFolderAsync_IncludesFolderInProject() + { + string? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); + var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, recursive) => { result = path; return Task.CompletedTask; }); - await storage.CreateFolderAsync("Folder"); + var storage = CreateInstance(folderManager: folderManager, project: project); - Assert.Equal(@"C:\Folder", result); - } + await storage.AddFolderAsync("Folder"); - [Fact] - public async Task AddFolderAsync_IncludesFolderInProject() - { - string? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); - var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, recursive) => { result = path; return Task.CompletedTask; }); + Assert.Equal(@"C:\Folder", result); + } - var storage = CreateInstance(folderManager: folderManager, project: project); + [Fact] + public async Task CreateFolderAsync_IncludesFolderInProjectNonRecursively() + { + bool? result = null; + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); + var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, recursive) => { result = recursive; return Task.CompletedTask; }); - await storage.AddFolderAsync("Folder"); + var storage = CreateInstance(folderManager: folderManager, project: project); - Assert.Equal(@"C:\Folder", result); - } + await storage.CreateFolderAsync("Folder"); - [Fact] - public async Task CreateFolderAsync_IncludesFolderInProjectNonRecursively() - { - bool? result = null; - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Root.csproj"); - var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, recursive) => { result = recursive; return Task.CompletedTask; }); - - var storage = CreateInstance(folderManager: folderManager, project: project); - - await storage.CreateFolderAsync("Folder"); - - Assert.False(result); - } - - [Theory] - [InlineData(@"C:\Project.csproj", @"Properties\File.cs", @"C:\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties\File.cs", @"C:\Projects\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\File.cs", @"C:\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\File.cs", @"C:\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\File.cs", @"D:\Properties\File.cs")] - [InlineData(@"C:\Project.csproj", @"Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder\File.cs", @"C:\Projects\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder\File.cs", @"D:\Properties\Folder\File.cs")] - [InlineData(@"C:\Project.csproj", @"Folder With Spaces\File.cs", @"C:\Folder With Spaces\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder\File.cs", @"C:\Projects\Folder With Spaces\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder\File.cs", @"D:\Folder With Spaces\Folder\File.cs")] - public async Task AddFileAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) - { - var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); - string? result = null; - var sourceItemsProvider = IProjectItemProviderFactory.AddItemAsync(path => { result = path; return null!; }); - var storage = CreateInstance(sourceItemsProvider: sourceItemsProvider, project: project); - - await storage.AddFileAsync(input); - - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(@"C:\Project.csproj", @"Properties\File.cs", @"C:\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties\File.cs", @"C:\Projects\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\File.cs", @"C:\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\File.cs", @"C:\Properties\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\File.cs", @"D:\Properties\File.cs")] - [InlineData(@"C:\Project.csproj", @"Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder\File.cs", @"C:\Projects\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder\File.cs", @"D:\Properties\Folder\File.cs")] - [InlineData(@"C:\Project.csproj", @"Folder With Spaces\File.cs", @"C:\Folder With Spaces\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder\File.cs", @"C:\Projects\Folder With Spaces\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder\File.cs", @"D:\Folder With Spaces\Folder\File.cs")] - public async Task CreateEmptyFileAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) - { - var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); - string? result = null; - var fileSystem = IFileSystemFactory.ImplementCreate(path => { result = path; }); - - var storage = CreateInstance(fileSystem: fileSystem, project: project); - - await storage.CreateEmptyFileAsync(input); - - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(@"C:\Project.csproj", @"Properties", @"C:\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties", @"C:\Projects\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties", @"C:\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties", @"C:\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties", @"D:\Properties")] - [InlineData(@"C:\Project.csproj", @"Properties\Folder", @"C:\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder", @"C:\Projects\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder", @"C:\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder", @"C:\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder", @"D:\Properties\Folder")] - [InlineData(@"C:\Project.csproj", @"Folder With Spaces", @"C:\Folder With Spaces")] - [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder", @"C:\Projects\Folder With Spaces\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder", @"D:\Folder With Spaces\Folder")] - public async Task CreateFolderAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) - { - var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); - string? result = null; - var fileSystem = IFileSystemFactory.ImplementCreateDirectory(path => { result = path; }); - - var storage = CreateInstance(fileSystem: fileSystem, project: project); - - await storage.CreateFolderAsync(input); - - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(@"C:\Project.csproj", @"Properties", @"C:\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties", @"C:\Projects\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties", @"C:\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties", @"C:\Properties")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties", @"D:\Properties")] - [InlineData(@"C:\Project.csproj", @"Properties\Folder", @"C:\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder", @"C:\Projects\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder", @"C:\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder", @"C:\Properties\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder", @"D:\Properties\Folder")] - [InlineData(@"C:\Project.csproj", @"Folder With Spaces", @"C:\Folder With Spaces")] - [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder", @"C:\Projects\Folder With Spaces\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] - [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder", @"D:\Folder With Spaces\Folder")] - public async Task AddFolderAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) - { - var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); - string? result = null; - var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, _) => { result = path; }); + Assert.False(result); + } + + [Theory] + [InlineData(@"C:\Project.csproj", @"Properties\File.cs", @"C:\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties\File.cs", @"C:\Projects\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\File.cs", @"C:\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\File.cs", @"C:\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\File.cs", @"D:\Properties\File.cs")] + [InlineData(@"C:\Project.csproj", @"Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder\File.cs", @"C:\Projects\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder\File.cs", @"D:\Properties\Folder\File.cs")] + [InlineData(@"C:\Project.csproj", @"Folder With Spaces\File.cs", @"C:\Folder With Spaces\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder\File.cs", @"C:\Projects\Folder With Spaces\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder\File.cs", @"D:\Folder With Spaces\Folder\File.cs")] + public async Task AddFileAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) + { + var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); + string? result = null; + var sourceItemsProvider = IProjectItemProviderFactory.AddItemAsync(path => { result = path; return null!; }); + var storage = CreateInstance(sourceItemsProvider: sourceItemsProvider, project: project); - var storage = CreateInstance(folderManager: folderManager, project: project); + await storage.AddFileAsync(input); - await storage.AddFolderAsync(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(@"C:\Project.csproj", @"Properties\File.cs", @"C:\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties\File.cs", @"C:\Projects\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\File.cs", @"C:\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\File.cs", @"C:\Properties\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\File.cs", @"D:\Properties\File.cs")] + [InlineData(@"C:\Project.csproj", @"Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder\File.cs", @"C:\Projects\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder\File.cs", @"C:\Properties\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder\File.cs", @"D:\Properties\Folder\File.cs")] + [InlineData(@"C:\Project.csproj", @"Folder With Spaces\File.cs", @"C:\Folder With Spaces\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder\File.cs", @"C:\Projects\Folder With Spaces\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder\File.cs", @"C:\Folder With Spaces\Folder\File.cs")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder\File.cs", @"D:\Folder With Spaces\Folder\File.cs")] + public async Task CreateEmptyFileAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) + { + var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); + string? result = null; + var fileSystem = IFileSystemFactory.ImplementCreate(path => { result = path; }); - Assert.Equal(expected, result); - } + var storage = CreateInstance(fileSystem: fileSystem, project: project); - private static PhysicalProjectTreeStorage CreateInstance(IProjectTreeService? projectTreeService = null, IProjectItemProvider? sourceItemsProvider = null, IFileSystem? fileSystem = null, IFolderManager? folderManager = null, UnconfiguredProject? project = null) - { - projectTreeService ??= IProjectTreeServiceFactory.Create(ProjectTreeParser.Parse("Root")); - fileSystem ??= IFileSystemFactory.Create(); - folderManager ??= IFolderManagerFactory.Create(); - sourceItemsProvider ??= IProjectItemProviderFactory.Create(); - project ??= UnconfiguredProjectFactory.Create(); - - return new PhysicalProjectTreeStorage( - project, - projectTreeService, - new Lazy(() => fileSystem), - IActiveConfiguredValueFactory.ImplementValue(() => new PhysicalProjectTreeStorage.ConfiguredImports(folderManager, sourceItemsProvider))); - } + await storage.CreateEmptyFileAsync(input); + + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(@"C:\Project.csproj", @"Properties", @"C:\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties", @"C:\Projects\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties", @"C:\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties", @"C:\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties", @"D:\Properties")] + [InlineData(@"C:\Project.csproj", @"Properties\Folder", @"C:\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder", @"C:\Projects\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder", @"C:\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder", @"C:\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder", @"D:\Properties\Folder")] + [InlineData(@"C:\Project.csproj", @"Folder With Spaces", @"C:\Folder With Spaces")] + [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder", @"C:\Projects\Folder With Spaces\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder", @"D:\Folder With Spaces\Folder")] + public async Task CreateFolderAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) + { + var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); + string? result = null; + var fileSystem = IFileSystemFactory.ImplementCreateDirectory(path => { result = path; }); + + var storage = CreateInstance(fileSystem: fileSystem, project: project); + + await storage.CreateFolderAsync(input); + + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(@"C:\Project.csproj", @"Properties", @"C:\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties", @"C:\Projects\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties", @"C:\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties", @"C:\Properties")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties", @"D:\Properties")] + [InlineData(@"C:\Project.csproj", @"Properties\Folder", @"C:\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"Properties\Folder", @"C:\Projects\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Properties\Folder", @"C:\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Properties\Folder", @"C:\Properties\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Properties\Folder", @"D:\Properties\Folder")] + [InlineData(@"C:\Project.csproj", @"Folder With Spaces", @"C:\Folder With Spaces")] + [InlineData(@"C:\Projects\Project.csproj", @"Folder With Spaces\Folder", @"C:\Projects\Folder With Spaces\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"..\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"C:\Folder With Spaces\Folder", @"C:\Folder With Spaces\Folder")] + [InlineData(@"C:\Projects\Project.csproj", @"D:\Folder With Spaces\Folder", @"D:\Folder With Spaces\Folder")] + public async Task AddFolderAsync_ValueAsPath_IsCalculatedRelativeToProjectDirectory(string projectPath, string input, string expected) + { + var project = UnconfiguredProjectFactory.Create(fullPath: projectPath); + string? result = null; + var folderManager = IFolderManagerFactory.IncludeFolderInProjectAsync((path, _) => { result = path; }); + + var storage = CreateInstance(folderManager: folderManager, project: project); + + await storage.AddFolderAsync(input); + + Assert.Equal(expected, result); + } + + private static PhysicalProjectTreeStorage CreateInstance(IProjectTreeService? projectTreeService = null, IProjectItemProvider? sourceItemsProvider = null, IFileSystem? fileSystem = null, IFolderManager? folderManager = null, UnconfiguredProject? project = null) + { + projectTreeService ??= IProjectTreeServiceFactory.Create(ProjectTreeParser.Parse("Root")); + fileSystem ??= IFileSystemFactory.Create(); + folderManager ??= IFolderManagerFactory.Create(); + sourceItemsProvider ??= IProjectItemProviderFactory.Create(); + project ??= UnconfiguredProjectFactory.Create(); + + return new PhysicalProjectTreeStorage( + project, + projectTreeService, + new Lazy(() => fileSystem), + IActiveConfiguredValueFactory.ImplementValue(() => new PhysicalProjectTreeStorage.ConfiguredImports(folderManager, sourceItemsProvider))); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeTests.cs index 14ac605743..87cfd614ad 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/PhysicalProjectTreeTests.cs @@ -1,86 +1,85 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class PhysicalProjectTreeTests { - public class PhysicalProjectTreeTests + [Fact] + public void Constructor_ValueAsTreeService_SetTreeServiceProperty() + { + var projectTreeService = new Lazy(IProjectTreeServiceFactory.Create); + var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); + var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); + + var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); + + Assert.Same(projectTreeService.Value, projectTree.TreeService); + } + + [Fact] + public void Constructor_ValueAsTreeProvider_SetTreeProviderProperty() { - [Fact] - public void Constructor_ValueAsTreeService_SetTreeServiceProperty() - { - var projectTreeService = new Lazy(IProjectTreeServiceFactory.Create); - var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); - var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); - - var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); - - Assert.Same(projectTreeService.Value, projectTree.TreeService); - } - - [Fact] - public void Constructor_ValueAsTreeProvider_SetTreeProviderProperty() - { - var projectTreeService = new Lazy(IProjectTreeServiceFactory.Create); - var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); - var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); - - var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); - - Assert.Same(projectTreeProvider.Value, projectTree.TreeProvider); - } - - [Fact] - public void Constructor_ValueAsTreeStorage_SetTreeStorageProperty() - { - var projectTreeService = new Lazy(IProjectTreeServiceFactory.Create); - var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); - var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); - - var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); - - Assert.Same(projectTreeStorage.Value, projectTree.TreeStorage); - } - - [Fact] - public void Constructor_WhenTreeServiceCurrentTreeIsNull_SetsCurrentTreeToNull() - { - var projectTreeService = new Lazy(() => IProjectTreeServiceFactory.ImplementCurrentTree(() => null)); - var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); - var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); - - var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); - - Assert.Null(projectTree.CurrentTree); - } - - [Fact] - public void Constructor_WhenTreeServiceCurrentTreeTreeIsNull_SetsCurrentTreeToNull() - { - var projectTreeServiceState = IProjectTreeServiceStateFactory.ImplementTree(() => null); - var projectTreeService = new Lazy(() => IProjectTreeServiceFactory.ImplementCurrentTree(() => projectTreeServiceState)); - var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); - var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); - - var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); - - Assert.Null(projectTree.CurrentTree); - } - - [Fact] - public void Constructor_ValueAsTreeService_SetsCurrentTreeToTreeServiceCurrentTreeTree() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - """); - - var projectTreeServiceState = IProjectTreeServiceStateFactory.ImplementTree(() => tree); - var projectTreeService = new Lazy(() => IProjectTreeServiceFactory.ImplementCurrentTree(() => projectTreeServiceState)); - var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); - var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); - - var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); - - Assert.Same(tree, projectTree.CurrentTree); - } + var projectTreeService = new Lazy(IProjectTreeServiceFactory.Create); + var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); + var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); + + var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); + + Assert.Same(projectTreeProvider.Value, projectTree.TreeProvider); + } + + [Fact] + public void Constructor_ValueAsTreeStorage_SetTreeStorageProperty() + { + var projectTreeService = new Lazy(IProjectTreeServiceFactory.Create); + var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); + var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); + + var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); + + Assert.Same(projectTreeStorage.Value, projectTree.TreeStorage); + } + + [Fact] + public void Constructor_WhenTreeServiceCurrentTreeIsNull_SetsCurrentTreeToNull() + { + var projectTreeService = new Lazy(() => IProjectTreeServiceFactory.ImplementCurrentTree(() => null)); + var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); + var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); + + var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); + + Assert.Null(projectTree.CurrentTree); + } + + [Fact] + public void Constructor_WhenTreeServiceCurrentTreeTreeIsNull_SetsCurrentTreeToNull() + { + var projectTreeServiceState = IProjectTreeServiceStateFactory.ImplementTree(() => null); + var projectTreeService = new Lazy(() => IProjectTreeServiceFactory.ImplementCurrentTree(() => projectTreeServiceState)); + var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); + var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); + + var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); + + Assert.Null(projectTree.CurrentTree); + } + + [Fact] + public void Constructor_ValueAsTreeService_SetsCurrentTreeToTreeServiceCurrentTreeTree() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + """); + + var projectTreeServiceState = IProjectTreeServiceStateFactory.ImplementTree(() => tree); + var projectTreeService = new Lazy(() => IProjectTreeServiceFactory.ImplementCurrentTree(() => projectTreeServiceState)); + var projectTreeProvider = new Lazy(() => IProjectTreeProviderFactory.Create()); + var projectTreeStorage = new Lazy(IPhysicalProjectTreeStorageFactory.Create); + + var projectTree = new PhysicalProjectTree(projectTreeService, projectTreeProvider, projectTreeStorage); + + Assert.Same(tree, projectTree.CurrentTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ProjectConfigurationExtensionsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ProjectConfigurationExtensionsTests.cs index bede7ab0e6..d9a328ec4b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ProjectConfigurationExtensionsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/ProjectConfigurationExtensionsTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public sealed class ProjectConfigurationExtensionsTests { - public sealed class ProjectConfigurationExtensionsTests + [Theory] + [InlineData("Debug|AnyCPU")] + [InlineData("Release|AnyCPU")] + [InlineData("Debug|AnyCPU|net48")] + [InlineData("Debug|AnyCPU|net6.0")] + public void GetDisplayString(string pattern) { - [Theory] - [InlineData("Debug|AnyCPU")] - [InlineData("Release|AnyCPU")] - [InlineData("Debug|AnyCPU|net48")] - [InlineData("Debug|AnyCPU|net6.0")] - public void GetDisplayString(string pattern) - { - Assert.Equal(pattern, ProjectConfigurationFactory.Create(pattern).GetDisplayString()); - } + Assert.Equal(pattern, ProjectConfigurationFactory.Create(pattern).GetDisplayString()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/AssemblyInfoPropertiesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/AssemblyInfoPropertiesProviderTests.cs index 8a17017d21..7407c2637f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/AssemblyInfoPropertiesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/AssemblyInfoPropertiesProviderTests.cs @@ -4,378 +4,377 @@ using Microsoft.VisualStudio.ProjectSystem.Properties.Package; using Microsoft.VisualStudio.Workspaces; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal class TestProjectFileOrAssemblyInfoPropertiesProvider : AbstractProjectFileOrAssemblyInfoPropertiesProvider { - internal class TestProjectFileOrAssemblyInfoPropertiesProvider : AbstractProjectFileOrAssemblyInfoPropertiesProvider + public TestProjectFileOrAssemblyInfoPropertiesProvider( + UnconfiguredProject? project = null, + Lazy? interceptingProvider = null, + Workspace? workspace = null, + IProjectThreadingService? threadingService = null, + IProjectProperties? defaultProperties = null, + IProjectInstancePropertiesProvider? instanceProvider = null, + Func? getActiveProjectId = null) + : this(workspace ?? WorkspaceFactory.Create(""), + project: project ?? UnconfiguredProjectFactory.Create(), + interceptingProvider: interceptingProvider, + threadingService: threadingService, + defaultProperties: defaultProperties, + instanceProvider: instanceProvider, + getActiveProjectId: getActiveProjectId) { - public TestProjectFileOrAssemblyInfoPropertiesProvider( - UnconfiguredProject? project = null, - Lazy? interceptingProvider = null, - Workspace? workspace = null, - IProjectThreadingService? threadingService = null, - IProjectProperties? defaultProperties = null, - IProjectInstancePropertiesProvider? instanceProvider = null, - Func? getActiveProjectId = null) - : this(workspace ?? WorkspaceFactory.Create(""), - project: project ?? UnconfiguredProjectFactory.Create(), - interceptingProvider: interceptingProvider, - threadingService: threadingService, - defaultProperties: defaultProperties, - instanceProvider: instanceProvider, - getActiveProjectId: getActiveProjectId) - { - } + } - public TestProjectFileOrAssemblyInfoPropertiesProvider( - Workspace workspace, - UnconfiguredProject project, - Lazy? interceptingProvider = null, - IProjectThreadingService? threadingService = null, - IProjectProperties? defaultProperties = null, - IProjectInstancePropertiesProvider? instanceProvider = null, - Func? getActiveProjectId = null) - : base(delegatedProvider: IProjectPropertiesProviderFactory.Create(defaultProperties ?? IProjectPropertiesFactory.MockWithProperty("").Object), - instanceProvider: instanceProvider ?? IProjectInstancePropertiesProviderFactory.Create(), - interceptingValueProviders: interceptingProvider is null - ? new[] { new Lazy( - () => IInterceptingPropertyValueProviderFactory.Create(), - IInterceptingPropertyValueProviderMetadataFactory.Create("TestPropertyName")) } - : new[] { interceptingProvider }, - project: project, - getActiveProjectId: getActiveProjectId ?? (() => workspace.CurrentSolution.ProjectIds.SingleOrDefault()), - workspace: workspace, - threadingService: threadingService ?? IProjectThreadingServiceFactory.Create()) - { - Requires.NotNull(workspace); - Requires.NotNull(project); - } + public TestProjectFileOrAssemblyInfoPropertiesProvider( + Workspace workspace, + UnconfiguredProject project, + Lazy? interceptingProvider = null, + IProjectThreadingService? threadingService = null, + IProjectProperties? defaultProperties = null, + IProjectInstancePropertiesProvider? instanceProvider = null, + Func? getActiveProjectId = null) + : base(delegatedProvider: IProjectPropertiesProviderFactory.Create(defaultProperties ?? IProjectPropertiesFactory.MockWithProperty("").Object), + instanceProvider: instanceProvider ?? IProjectInstancePropertiesProviderFactory.Create(), + interceptingValueProviders: interceptingProvider is null + ? new[] { new Lazy( + () => IInterceptingPropertyValueProviderFactory.Create(), + IInterceptingPropertyValueProviderMetadataFactory.Create("TestPropertyName")) } + : new[] { interceptingProvider }, + project: project, + getActiveProjectId: getActiveProjectId ?? (() => workspace.CurrentSolution.ProjectIds.SingleOrDefault()), + workspace: workspace, + threadingService: threadingService ?? IProjectThreadingServiceFactory.Create()) + { + Requires.NotNull(workspace); + Requires.NotNull(project); } +} - public class AssemblyInfoPropertiesProviderTests +public class AssemblyInfoPropertiesProviderTests +{ + private static TestProjectFileOrAssemblyInfoPropertiesProvider CreateProviderForSourceFileValidation( + string code, + out Workspace workspace, + Lazy? interceptingProvider = null, + Dictionary? additionalProps = null) { - private static TestProjectFileOrAssemblyInfoPropertiesProvider CreateProviderForSourceFileValidation( - string code, - out Workspace workspace, - Lazy? interceptingProvider = null, - Dictionary? additionalProps = null) - { - var language = code.Contains("[") ? LanguageNames.CSharp : LanguageNames.VisualBasic; - workspace = WorkspaceFactory.Create(code, language); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - var project = UnconfiguredProjectFactory.Create(fullPath: projectFilePath); - var defaultProperties = CreateProjectProperties(additionalProps, saveInProjectFile: false); - return new TestProjectFileOrAssemblyInfoPropertiesProvider(project, workspace: workspace, defaultProperties: defaultProperties, interceptingProvider: interceptingProvider); - } + var language = code.Contains("[") ? LanguageNames.CSharp : LanguageNames.VisualBasic; + workspace = WorkspaceFactory.Create(code, language); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + var project = UnconfiguredProjectFactory.Create(fullPath: projectFilePath); + var defaultProperties = CreateProjectProperties(additionalProps, saveInProjectFile: false); + return new TestProjectFileOrAssemblyInfoPropertiesProvider(project, workspace: workspace, defaultProperties: defaultProperties, interceptingProvider: interceptingProvider); + } - private static TestProjectFileOrAssemblyInfoPropertiesProvider CreateProviderForProjectFileValidation( - string code, - string propertyName, - string propertyValueInProjectFile, - out Workspace workspace, - Lazy? interceptingProvider = null, - Dictionary? additionalProps = null) - { - var language = code.Contains("[") ? LanguageNames.CSharp : LanguageNames.VisualBasic; - workspace = WorkspaceFactory.Create(code, language); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - var project = UnconfiguredProjectFactory.Create(fullPath: projectFilePath); - additionalProps ??= new Dictionary(); - additionalProps[propertyName] = propertyValueInProjectFile; - var defaultProperties = CreateProjectProperties(additionalProps, saveInProjectFile: true); - return new TestProjectFileOrAssemblyInfoPropertiesProvider(project, workspace: workspace, defaultProperties: defaultProperties, interceptingProvider: interceptingProvider); - } + private static TestProjectFileOrAssemblyInfoPropertiesProvider CreateProviderForProjectFileValidation( + string code, + string propertyName, + string propertyValueInProjectFile, + out Workspace workspace, + Lazy? interceptingProvider = null, + Dictionary? additionalProps = null) + { + var language = code.Contains("[") ? LanguageNames.CSharp : LanguageNames.VisualBasic; + workspace = WorkspaceFactory.Create(code, language); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + var project = UnconfiguredProjectFactory.Create(fullPath: projectFilePath); + additionalProps ??= new Dictionary(); + additionalProps[propertyName] = propertyValueInProjectFile; + var defaultProperties = CreateProjectProperties(additionalProps, saveInProjectFile: true); + return new TestProjectFileOrAssemblyInfoPropertiesProvider(project, workspace: workspace, defaultProperties: defaultProperties, interceptingProvider: interceptingProvider); + } - private static IProjectProperties CreateProjectProperties(Dictionary? additionalProps, bool saveInProjectFile) - { - additionalProps ??= new Dictionary(); + private static IProjectProperties CreateProjectProperties(Dictionary? additionalProps, bool saveInProjectFile) + { + additionalProps ??= new Dictionary(); - // Configure whether AssemblyInfo properties are generated in project file or not. - var saveInProjectFileStr = saveInProjectFile.ToString(); - foreach (var kvp in AssemblyInfoProperties.AssemblyPropertyInfoMap) - { - var generatePropertyInProjectFileName = kvp.Value.generatePropertyInProjectFileName; - additionalProps[generatePropertyInProjectFileName] = saveInProjectFileStr; - } + // Configure whether AssemblyInfo properties are generated in project file or not. + var saveInProjectFileStr = saveInProjectFile.ToString(); + foreach (var kvp in AssemblyInfoProperties.AssemblyPropertyInfoMap) + { + var generatePropertyInProjectFileName = kvp.Value.generatePropertyInProjectFileName; + additionalProps[generatePropertyInProjectFileName] = saveInProjectFileStr; + } - additionalProps["GenerateAssemblyInfo"] = saveInProjectFileStr; + additionalProps["GenerateAssemblyInfo"] = saveInProjectFileStr; - return IProjectPropertiesFactory.MockWithPropertiesAndValues(additionalProps).Object; - } + return IProjectPropertiesFactory.MockWithPropertiesAndValues(additionalProps).Object; + } - [Fact] - public void DefaultProjectPath() - { - var provider = new TestProjectFileOrAssemblyInfoPropertiesProvider(UnconfiguredProjectFactory.Create(fullPath: "D:\\TestFile")); - Assert.Equal("D:\\TestFile", provider.DefaultProjectPath); - } + [Fact] + public void DefaultProjectPath() + { + var provider = new TestProjectFileOrAssemblyInfoPropertiesProvider(UnconfiguredProjectFactory.Create(fullPath: "D:\\TestFile")); + Assert.Equal("D:\\TestFile", provider.DefaultProjectPath); + } - [Fact] - public void GetProperties_NotNull() - { - var provider = new TestProjectFileOrAssemblyInfoPropertiesProvider(); - var properties = provider.GetProperties("file", null, null); - Assert.NotNull(properties); - } + [Fact] + public void GetProperties_NotNull() + { + var provider = new TestProjectFileOrAssemblyInfoPropertiesProvider(); + var properties = provider.GetProperties("file", null, null); + Assert.NotNull(properties); + } - [Theory] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription")] - [InlineData("""[assembly: System.Reflection.AssemblyCompanyAttribute("MyCompany")]""", "Company", "MyCompany")] - [InlineData("""[assembly: System.Reflection.AssemblyProductAttribute("MyProduct")]""", "Product", "MyProduct")] - [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("MyVersion")]""", "AssemblyVersion", "MyVersion")] - [InlineData("""[assembly: System.Resources.NeutralResourcesLanguageAttribute("en-us")]""", "NeutralLanguage", "en-us")] - // Negative cases - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "SomeProperty", "")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Company", "")] - [InlineData("""[assembly: System.Runtime.InteropServices.AssemblyDescriptionAttribute(true)]""", "Description", "")] - [InlineData("""[assembly: System.Runtime.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "")] - public async Task SourceFileProperties_GetEvaluatedPropertyAsync(string code, string propertyName, string expectedValue) - { - var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + [Theory] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription")] + [InlineData("""[assembly: System.Reflection.AssemblyCompanyAttribute("MyCompany")]""", "Company", "MyCompany")] + [InlineData("""[assembly: System.Reflection.AssemblyProductAttribute("MyProduct")]""", "Product", "MyProduct")] + [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("MyVersion")]""", "AssemblyVersion", "MyVersion")] + [InlineData("""[assembly: System.Resources.NeutralResourcesLanguageAttribute("en-us")]""", "NeutralLanguage", "en-us")] + // Negative cases + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "SomeProperty", "")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Company", "")] + [InlineData("""[assembly: System.Runtime.InteropServices.AssemblyDescriptionAttribute(true)]""", "Description", "")] + [InlineData("""[assembly: System.Runtime.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "")] + public async Task SourceFileProperties_GetEvaluatedPropertyAsync(string code, string propertyName, string expectedValue) + { + var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + var properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedValue, propertyValue); - } + Assert.Equal(expectedValue, propertyValue); + } - [Theory] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription", "MyDescription")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription2", "MyDescription2")] - [InlineData("", "Description", "MyDescription", "MyDescription")] - [InlineData("""[assembly: System.Reflection.AssemblyCompanyAttribute("MyCompany")]""", "Company", "MyCompany2", "MyCompany2")] - [InlineData("""[assembly: System.Reflection.AssemblyProductAttribute("MyProduct")]""", "Product", "MyProduct2", "MyProduct2")] - [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("MyVersion")]""", "AssemblyVersion", "MyVersion2", "MyVersion2")] - [InlineData("""[assembly: System.Resources.NeutralResourcesLanguageAttribute("en-us")]""", "NeutralResourcesLanguage", "en-uk", "en-uk")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute(true)]""", "Description", "MyDescription", "MyDescription")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription"]""", "Description", "", "")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription"]""", "Description", null, "")] - public async Task ProjectFileProperties_GetEvaluatedPropertyAsync(string code, string propertyName, string propertyValueInProjectFile, string expectedValue) - { - var provider = CreateProviderForProjectFileValidation(code, propertyName, propertyValueInProjectFile, out Workspace workspace); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + [Theory] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription", "MyDescription")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription2", "MyDescription2")] + [InlineData("", "Description", "MyDescription", "MyDescription")] + [InlineData("""[assembly: System.Reflection.AssemblyCompanyAttribute("MyCompany")]""", "Company", "MyCompany2", "MyCompany2")] + [InlineData("""[assembly: System.Reflection.AssemblyProductAttribute("MyProduct")]""", "Product", "MyProduct2", "MyProduct2")] + [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("MyVersion")]""", "AssemblyVersion", "MyVersion2", "MyVersion2")] + [InlineData("""[assembly: System.Resources.NeutralResourcesLanguageAttribute("en-us")]""", "NeutralResourcesLanguage", "en-uk", "en-uk")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute(true)]""", "Description", "MyDescription", "MyDescription")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription"]""", "Description", "", "")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription"]""", "Description", null, "")] + public async Task ProjectFileProperties_GetEvaluatedPropertyAsync(string code, string propertyName, string propertyValueInProjectFile, string expectedValue) + { + var provider = CreateProviderForProjectFileValidation(code, propertyName, propertyValueInProjectFile, out Workspace workspace); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + var properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedValue, propertyValue); - } + Assert.Equal(expectedValue, propertyValue); + } - [Theory] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "NewDescription", - """[assembly: System.Reflection.AssemblyDescriptionAttribute("NewDescription")]""")] - [InlineData("""""", "Description", "NewDescription", - """""")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute(/*Trivia*/ "MyDescription" /*Trivia*/)]""", "Description", "NewDescription", - """[assembly: System.Reflection.AssemblyDescriptionAttribute(/*Trivia*/ "NewDescription" /*Trivia*/)]""")] - [InlineData("""""", "Description", "NewDescription", - """""")] - //Negative cases - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Product", "NewDescription", - """[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "SomeRandomProperty", "NewDescription", - """[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription", "MyDescription")]""", "Description", "NewDescription", - """[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription", "MyDescription")]""")] - [InlineData("""[assembly: System.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "NewDescription", - """[assembly: System.AssemblyDescriptionAttribute("MyDescription")]""")] - public async Task SourceFileProperties_SetPropertyValueAsync(string code, string propertyName, string propertyValue, string expectedCode) - { - using var _ = SynchronizationContextUtil.Suppress(); + [Theory] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "NewDescription", + """[assembly: System.Reflection.AssemblyDescriptionAttribute("NewDescription")]""")] + [InlineData("""""", "Description", "NewDescription", + """""")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute(/*Trivia*/ "MyDescription" /*Trivia*/)]""", "Description", "NewDescription", + """[assembly: System.Reflection.AssemblyDescriptionAttribute(/*Trivia*/ "NewDescription" /*Trivia*/)]""")] + [InlineData("""""", "Description", "NewDescription", + """""")] + //Negative cases + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Product", "NewDescription", + """[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "SomeRandomProperty", "NewDescription", + """[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription", "MyDescription")]""", "Description", "NewDescription", + """[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription", "MyDescription")]""")] + [InlineData("""[assembly: System.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "NewDescription", + """[assembly: System.AssemblyDescriptionAttribute("MyDescription")]""")] + public async Task SourceFileProperties_SetPropertyValueAsync(string code, string propertyName, string propertyValue, string expectedCode) + { + using var _ = SynchronizationContextUtil.Suppress(); - var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); - await properties.SetPropertyValueAsync(propertyName, propertyValue); + var properties = provider.GetProperties(projectFilePath, null, null); + await properties.SetPropertyValueAsync(propertyName, propertyValue); - var newCode = (await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync()).ToString(); - Assert.Equal(expectedCode, newCode); - } + var newCode = (await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync()).ToString(); + Assert.Equal(expectedCode, newCode); + } - [Theory] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "", "NewDescription", "NewDescription")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "", "", "")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "OldDescription", "NewDescription", "NewDescription")] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription", "MyDescription")]""", "Description", "OldDescription", "", "")] - public async Task ProjectFileProperties_SetPropertyValueAsync(string code, string propertyName, string existingPropertyValue, string propertyValueToSet, string expectedValue) - { - var propertyValues = new Dictionary(); - var provider = CreateProviderForProjectFileValidation(code, propertyName, existingPropertyValue, out Workspace workspace, additionalProps: propertyValues); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + [Theory] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "", "NewDescription", "NewDescription")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "", "", "")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "OldDescription", "NewDescription", "NewDescription")] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription", "MyDescription")]""", "Description", "OldDescription", "", "")] + public async Task ProjectFileProperties_SetPropertyValueAsync(string code, string propertyName, string existingPropertyValue, string propertyValueToSet, string expectedValue) + { + var propertyValues = new Dictionary(); + var provider = CreateProviderForProjectFileValidation(code, propertyName, existingPropertyValue, out Workspace workspace, additionalProps: propertyValues); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); + var properties = provider.GetProperties(projectFilePath, null, null); - string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - Assert.Equal(existingPropertyValue, propertyValue); + string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + Assert.Equal(existingPropertyValue, propertyValue); - await properties.SetPropertyValueAsync(propertyName, propertyValueToSet); + await properties.SetPropertyValueAsync(propertyName, propertyValueToSet); - // Confirm the new value. - propertyValue = propertyValues[propertyName]; - Assert.Equal(expectedValue, propertyValue); + // Confirm the new value. + propertyValue = propertyValues[propertyName]; + Assert.Equal(expectedValue, propertyValue); - // Verify no code changes as property was written to project file. - var newCode = (await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync()).ToString(); - Assert.Equal(code, newCode); - } + // Verify no code changes as property was written to project file. + var newCode = (await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync()).ToString(); + Assert.Equal(code, newCode); + } - [Theory] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription")] - public async Task SourceFileProperties_GetUnevaluatedPropertyAsync(string code, string propertyName, string expectedValue) - { - var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + [Theory] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription")] + public async Task SourceFileProperties_GetUnevaluatedPropertyAsync(string code, string propertyName, string expectedValue) + { + var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); + var properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedValue, propertyValue); - } + Assert.Equal(expectedValue, propertyValue); + } - [Theory] - [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription2", "MyDescription2")] - [InlineData("", "Description", "MyDescription", "MyDescription")] - public async Task ProjectFileProperties_GetUnevaluatedPropertyAsync(string code, string propertyName, string propertyValueInProjectFile, string expectedValue) - { - var provider = CreateProviderForProjectFileValidation(code, propertyName, propertyValueInProjectFile, out Workspace workspace); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + [Theory] + [InlineData("""[assembly: System.Reflection.AssemblyDescriptionAttribute("MyDescription")]""", "Description", "MyDescription2", "MyDescription2")] + [InlineData("", "Description", "MyDescription", "MyDescription")] + public async Task ProjectFileProperties_GetUnevaluatedPropertyAsync(string code, string propertyName, string propertyValueInProjectFile, string expectedValue) + { + var provider = CreateProviderForProjectFileValidation(code, propertyName, propertyValueInProjectFile, out Workspace workspace); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); + var properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedValue, propertyValue); - } + Assert.Equal(expectedValue, propertyValue); + } - [Theory] - // AssemblyVersion - [InlineData("", "AssemblyVersion", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("1.1.1")]""", "AssemblyVersion", "1.1.1", typeof(AssemblyVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("")]""", "AssemblyVersion", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("random")]""", "AssemblyVersion", "random", typeof(AssemblyVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.0")]""", "AssemblyVersion", "2.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.1-beta1")]""", "AssemblyVersion", "2.0.1.0", typeof(AssemblyVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2016.2")]""", "AssemblyVersion", "2016.2.0.0", typeof(AssemblyVersionValueProvider))] - // FileVersion - [InlineData("", "FileVersion", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyFileVersionAttribute("1.1.1")]""", "FileVersion", "1.1.1", typeof(FileVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyFileVersionAttribute("")]""", "FileVersion", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyFileVersionAttribute("random")]""", "FileVersion", "random", typeof(FileVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.0")]""", "FileVersion", "2.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.1-beta1")]""", "FileVersion", "2.0.1.0", typeof(FileVersionValueProvider))] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2016.2")]""", "FileVersion", "2016.2.0.0", typeof(FileVersionValueProvider))] - // Version - [InlineData("", "Version", "", null)] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.1.1")]""", "Version", "1.1.1", null)] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("")]""", "Version", "", null)] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("random")]""", "Version", "random", null)] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.0")]""", "Version", "2.0.0", null)] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.1-beta1")]""", "Version", "2.0.1-beta1", null)] - [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2016.2")]""", "Version", "2016.2", null)] - internal async Task SourceFileProperties_DefaultValues_GetEvaluatedPropertyAsync(string code, string propertyName, string expectedValue, Type interceptingProviderType) - { - var interceptingProvider = interceptingProviderType is not null - ? new Lazy( - valueFactory: () => (IInterceptingPropertyValueProvider)Activator.CreateInstance(interceptingProviderType), - metadata: IInterceptingPropertyValueProviderMetadataFactory.Create(propertyName)) - : null; - var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace, interceptingProvider); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); - - var properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - - Assert.Equal(expectedValue, propertyValue); - } + [Theory] + // AssemblyVersion + [InlineData("", "AssemblyVersion", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("1.1.1")]""", "AssemblyVersion", "1.1.1", typeof(AssemblyVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("")]""", "AssemblyVersion", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyVersionAttribute("random")]""", "AssemblyVersion", "random", typeof(AssemblyVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.0")]""", "AssemblyVersion", "2.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.1-beta1")]""", "AssemblyVersion", "2.0.1.0", typeof(AssemblyVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2016.2")]""", "AssemblyVersion", "2016.2.0.0", typeof(AssemblyVersionValueProvider))] + // FileVersion + [InlineData("", "FileVersion", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyFileVersionAttribute("1.1.1")]""", "FileVersion", "1.1.1", typeof(FileVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyFileVersionAttribute("")]""", "FileVersion", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyFileVersionAttribute("random")]""", "FileVersion", "random", typeof(FileVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.0")]""", "FileVersion", "2.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.1-beta1")]""", "FileVersion", "2.0.1.0", typeof(FileVersionValueProvider))] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2016.2")]""", "FileVersion", "2016.2.0.0", typeof(FileVersionValueProvider))] + // Version + [InlineData("", "Version", "", null)] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.1.1")]""", "Version", "1.1.1", null)] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("")]""", "Version", "", null)] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("random")]""", "Version", "random", null)] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.0")]""", "Version", "2.0.0", null)] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2.0.1-beta1")]""", "Version", "2.0.1-beta1", null)] + [InlineData("""[assembly: System.Reflection.AssemblyInformationalVersionAttribute("2016.2")]""", "Version", "2016.2", null)] + internal async Task SourceFileProperties_DefaultValues_GetEvaluatedPropertyAsync(string code, string propertyName, string expectedValue, Type interceptingProviderType) + { + var interceptingProvider = interceptingProviderType is not null + ? new Lazy( + valueFactory: () => (IInterceptingPropertyValueProvider)Activator.CreateInstance(interceptingProviderType), + metadata: IInterceptingPropertyValueProviderMetadataFactory.Create(propertyName)) + : null; + var provider = CreateProviderForSourceFileValidation(code, out Workspace workspace, interceptingProvider); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); + + var properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + + Assert.Equal(expectedValue, propertyValue); + } - [Theory] - // PackageId - [InlineData("MyApp", "PackageId", null, "")] - [InlineData("MyApp", "PackageId", "", "")] - [InlineData("MyApp", "PackageId", "ExistingValue", "ExistingValue")] - // Authors - [InlineData("MyApp", "Authors", null, "")] - [InlineData("MyApp", "Authors", "", "")] - [InlineData("MyApp", "Authors", "ExistingValue", "ExistingValue")] - // Product - [InlineData("MyApp", "Product", null, "")] - [InlineData("MyApp", "Product", "", "")] - [InlineData("MyApp", "Product", "ExistingValue", "ExistingValue")] - internal async Task ProjectFileProperties_DefaultValues_GetEvaluatedPropertyAsync(string assemblyName, string propertyName, string existingPropertyValue, string expectedValue) - { - var additionalProps = new Dictionary() { { "AssemblyName", assemblyName } }; + [Theory] + // PackageId + [InlineData("MyApp", "PackageId", null, "")] + [InlineData("MyApp", "PackageId", "", "")] + [InlineData("MyApp", "PackageId", "ExistingValue", "ExistingValue")] + // Authors + [InlineData("MyApp", "Authors", null, "")] + [InlineData("MyApp", "Authors", "", "")] + [InlineData("MyApp", "Authors", "ExistingValue", "ExistingValue")] + // Product + [InlineData("MyApp", "Product", null, "")] + [InlineData("MyApp", "Product", "", "")] + [InlineData("MyApp", "Product", "ExistingValue", "ExistingValue")] + internal async Task ProjectFileProperties_DefaultValues_GetEvaluatedPropertyAsync(string assemblyName, string propertyName, string existingPropertyValue, string expectedValue) + { + var additionalProps = new Dictionary() { { "AssemblyName", assemblyName } }; - string code = ""; - var provider = CreateProviderForProjectFileValidation(code, propertyName, existingPropertyValue, out Workspace workspace, additionalProps: additionalProps); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); + string code = ""; + var provider = CreateProviderForProjectFileValidation(code, propertyName, existingPropertyValue, out Workspace workspace, additionalProps: additionalProps); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); - var properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + var properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedValue, propertyValue); - } + Assert.Equal(expectedValue, propertyValue); + } - [Theory] - // AssemblyVersion - [InlineData("AssemblyVersion", null, "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("AssemblyVersion", "", "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("AssemblyVersion", null, "", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("AssemblyVersion", "1.0.0.0", "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("AssemblyVersion", "1.1.1", "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("AssemblyVersion", "1.0.0.0", "1.0.0", "1.0.0", typeof(AssemblyVersionValueProvider))] - [InlineData("AssemblyVersion", null, "2016.2", "2016.2", typeof(AssemblyVersionValueProvider))] - // FileVersion - [InlineData("FileVersion", null, "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("FileVersion", "", "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("FileVersion", null, "", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("FileVersion", "1.0.0.0", "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("FileVersion", "1.1.1", "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] - [InlineData("FileVersion", "1.0.0.0", "1.0.0", "1.0.0", typeof(FileVersionValueProvider))] - [InlineData("FileVersion", null, "2016.2", "2016.2", typeof(FileVersionValueProvider))] - // PackageVersion - [InlineData("Version", null, "1.0.0", "1.0.0", null)] - [InlineData("Version", null, "1.0.0-beta1", "1.0.0-beta1", null)] - [InlineData("Version", "", "1.0.0.0", "1.0.0.0", null)] - [InlineData("Version", "", "1.0.0-beta1", "1.0.0-beta1", null)] - [InlineData("Version", "1.0.0", "1.0.0", "1.0.0", null)] - [InlineData("Version", "1.0.0-beta1", "1.0.0", "1.0.0", null)] - [InlineData("Version", "1.0.0-beta1", "1.0.0-beta2", "1.0.0-beta2", null)] - [InlineData("Version", "1.0.0", "1.0.0-beta1", "1.0.0-beta1", null)] - [InlineData("Version", "1.1.1", "1.0.0.0", "1.0.0.0", null)] - [InlineData("Version", "1.0.0", "1.0.0.0", "1.0.0.0", null)] - [InlineData("Version", null, "2016.2", "2016.2", null)] - internal async Task ProjectFileProperties_WithInterception_SetEvaluatedPropertyAsync(string propertyName, string existingPropertyValue, string propertyValueToSet, string expectedValue, Type interceptingProviderType) - { - var interceptingProvider = interceptingProviderType is not null - ? new Lazy( - valueFactory: () => (IInterceptingPropertyValueProvider)Activator.CreateInstance(interceptingProviderType), - metadata: IInterceptingPropertyValueProviderMetadataFactory.Create(propertyName)) - : null; - - string code = ""; - var provider = CreateProviderForProjectFileValidation(code, propertyName, existingPropertyValue, out Workspace workspace, interceptingProvider); - var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; - Assumes.NotNull(projectFilePath); - - var properties = provider.GetProperties(projectFilePath, null, null); - await properties.SetPropertyValueAsync(propertyName, propertyValueToSet); - - // Read the property value again and confirm the new value. - properties = provider.GetProperties(projectFilePath, null, null); - var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedValue, propertyValue); - - // Verify no code changes as property was written to project file. - var newCode = (await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync()).ToString(); - Assert.Equal(code, newCode); - } + [Theory] + // AssemblyVersion + [InlineData("AssemblyVersion", null, "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("AssemblyVersion", "", "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("AssemblyVersion", null, "", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("AssemblyVersion", "1.0.0.0", "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("AssemblyVersion", "1.1.1", "1.0.0.0", "1.0.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("AssemblyVersion", "1.0.0.0", "1.0.0", "1.0.0", typeof(AssemblyVersionValueProvider))] + [InlineData("AssemblyVersion", null, "2016.2", "2016.2", typeof(AssemblyVersionValueProvider))] + // FileVersion + [InlineData("FileVersion", null, "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("FileVersion", "", "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("FileVersion", null, "", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("FileVersion", "1.0.0.0", "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("FileVersion", "1.1.1", "1.0.0.0", "1.0.0.0", typeof(FileVersionValueProvider))] + [InlineData("FileVersion", "1.0.0.0", "1.0.0", "1.0.0", typeof(FileVersionValueProvider))] + [InlineData("FileVersion", null, "2016.2", "2016.2", typeof(FileVersionValueProvider))] + // PackageVersion + [InlineData("Version", null, "1.0.0", "1.0.0", null)] + [InlineData("Version", null, "1.0.0-beta1", "1.0.0-beta1", null)] + [InlineData("Version", "", "1.0.0.0", "1.0.0.0", null)] + [InlineData("Version", "", "1.0.0-beta1", "1.0.0-beta1", null)] + [InlineData("Version", "1.0.0", "1.0.0", "1.0.0", null)] + [InlineData("Version", "1.0.0-beta1", "1.0.0", "1.0.0", null)] + [InlineData("Version", "1.0.0-beta1", "1.0.0-beta2", "1.0.0-beta2", null)] + [InlineData("Version", "1.0.0", "1.0.0-beta1", "1.0.0-beta1", null)] + [InlineData("Version", "1.1.1", "1.0.0.0", "1.0.0.0", null)] + [InlineData("Version", "1.0.0", "1.0.0.0", "1.0.0.0", null)] + [InlineData("Version", null, "2016.2", "2016.2", null)] + internal async Task ProjectFileProperties_WithInterception_SetEvaluatedPropertyAsync(string propertyName, string existingPropertyValue, string propertyValueToSet, string expectedValue, Type interceptingProviderType) + { + var interceptingProvider = interceptingProviderType is not null + ? new Lazy( + valueFactory: () => (IInterceptingPropertyValueProvider)Activator.CreateInstance(interceptingProviderType), + metadata: IInterceptingPropertyValueProviderMetadataFactory.Create(propertyName)) + : null; + + string code = ""; + var provider = CreateProviderForProjectFileValidation(code, propertyName, existingPropertyValue, out Workspace workspace, interceptingProvider); + var projectFilePath = workspace.CurrentSolution.Projects.First().FilePath; + Assumes.NotNull(projectFilePath); + + var properties = provider.GetProperties(projectFilePath, null, null); + await properties.SetPropertyValueAsync(propertyName, propertyValueToSet); + + // Read the property value again and confirm the new value. + properties = provider.GetProperties(projectFilePath, null, null); + var propertyValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + Assert.Equal(expectedValue, propertyValue); + + // Verify no code changes as property was written to project file. + var newCode = (await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync()).ToString(); + Assert.Equal(code, newCode); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/BasePropertyExtensionTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/BasePropertyExtensionTests.cs index 8a5f88b4b2..9375644a43 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/BasePropertyExtensionTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/BasePropertyExtensionTests.cs @@ -2,87 +2,86 @@ using Microsoft.Build.Framework.XamlTypes; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class BasePropertyExtensionTests { - public class BasePropertyExtensionTests + [Fact] + public void WhenThePropertyIsNull_GetMetadataValueThrows() { - [Fact] - public void WhenThePropertyIsNull_GetMetadataValueThrows() - { - TestProperty testProperty = null!; + TestProperty testProperty = null!; - Assert.Throws(() => testProperty.GetMetadataValueOrNull(metadataName: "DoesntMatter")); - } + Assert.Throws(() => testProperty.GetMetadataValueOrNull(metadataName: "DoesntMatter")); + } - [Fact] - public void WhenTheMetadataNameIsNull_GetMetadataValueThrows() - { + [Fact] + public void WhenTheMetadataNameIsNull_GetMetadataValueThrows() + { - TestProperty testProperty = new TestProperty() { Metadata = new() }; - string metadataName = null!; + TestProperty testProperty = new TestProperty() { Metadata = new() }; + string metadataName = null!; - Assert.Throws(() => testProperty.GetMetadataValueOrNull(metadataName)); - } + Assert.Throws(() => testProperty.GetMetadataValueOrNull(metadataName)); + } - [Fact] - public void WhenTheMetadataNameIsTheEmptyString_GetMetadataValueThrows() - { - TestProperty testProperty = new TestProperty() { Metadata = new() }; - string metadataName = string.Empty; + [Fact] + public void WhenTheMetadataNameIsTheEmptyString_GetMetadataValueThrows() + { + TestProperty testProperty = new TestProperty() { Metadata = new() }; + string metadataName = string.Empty; - Assert.Throws(() => testProperty.GetMetadataValueOrNull(metadataName)); - } + Assert.Throws(() => testProperty.GetMetadataValueOrNull(metadataName)); + } - [Fact] - public void WhenTheRequestedMetadataIsNotFound_GetMetadataValueReturnsNull() - { - TestProperty testProperty = new TestProperty() { Metadata = new() }; - string metadataName = "MyMetadata"; + [Fact] + public void WhenTheRequestedMetadataIsNotFound_GetMetadataValueReturnsNull() + { + TestProperty testProperty = new TestProperty() { Metadata = new() }; + string metadataName = "MyMetadata"; - Assert.Null(testProperty.GetMetadataValueOrNull(metadataName)); - } + Assert.Null(testProperty.GetMetadataValueOrNull(metadataName)); + } - [Fact] - public void WhenTheRequestedMetadataIsFound_GetMetadataValueReturnsTheValue() + [Fact] + public void WhenTheRequestedMetadataIsFound_GetMetadataValueReturnsTheValue() + { + TestProperty testProperty = new TestProperty() { - TestProperty testProperty = new TestProperty() + Metadata = new() { - Metadata = new() - { - new() { Name = "Alpha", Value = "Kangaroo" }, - new() { Name = "Beta", Value = "Wallaby" } - } - }; + new() { Name = "Alpha", Value = "Kangaroo" }, + new() { Name = "Beta", Value = "Wallaby" } + } + }; - string metadataName = "Beta"; + string metadataName = "Beta"; - Assert.Equal(expected: "Wallaby", actual: testProperty.GetMetadataValueOrNull(metadataName)); - } + Assert.Equal(expected: "Wallaby", actual: testProperty.GetMetadataValueOrNull(metadataName)); + } - [Fact] - public void WhenTheRequestedMetadataIsSpecifiedMoreThanOnce_GetMetadataValueReturnsTheFirstValue() + [Fact] + public void WhenTheRequestedMetadataIsSpecifiedMoreThanOnce_GetMetadataValueReturnsTheFirstValue() + { + TestProperty testProperty = new TestProperty() { - TestProperty testProperty = new TestProperty() + Metadata = new() { - Metadata = new() - { - new() { Name = "Beta", Value = "Wallaby" }, - new() { Name = "Alpha", Value = "Dingo" }, - new() { Name = "Alpha", Value = "Kangaroo" } - } - }; - - string metadataName = "Alpha"; - - Assert.Equal(expected: "Dingo", actual: testProperty.GetMetadataValueOrNull(metadataName)); - } - - /// - /// is abstract, so we need a concrete implementation - /// even though there is nothing interesting to implement. - /// - private class TestProperty : BaseProperty - { - } + new() { Name = "Beta", Value = "Wallaby" }, + new() { Name = "Alpha", Value = "Dingo" }, + new() { Name = "Alpha", Value = "Kangaroo" } + } + }; + + string metadataName = "Alpha"; + + Assert.Equal(expected: "Dingo", actual: testProperty.GetMetadataValueOrNull(metadataName)); + } + + /// + /// is abstract, so we need a concrete implementation + /// even though there is nothing interesting to implement. + /// + private class TestProperty : BaseProperty + { } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationManifestValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationManifestValueProviderTests.cs index 5ecff98cdf..4c0b32b252 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationManifestValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationManifestValueProviderTests.cs @@ -1,48 +1,47 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class ApplicationManifestValueProviderTests { - public class ApplicationManifestValueProviderTests + [Theory] + [InlineData(@"C:\somepath", "true", @"C:\somepath")] + [InlineData(@"Invalidpath/\/", "true", @"Invalidpath/\/")] + [InlineData("", "true", "NoManifest")] + [InlineData("", "TRue", "NoManifest")] + [InlineData("", "false", "DefaultManifest")] + [InlineData("", null, "DefaultManifest")] + public async Task GetApplicationManifest(string appManifestPropValue, string noManifestValue, string expectedValue) { - [Theory] - [InlineData(@"C:\somepath", "true", @"C:\somepath")] - [InlineData(@"Invalidpath/\/", "true", @"Invalidpath/\/")] - [InlineData("", "true", "NoManifest")] - [InlineData("", "TRue", "NoManifest")] - [InlineData("", "false", "DefaultManifest")] - [InlineData("", null, "DefaultManifest")] - public async Task GetApplicationManifest(string appManifestPropValue, string noManifestValue, string expectedValue) - { - var provider = new ApplicationManifestValueProvider(UnconfiguredProjectFactory.Create()); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("NoWin32Manifest", noManifestValue); + var provider = new ApplicationManifestValueProvider(UnconfiguredProjectFactory.Create()); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("NoWin32Manifest", noManifestValue); - var appManifestValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, appManifestPropValue, defaultProperties); - Assert.Equal(expectedValue, appManifestValue); - } + var appManifestValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, appManifestPropValue, defaultProperties); + Assert.Equal(expectedValue, appManifestValue); + } - [Theory] - [InlineData(@"inp.man", "true", @"out.man", @"out.man", "")] - [InlineData(@"C:\projectdir\foo.man", "true", @"C:\projectdir\bar.man", @"bar.man", "")] - [InlineData(@"C:\projectdir\foo.man", "true", @" a asd ", @" a asd ", "")] - [InlineData(@"C:\projectdir\foo.man", null, @"NoManifest", null, "true")] - [InlineData(@"C:\projectdir\foo.man", null, @"nomANifest", null, "true")] - [InlineData(@"C:\projectdir\foo.man", null, @"DefaultManifest", null, "")] - [InlineData(@"C:\projectdir\foo.man", null, "", null, "")] - [InlineData(@"C:\projectdir\foo.man", null, null, null, "")] - public async Task SetApplicationManifest(string appManifestPropValue, string? noManifestPropValue, string? valueToSet, string? expectedAppManifestValue, string expectedNoManifestValue) - { - var provider = new ApplicationManifestValueProvider(UnconfiguredProjectFactory.Create(fullPath: @"C:\projectdir\proj.proj")); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(new Dictionary - { - { "ApplicationManifest", appManifestPropValue }, - { "NoWin32Manifest", noManifestPropValue } - }); + [Theory] + [InlineData(@"inp.man", "true", @"out.man", @"out.man", "")] + [InlineData(@"C:\projectdir\foo.man", "true", @"C:\projectdir\bar.man", @"bar.man", "")] + [InlineData(@"C:\projectdir\foo.man", "true", @" a asd ", @" a asd ", "")] + [InlineData(@"C:\projectdir\foo.man", null, @"NoManifest", null, "true")] + [InlineData(@"C:\projectdir\foo.man", null, @"nomANifest", null, "true")] + [InlineData(@"C:\projectdir\foo.man", null, @"DefaultManifest", null, "")] + [InlineData(@"C:\projectdir\foo.man", null, "", null, "")] + [InlineData(@"C:\projectdir\foo.man", null, null, null, "")] + public async Task SetApplicationManifest(string appManifestPropValue, string? noManifestPropValue, string? valueToSet, string? expectedAppManifestValue, string expectedNoManifestValue) + { + var provider = new ApplicationManifestValueProvider(UnconfiguredProjectFactory.Create(fullPath: @"C:\projectdir\proj.proj")); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(new Dictionary + { + { "ApplicationManifest", appManifestPropValue }, + { "NoWin32Manifest", noManifestPropValue } + }); - var appManifestValue = await provider.OnSetPropertyValueAsync(string.Empty, valueToSet, defaultProperties); - var noManifestValue = await defaultProperties.GetEvaluatedPropertyValueAsync("NoWin32Manifest"); + var appManifestValue = await provider.OnSetPropertyValueAsync(string.Empty, valueToSet, defaultProperties); + var noManifestValue = await defaultProperties.GetEvaluatedPropertyValueAsync("NoWin32Manifest"); - Assert.Equal(expectedAppManifestValue, appManifestValue); - Assert.Equal(expectedNoManifestValue, noManifestValue); - } + Assert.Equal(expectedAppManifestValue, appManifestValue); + Assert.Equal(expectedNoManifestValue, noManifestValue); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProviderTests.cs index 30afa882e1..3e42f6d343 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ApplicationManifestKindValueProviderTests.cs @@ -1,72 +1,71 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class ApplicationManifestKindValueProviderTests { - public class ApplicationManifestKindValueProviderTests + [Theory] + // ApplicationManifest NoWin32Manifest Stored value Expected value + [InlineData("", "", null, "DefaultManifest")] + [InlineData("", "", "NoManifest", "NoManifest")] + [InlineData("", "", "CustomManifest", "CustomManifest")] + [InlineData("", "true", null, "NoManifest")] + [InlineData("", "false", null, "DefaultManifest")] + [InlineData("", "false", "CustomManifest", "CustomManifest")] + [InlineData(@"C:\alpha\beta\gamma.manifest", "", null, "CustomManifest")] + [InlineData(@"C:\alpha\beta\gamma.manifest", "true", null, "CustomManifest")] + [InlineData(@"C:\alpha\beta\gamma.manifest", "true", "DefaultManifest", "CustomManifest")] + public async Task GetApplicationManifestKind(string applicationManifestPropertyValue, string noManifestPropertyValue, string? storedValue, string expectedValue) { - [Theory] - // ApplicationManifest NoWin32Manifest Stored value Expected value - [InlineData("", "", null, "DefaultManifest")] - [InlineData("", "", "NoManifest", "NoManifest")] - [InlineData("", "", "CustomManifest", "CustomManifest")] - [InlineData("", "true", null, "NoManifest")] - [InlineData("", "false", null, "DefaultManifest")] - [InlineData("", "false", "CustomManifest", "CustomManifest")] - [InlineData(@"C:\alpha\beta\gamma.manifest", "", null, "CustomManifest")] - [InlineData(@"C:\alpha\beta\gamma.manifest", "true", null, "CustomManifest")] - [InlineData(@"C:\alpha\beta\gamma.manifest", "true", "DefaultManifest", "CustomManifest")] - public async Task GetApplicationManifestKind(string applicationManifestPropertyValue, string noManifestPropertyValue, string? storedValue, string expectedValue) + Dictionary? storedValues = null; + if (storedValue is not null) { - Dictionary? storedValues = null; - if (storedValue is not null) - { - storedValues = new Dictionary - { - { "ApplicationManifestKind", storedValue } - }; - } - var storage = ITemporaryPropertyStorageFactory.Create(storedValues); - var provider = new ApplicationManifestKindValueProvider(storage); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(new Dictionary + storedValues = new Dictionary { - { "ApplicationManifest", applicationManifestPropertyValue }, - { "NoWin32Manifest", noManifestPropertyValue } - }); + { "ApplicationManifestKind", storedValue } + }; + } + var storage = ITemporaryPropertyStorageFactory.Create(storedValues); + var provider = new ApplicationManifestKindValueProvider(storage); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(new Dictionary + { + { "ApplicationManifest", applicationManifestPropertyValue }, + { "NoWin32Manifest", noManifestPropertyValue } + }); - var kindValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, defaultProperties); + var kindValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, defaultProperties); - Assert.Equal(expected: expectedValue, actual: kindValue); - } + Assert.Equal(expected: expectedValue, actual: kindValue); + } - [Theory] - // New value Current Current Expected Expected Expected - // AppManifest NoWin32Manifest AppManifest NoWin32Manifest stored value - [InlineData("DefaultManifest", @"C:\alpha\beta\gamma.manifest", null, null, null, "DefaultManifest")] - [InlineData("DefaultManifest", @"C:\alpha\beta\gamma.manifest", "false", null, null, "DefaultManifest")] - [InlineData("DefaultManifest", null, "true", null, null, "DefaultManifest")] - [InlineData("CustomManifest", null, "true", null, null, "CustomManifest")] - [InlineData("NoManifest", @"C:\alpha\beta\gamma.manifest", null, null, "true", "NoManifest")] - [InlineData("NoManifest", @"C:\alpha\beta\gamma.manifest", "false", null, "true", "NoManifest")] - public async Task SetApplicationManifestKind(string newValue, string? currentApplicationManifestPropertyValue, string? currentNoManifestPropertyValue, string? expectedAppManifestPropertyValue, string? expectedNoManifestPropertyValue, string? expectedStoredValue) - { - Dictionary storageDictionary = new(); - var storage = ITemporaryPropertyStorageFactory.Create(storageDictionary); + [Theory] + // New value Current Current Expected Expected Expected + // AppManifest NoWin32Manifest AppManifest NoWin32Manifest stored value + [InlineData("DefaultManifest", @"C:\alpha\beta\gamma.manifest", null, null, null, "DefaultManifest")] + [InlineData("DefaultManifest", @"C:\alpha\beta\gamma.manifest", "false", null, null, "DefaultManifest")] + [InlineData("DefaultManifest", null, "true", null, null, "DefaultManifest")] + [InlineData("CustomManifest", null, "true", null, null, "CustomManifest")] + [InlineData("NoManifest", @"C:\alpha\beta\gamma.manifest", null, null, "true", "NoManifest")] + [InlineData("NoManifest", @"C:\alpha\beta\gamma.manifest", "false", null, "true", "NoManifest")] + public async Task SetApplicationManifestKind(string newValue, string? currentApplicationManifestPropertyValue, string? currentNoManifestPropertyValue, string? expectedAppManifestPropertyValue, string? expectedNoManifestPropertyValue, string? expectedStoredValue) + { + Dictionary storageDictionary = new(); + var storage = ITemporaryPropertyStorageFactory.Create(storageDictionary); - Dictionary defaultPropertiesDictionary = new(); - defaultPropertiesDictionary["ApplicationManifest"] = currentApplicationManifestPropertyValue; - defaultPropertiesDictionary["NoWin32Manifest"] = currentNoManifestPropertyValue; - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(defaultPropertiesDictionary); + Dictionary defaultPropertiesDictionary = new(); + defaultPropertiesDictionary["ApplicationManifest"] = currentApplicationManifestPropertyValue; + defaultPropertiesDictionary["NoWin32Manifest"] = currentNoManifestPropertyValue; + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(defaultPropertiesDictionary); - var provider = new ApplicationManifestKindValueProvider(storage); - await provider.OnSetPropertyValueAsync("", newValue, defaultProperties); + var provider = new ApplicationManifestKindValueProvider(storage); + await provider.OnSetPropertyValueAsync("", newValue, defaultProperties); - defaultPropertiesDictionary.TryGetValue("ApplicationManifest", out string? finalAppManifestPropertyValue); - defaultPropertiesDictionary.TryGetValue("NoWin32Manifest", out string? finalNoManifestPropertyValue); - storageDictionary.TryGetValue("ApplicationManifestKind", out string? finalStoredValue); + defaultPropertiesDictionary.TryGetValue("ApplicationManifest", out string? finalAppManifestPropertyValue); + defaultPropertiesDictionary.TryGetValue("NoWin32Manifest", out string? finalNoManifestPropertyValue); + storageDictionary.TryGetValue("ApplicationManifestKind", out string? finalStoredValue); - Assert.Equal(expectedAppManifestPropertyValue, finalAppManifestPropertyValue); - Assert.Equal(expectedNoManifestPropertyValue, finalNoManifestPropertyValue); - Assert.Equal(expectedStoredValue, finalStoredValue); - } + Assert.Equal(expectedAppManifestPropertyValue, finalAppManifestPropertyValue); + Assert.Equal(expectedNoManifestPropertyValue, finalNoManifestPropertyValue); + Assert.Equal(expectedStoredValue, finalStoredValue); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProviderTests.cs index 8d04fe5013..050548ab90 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/ApplicationPropertyPage/ResourceSpecificationKindValueProviderTests.cs @@ -1,92 +1,91 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class ResourceSpecificationKindValueProviderTests { - public class ResourceSpecificationKindValueProviderTests + [Fact] + public async Task WhenSettingTheValue_TheNewValueIsStoredInTemporaryStorage() + { + var storageDictionary = new Dictionary(); + var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create(storageDictionary)); + + var result = await provider.OnSetPropertyValueAsync( + ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, + ResourceSpecificationKindValueProvider.ResourceFileValue, + Mock.Of()); + + Assert.Null(result); + Assert.True(storageDictionary.TryGetValue(ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, out string savedValue)); + Assert.Equal(expected: ResourceSpecificationKindValueProvider.ResourceFileValue, actual: savedValue); + } + + [Fact] + public async Task WhenGettingTheValue_DefaultsToIconAndManifest() + { + var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); + + var result = await provider.OnGetEvaluatedPropertyValueAsync( + ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, + string.Empty, + Mock.Of()); + + Assert.Equal(expected: ResourceSpecificationKindValueProvider.IconAndManifestValue, actual: result); + } + + [Fact] + public async Task WhenGettingTheValue_TheValueIsRetrievedFromTemporaryStorageIfAvailable() + { + var storageDictionary = new Dictionary { [ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty] = ResourceSpecificationKindValueProvider.ResourceFileValue }; + var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create(storageDictionary)); + + var result = await provider.OnGetEvaluatedPropertyValueAsync( + ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, + string.Empty, + Mock.Of()); + + Assert.Equal(expected: ResourceSpecificationKindValueProvider.ResourceFileValue, actual: result); + } + + [Fact] + public async Task WhenGettingTheValue_ReturnsResourceFileIfWin32ResourceIsSet() + { + var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(ResourceSpecificationKindValueProvider.Win32ResourceMSBuildProperty, @"C:\alpha\beta\gamma.res"); + + var result = await provider.OnGetEvaluatedPropertyValueAsync( + ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, + string.Empty, + defaultProperties); + + Assert.Equal(expected: ResourceSpecificationKindValueProvider.ResourceFileValue, actual: result); + } + + [Fact] + public async Task WhenGettingTheValue_ReturnsIconAndManifestIfIconSet() { - [Fact] - public async Task WhenSettingTheValue_TheNewValueIsStoredInTemporaryStorage() - { - var storageDictionary = new Dictionary(); - var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create(storageDictionary)); - - var result = await provider.OnSetPropertyValueAsync( - ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, - ResourceSpecificationKindValueProvider.ResourceFileValue, - Mock.Of()); - - Assert.Null(result); - Assert.True(storageDictionary.TryGetValue(ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, out string savedValue)); - Assert.Equal(expected: ResourceSpecificationKindValueProvider.ResourceFileValue, actual: savedValue); - } - - [Fact] - public async Task WhenGettingTheValue_DefaultsToIconAndManifest() - { - var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); - - var result = await provider.OnGetEvaluatedPropertyValueAsync( - ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, - string.Empty, - Mock.Of()); - - Assert.Equal(expected: ResourceSpecificationKindValueProvider.IconAndManifestValue, actual: result); - } - - [Fact] - public async Task WhenGettingTheValue_TheValueIsRetrievedFromTemporaryStorageIfAvailable() - { - var storageDictionary = new Dictionary { [ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty] = ResourceSpecificationKindValueProvider.ResourceFileValue }; - var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create(storageDictionary)); - - var result = await provider.OnGetEvaluatedPropertyValueAsync( - ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, - string.Empty, - Mock.Of()); - - Assert.Equal(expected: ResourceSpecificationKindValueProvider.ResourceFileValue, actual: result); - } - - [Fact] - public async Task WhenGettingTheValue_ReturnsResourceFileIfWin32ResourceIsSet() - { - var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(ResourceSpecificationKindValueProvider.Win32ResourceMSBuildProperty, @"C:\alpha\beta\gamma.res"); - - var result = await provider.OnGetEvaluatedPropertyValueAsync( - ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, - string.Empty, - defaultProperties); - - Assert.Equal(expected: ResourceSpecificationKindValueProvider.ResourceFileValue, actual: result); - } - - [Fact] - public async Task WhenGettingTheValue_ReturnsIconAndManifestIfIconSet() - { - var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(ResourceSpecificationKindValueProvider.ApplicationIconMSBuildProperty, @"C:\alpha\beta\gamma.ico"); - - var result = await provider.OnGetEvaluatedPropertyValueAsync( - ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, - string.Empty, - defaultProperties); - - Assert.Equal(expected: ResourceSpecificationKindValueProvider.IconAndManifestValue, actual: result); - } - - [Fact] - public async Task WhenGettingTheValue_ReturnsIconAndManifestIfManifestSet() - { - var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(ResourceSpecificationKindValueProvider.ApplicationManifestMSBuildProperty, @"C:\alpha\beta\app.config"); - - var result = await provider.OnGetEvaluatedPropertyValueAsync( - ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, - string.Empty, - defaultProperties); - - Assert.Equal(expected: ResourceSpecificationKindValueProvider.IconAndManifestValue, actual: result); - } + var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(ResourceSpecificationKindValueProvider.ApplicationIconMSBuildProperty, @"C:\alpha\beta\gamma.ico"); + + var result = await provider.OnGetEvaluatedPropertyValueAsync( + ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, + string.Empty, + defaultProperties); + + Assert.Equal(expected: ResourceSpecificationKindValueProvider.IconAndManifestValue, actual: result); + } + + [Fact] + public async Task WhenGettingTheValue_ReturnsIconAndManifestIfManifestSet() + { + var provider = new ResourceSpecificationKindValueProvider(ITemporaryPropertyStorageFactory.Create()); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(ResourceSpecificationKindValueProvider.ApplicationManifestMSBuildProperty, @"C:\alpha\beta\app.config"); + + var result = await provider.OnGetEvaluatedPropertyValueAsync( + ResourceSpecificationKindValueProvider.ResourceSpecificationKindProperty, + string.Empty, + defaultProperties); + + Assert.Equal(expected: ResourceSpecificationKindValueProvider.IconAndManifestValue, actual: result); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/AssemblyOriginatorKeyFileValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/AssemblyOriginatorKeyFileValueProviderTests.cs index faff4780fc..35fb0b8492 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/AssemblyOriginatorKeyFileValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/AssemblyOriginatorKeyFileValueProviderTests.cs @@ -2,50 +2,49 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.ProjectPropertiesProviders +namespace Microsoft.VisualStudio.ProjectSystem.ProjectPropertiesProviders; + +public class AssemblyOriginatorKeyFileValueProviderTests { - public class AssemblyOriginatorKeyFileValueProviderTests - { - private const string AssemblyOriginatorKeyFilePropertyName = "AssemblyOriginatorKeyFile"; + private const string AssemblyOriginatorKeyFilePropertyName = "AssemblyOriginatorKeyFile"; - [Fact] - public async Task VerifySetKeyFilePropertyAsync() - { - string projectFolder = @"C:\project\root"; - string projectFullPath = $@"{projectFolder}\project.testproj"; - string keyFileName = "KeyFile.snk"; - string keyFileFullPath = $@"{projectFolder}\{keyFileName}"; - var instancePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( - new Dictionary - { - { AssemblyOriginatorKeyFilePropertyName, keyFileFullPath } - }); + [Fact] + public async Task VerifySetKeyFilePropertyAsync() + { + string projectFolder = @"C:\project\root"; + string projectFullPath = $@"{projectFolder}\project.testproj"; + string keyFileName = "KeyFile.snk"; + string keyFileFullPath = $@"{projectFolder}\{keyFileName}"; + var instancePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( + new Dictionary + { + { AssemblyOriginatorKeyFilePropertyName, keyFileFullPath } + }); - var instanceProperties = instancePropertiesMock.Object; - var instanceProvider = IProjectInstancePropertiesProviderFactory.ImplementsGetCommonProperties(instanceProperties); + var instanceProperties = instancePropertiesMock.Object; + var instanceProvider = IProjectInstancePropertiesProviderFactory.ImplementsGetCommonProperties(instanceProperties); - // Verify get key file value without intercepted provider. - var properties = instanceProvider.GetCommonProperties(null!); - var propertyValue = await properties.GetEvaluatedPropertyValueAsync(AssemblyOriginatorKeyFilePropertyName); - Assert.Equal(keyFileFullPath, propertyValue); + // Verify get key file value without intercepted provider. + var properties = instanceProvider.GetCommonProperties(null!); + var propertyValue = await properties.GetEvaluatedPropertyValueAsync(AssemblyOriginatorKeyFilePropertyName); + Assert.Equal(keyFileFullPath, propertyValue); - // Verify relative path key file value from intercepted key file provider. - var project = UnconfiguredProjectFactory.Create(fullPath: projectFullPath); - var delegateProvider = IProjectPropertiesProviderFactory.Create(); - var keyFileProvider = new AssemblyOriginatorKeyFileValueProvider(project); - var providerMetadata = IInterceptingPropertyValueProviderMetadataFactory.Create(AssemblyOriginatorKeyFilePropertyName); - var lazyArray = new[] { new Lazy( - () => keyFileProvider, providerMetadata) }; - var interceptedProvider = new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(delegateProvider, instanceProvider, project, lazyArray); - var propertyNames = await properties.GetPropertyNamesAsync(); - Assert.Single(propertyNames); - Assert.Equal(AssemblyOriginatorKeyFilePropertyName, propertyNames.First()); - properties = interceptedProvider.GetCommonProperties(null!); - string newKeyFileName = "KeyFile2.snk"; - string newKeyFileFullPath = $@"{projectFolder}\{newKeyFileName}"; - await properties.SetPropertyValueAsync(AssemblyOriginatorKeyFilePropertyName, newKeyFileFullPath); - propertyValue = await properties.GetEvaluatedPropertyValueAsync(AssemblyOriginatorKeyFilePropertyName); - Assert.Equal(newKeyFileName, propertyValue); - } + // Verify relative path key file value from intercepted key file provider. + var project = UnconfiguredProjectFactory.Create(fullPath: projectFullPath); + var delegateProvider = IProjectPropertiesProviderFactory.Create(); + var keyFileProvider = new AssemblyOriginatorKeyFileValueProvider(project); + var providerMetadata = IInterceptingPropertyValueProviderMetadataFactory.Create(AssemblyOriginatorKeyFilePropertyName); + var lazyArray = new[] { new Lazy( + () => keyFileProvider, providerMetadata) }; + var interceptedProvider = new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(delegateProvider, instanceProvider, project, lazyArray); + var propertyNames = await properties.GetPropertyNamesAsync(); + Assert.Single(propertyNames); + Assert.Equal(AssemblyOriginatorKeyFilePropertyName, propertyNames.First()); + properties = interceptedProvider.GetCommonProperties(null!); + string newKeyFileName = "KeyFile2.snk"; + string newKeyFileFullPath = $@"{projectFolder}\{newKeyFileName}"; + await properties.SetPropertyValueAsync(AssemblyOriginatorKeyFilePropertyName, newKeyFileFullPath); + propertyValue = await properties.GetEvaluatedPropertyValueAsync(AssemblyOriginatorKeyFilePropertyName); + Assert.Equal(newKeyFileName, propertyValue); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/InterceptedProjectPropertiesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/InterceptedProjectPropertiesProviderTests.cs index 5726aa0573..ee1c873e67 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/InterceptedProjectPropertiesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/InterceptedProjectPropertiesProviderTests.cs @@ -1,127 +1,126 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class InterceptedProjectPropertiesProviderTests { - public class InterceptedProjectPropertiesProviderTests + private const string MockPropertyName = "MockProperty"; + + [Fact] + public async Task VerifyInterceptedPropertiesProviderAsync() + { + var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( + new Dictionary + { + { MockPropertyName, "DummyValue" } + }); + + var delegateProperties = delegatePropertiesMock.Object; + var delegateProvider = IProjectPropertiesProviderFactory.Create(delegateProperties); + + bool getEvaluatedInvoked = false; + bool getUnevaluatedInvoked = false; + bool setValueInvoked = false; + + var mockPropertyProvider = IInterceptingPropertyValueProviderFactory.Create(MockPropertyName, + onGetEvaluatedPropertyValue: (v, p) => { getEvaluatedInvoked = true; return v; }, + onGetUnevaluatedPropertyValue: (v, p) => { getUnevaluatedInvoked = true; return v; }, + onSetPropertyValue: (v, p, d) => { setValueInvoked = true; return v; }); + var project = UnconfiguredProjectFactory.Create(); + var instanceProvider = IProjectInstancePropertiesProviderFactory.Create(); + + var interceptedProvider = new ProjectFileInterceptedProjectPropertiesProvider(delegateProvider, instanceProvider, project, new[] { mockPropertyProvider }); + var properties = interceptedProvider.GetProperties("path/to/project.testproj", null, null); + + // Verify interception for GetEvaluatedPropertyValueAsync. + string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(MockPropertyName); + Assert.True(getEvaluatedInvoked); + + // Verify interception for GetUnevaluatedPropertyValueAsync. + propertyValue = await properties.GetUnevaluatedPropertyValueAsync(MockPropertyName); + Assert.True(getUnevaluatedInvoked); + + // Verify interception for SetPropertyValueAsync. + await properties.SetPropertyValueAsync(MockPropertyName, "NewValue", null); + Assert.True(setValueInvoked); + } + + [Fact] + public async Task VerifyInterceptedViaSnapshotCommonPropertiesProviderAsync() + { + var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( + new Dictionary + { + { MockPropertyName, "DummyValue" } + }); + + var delegateProperties = delegatePropertiesMock.Object; + var delegateProvider = IProjectPropertiesProviderFactory.Create(commonProps: delegateProperties); + + bool getEvaluatedInvoked = false; + bool getUnevaluatedInvoked = false; + bool setValueInvoked = false; + + var mockPropertyProvider = IInterceptingPropertyValueProviderFactory.Create(MockPropertyName, + onGetEvaluatedPropertyValue: (v, p) => { getEvaluatedInvoked = true; return v; }, + onGetUnevaluatedPropertyValue: (v, p) => { getUnevaluatedInvoked = true; return v; }, + onSetPropertyValue: (v, p, d) => { setValueInvoked = true; return v; }); + var unconfiguredProject = UnconfiguredProjectFactory.Create(); + var instanceProvider = IProjectInstancePropertiesProviderFactory.Create(); + + var interceptedProvider = new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(delegateProvider, instanceProvider, unconfiguredProject, new[] { mockPropertyProvider }); + var properties = interceptedProvider.GetCommonProperties(); + + // Verify interception for GetEvaluatedPropertyValueAsync. + string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(MockPropertyName); + Assert.True(getEvaluatedInvoked); + + // Verify interception for GetUnevaluatedPropertyValueAsync. + propertyValue = await properties.GetUnevaluatedPropertyValueAsync(MockPropertyName); + Assert.True(getUnevaluatedInvoked); + + // Verify interception for SetPropertyValueAsync. + await properties.SetPropertyValueAsync(MockPropertyName, "NewValue", null); + Assert.True(setValueInvoked); + } + + [Fact] + public async Task VerifyInterceptedViaSnapshotInstanceCommonPropertiesProviderAsync() { - private const string MockPropertyName = "MockProperty"; - - [Fact] - public async Task VerifyInterceptedPropertiesProviderAsync() - { - var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( - new Dictionary - { - { MockPropertyName, "DummyValue" } - }); - - var delegateProperties = delegatePropertiesMock.Object; - var delegateProvider = IProjectPropertiesProviderFactory.Create(delegateProperties); - - bool getEvaluatedInvoked = false; - bool getUnevaluatedInvoked = false; - bool setValueInvoked = false; - - var mockPropertyProvider = IInterceptingPropertyValueProviderFactory.Create(MockPropertyName, - onGetEvaluatedPropertyValue: (v, p) => { getEvaluatedInvoked = true; return v; }, - onGetUnevaluatedPropertyValue: (v, p) => { getUnevaluatedInvoked = true; return v; }, - onSetPropertyValue: (v, p, d) => { setValueInvoked = true; return v; }); - var project = UnconfiguredProjectFactory.Create(); - var instanceProvider = IProjectInstancePropertiesProviderFactory.Create(); - - var interceptedProvider = new ProjectFileInterceptedProjectPropertiesProvider(delegateProvider, instanceProvider, project, new[] { mockPropertyProvider }); - var properties = interceptedProvider.GetProperties("path/to/project.testproj", null, null); - - // Verify interception for GetEvaluatedPropertyValueAsync. - string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(MockPropertyName); - Assert.True(getEvaluatedInvoked); - - // Verify interception for GetUnevaluatedPropertyValueAsync. - propertyValue = await properties.GetUnevaluatedPropertyValueAsync(MockPropertyName); - Assert.True(getUnevaluatedInvoked); - - // Verify interception for SetPropertyValueAsync. - await properties.SetPropertyValueAsync(MockPropertyName, "NewValue", null); - Assert.True(setValueInvoked); - } - - [Fact] - public async Task VerifyInterceptedViaSnapshotCommonPropertiesProviderAsync() - { - var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( - new Dictionary - { - { MockPropertyName, "DummyValue" } - }); - - var delegateProperties = delegatePropertiesMock.Object; - var delegateProvider = IProjectPropertiesProviderFactory.Create(commonProps: delegateProperties); - - bool getEvaluatedInvoked = false; - bool getUnevaluatedInvoked = false; - bool setValueInvoked = false; - - var mockPropertyProvider = IInterceptingPropertyValueProviderFactory.Create(MockPropertyName, - onGetEvaluatedPropertyValue: (v, p) => { getEvaluatedInvoked = true; return v; }, - onGetUnevaluatedPropertyValue: (v, p) => { getUnevaluatedInvoked = true; return v; }, - onSetPropertyValue: (v, p, d) => { setValueInvoked = true; return v; }); - var unconfiguredProject = UnconfiguredProjectFactory.Create(); - var instanceProvider = IProjectInstancePropertiesProviderFactory.Create(); - - var interceptedProvider = new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(delegateProvider, instanceProvider, unconfiguredProject, new[] { mockPropertyProvider }); - var properties = interceptedProvider.GetCommonProperties(); - - // Verify interception for GetEvaluatedPropertyValueAsync. - string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(MockPropertyName); - Assert.True(getEvaluatedInvoked); - - // Verify interception for GetUnevaluatedPropertyValueAsync. - propertyValue = await properties.GetUnevaluatedPropertyValueAsync(MockPropertyName); - Assert.True(getUnevaluatedInvoked); - - // Verify interception for SetPropertyValueAsync. - await properties.SetPropertyValueAsync(MockPropertyName, "NewValue", null); - Assert.True(setValueInvoked); - } - - [Fact] - public async Task VerifyInterceptedViaSnapshotInstanceCommonPropertiesProviderAsync() - { - var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( - new Dictionary - { - { MockPropertyName, "DummyValue" } - }); - - var delegateProperties = delegatePropertiesMock.Object; - var delegateInstanceProvider = IProjectInstancePropertiesProviderFactory.ImplementsGetCommonProperties(delegateProperties); - - bool getEvaluatedInvoked = false; - bool getUnevaluatedInvoked = false; - bool setValueInvoked = false; - - var mockPropertyProvider = IInterceptingPropertyValueProviderFactory.Create(MockPropertyName, - onGetEvaluatedPropertyValue: (v, p) => { getEvaluatedInvoked = true; return v; }, - onGetUnevaluatedPropertyValue: (v, p) => { getUnevaluatedInvoked = true; return v; }, - onSetPropertyValue: (v, p, d) => { setValueInvoked = true; return v; }); - - var unconfiguredProject = UnconfiguredProjectFactory.Create(); - var provider = IProjectPropertiesProviderFactory.Create(); - - var interceptedProvider = new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(provider, delegateInstanceProvider, unconfiguredProject, new[] { mockPropertyProvider }); - var properties = interceptedProvider.GetCommonProperties(projectInstance: null!); - - // Verify interception for GetEvaluatedPropertyValueAsync. - string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(MockPropertyName); - Assert.True(getEvaluatedInvoked); - - // Verify interception for GetUnevaluatedPropertyValueAsync. - propertyValue = await properties.GetUnevaluatedPropertyValueAsync(MockPropertyName); - Assert.True(getUnevaluatedInvoked); - - // Verify interception for SetPropertyValueAsync. - await properties.SetPropertyValueAsync(MockPropertyName, "NewValue", null); - Assert.True(setValueInvoked); - } + var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues( + new Dictionary + { + { MockPropertyName, "DummyValue" } + }); + + var delegateProperties = delegatePropertiesMock.Object; + var delegateInstanceProvider = IProjectInstancePropertiesProviderFactory.ImplementsGetCommonProperties(delegateProperties); + + bool getEvaluatedInvoked = false; + bool getUnevaluatedInvoked = false; + bool setValueInvoked = false; + + var mockPropertyProvider = IInterceptingPropertyValueProviderFactory.Create(MockPropertyName, + onGetEvaluatedPropertyValue: (v, p) => { getEvaluatedInvoked = true; return v; }, + onGetUnevaluatedPropertyValue: (v, p) => { getUnevaluatedInvoked = true; return v; }, + onSetPropertyValue: (v, p, d) => { setValueInvoked = true; return v; }); + + var unconfiguredProject = UnconfiguredProjectFactory.Create(); + var provider = IProjectPropertiesProviderFactory.Create(); + + var interceptedProvider = new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(provider, delegateInstanceProvider, unconfiguredProject, new[] { mockPropertyProvider }); + var properties = interceptedProvider.GetCommonProperties(projectInstance: null!); + + // Verify interception for GetEvaluatedPropertyValueAsync. + string? propertyValue = await properties.GetEvaluatedPropertyValueAsync(MockPropertyName); + Assert.True(getEvaluatedInvoked); + + // Verify interception for GetUnevaluatedPropertyValueAsync. + propertyValue = await properties.GetUnevaluatedPropertyValueAsync(MockPropertyName); + Assert.True(getUnevaluatedInvoked); + + // Verify interception for SetPropertyValueAsync. + await properties.SetPropertyValueAsync(MockPropertyName, "NewValue", null); + Assert.True(setValueInvoked); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/NeutralLanguageValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/NeutralLanguageValueProviderTests.cs index 196c469aab..7eb795900b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/NeutralLanguageValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/NeutralLanguageValueProviderTests.cs @@ -2,56 +2,55 @@ using Microsoft.VisualStudio.ProjectSystem.Properties.Package; -namespace Microsoft.VisualStudio.ProjectSystem.Properties.InterceptingProjectProperties +namespace Microsoft.VisualStudio.ProjectSystem.Properties.InterceptingProjectProperties; + +public class NeutralLanguageValueProviderTests { - public class NeutralLanguageValueProviderTests + [Fact] + public async Task OnSetPropertyValueAsync_ToNoneValue_DeletesTheProperty() { - [Fact] - public async Task OnSetPropertyValueAsync_ToNoneValue_DeletesTheProperty() - { - var provider = new NeutralLanguageValueProvider(); + var provider = new NeutralLanguageValueProvider(); - var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(NeutralLanguageValueProvider.NeutralLanguagePropertyName, "en-GB"); - var updatedValue = await provider.OnSetPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, NeutralLanguageValueProvider.NoneValue, projectProperties); - var valueInProjectProperties = await projectProperties.GetUnevaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName); + var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(NeutralLanguageValueProvider.NeutralLanguagePropertyName, "en-GB"); + var updatedValue = await provider.OnSetPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, NeutralLanguageValueProvider.NoneValue, projectProperties); + var valueInProjectProperties = await projectProperties.GetUnevaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName); - Assert.Null(updatedValue); - Assert.Null(valueInProjectProperties); - } + Assert.Null(updatedValue); + Assert.Null(valueInProjectProperties); + } - [Fact] - public async Task OnSetPropertyValueAsync_ToAnythingOtherThanNone_ReturnsSameValue() - { - var provider = new NeutralLanguageValueProvider(); + [Fact] + public async Task OnSetPropertyValueAsync_ToAnythingOtherThanNone_ReturnsSameValue() + { + var provider = new NeutralLanguageValueProvider(); - var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(NeutralLanguageValueProvider.NeutralLanguagePropertyName, "en-GB"); - var updatedValue = await provider.OnSetPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, "pt-BR", projectProperties); - var valueInProjectProperties = await projectProperties.GetUnevaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName); + var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue(NeutralLanguageValueProvider.NeutralLanguagePropertyName, "en-GB"); + var updatedValue = await provider.OnSetPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, "pt-BR", projectProperties); + var valueInProjectProperties = await projectProperties.GetUnevaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName); - Assert.Equal(expected: "pt-BR", actual: updatedValue); - Assert.Equal(expected: "en-GB", actual: valueInProjectProperties); - } + Assert.Equal(expected: "pt-BR", actual: updatedValue); + Assert.Equal(expected: "en-GB", actual: valueInProjectProperties); + } - [Fact] - public async Task OnGetEvaluatedPropertyValueAsync_WhenPropertyValueIsEmpty_ReturnsNoneValue() - { - var provider = new NeutralLanguageValueProvider(); + [Fact] + public async Task OnGetEvaluatedPropertyValueAsync_WhenPropertyValueIsEmpty_ReturnsNoneValue() + { + var provider = new NeutralLanguageValueProvider(); - var projectProperties = Mock.Of(); - var value = await provider.OnGetEvaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, evaluatedPropertyValue: string.Empty, projectProperties); + var projectProperties = Mock.Of(); + var value = await provider.OnGetEvaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, evaluatedPropertyValue: string.Empty, projectProperties); - Assert.Equal(expected: NeutralLanguageValueProvider.NoneValue, actual: value); - } + Assert.Equal(expected: NeutralLanguageValueProvider.NoneValue, actual: value); + } - [Fact] - public async Task OnGetEvaluatedPropertyValueAsync_WhenPropertyValueIsNotEmpty_ReturnsSameValue() - { - var provider = new NeutralLanguageValueProvider(); + [Fact] + public async Task OnGetEvaluatedPropertyValueAsync_WhenPropertyValueIsNotEmpty_ReturnsSameValue() + { + var provider = new NeutralLanguageValueProvider(); - var projectProperties = Mock.Of(); - var value = await provider.OnGetEvaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, evaluatedPropertyValue: "en-GB", projectProperties); + var projectProperties = Mock.Of(); + var value = await provider.OnGetEvaluatedPropertyValueAsync(NeutralLanguageValueProvider.NeutralLanguagePropertyName, evaluatedPropertyValue: "en-GB", projectProperties); - Assert.Equal(expected: "en-GB", actual: value); - } + Assert.Equal(expected: "en-GB", actual: value); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBaseTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBaseTests.cs index 6ac3e2e550..979d4856cf 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBaseTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageFilePropertyValueProviderBaseTests.cs @@ -2,94 +2,93 @@ using Microsoft.VisualStudio.ProjectSystem.Properties.Package; -namespace Microsoft.VisualStudio.ProjectSystem.Properties.InterceptingProjectProperties.PackagePropertyPage +namespace Microsoft.VisualStudio.ProjectSystem.Properties.InterceptingProjectProperties.PackagePropertyPage; + +public class PackageFilePropertyValueProviderBaseTests { - public class PackageFilePropertyValueProviderBaseTests - { - private const string TestPropertyName = "Example"; - private const string ProjectPath = @"C:\Test\Path\Here"; - private static readonly IProjectProperties EmptyProjectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(ImmutableDictionary.Empty); + private const string TestPropertyName = "Example"; + private const string ProjectPath = @"C:\Test\Path\Here"; + private static readonly IProjectProperties EmptyProjectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(ImmutableDictionary.Empty); - [Theory] - [InlineData(null, null, null, null)] - [InlineData(null, null, @"Test.txt", @"Test.txt")] - [InlineData(@"..\..\..\Test.txt", "True", @"Test.txt", @"..\..\..\Test.txt")] - [InlineData(@"..\..\..\Test.txt", "False", @"docs\Test.txt", @"docs\Test.txt")] - [InlineData(@"..\..\..\Test.txt", null, @"docs\Test.txt", @"docs\Test.txt")] - [InlineData(@"TotallyDifferentFile.txt", "True", @"docs\Test.txt", @"docs\Test.txt")] - [InlineData(@"..\..\..\Test.txt", "True", @"docs\Test.txt", @"..\..\..\Test.txt")] - [InlineData(@"Documents\Test.txt", "True", @"docs\Test.txt", @"Documents\Test.txt")] - [InlineData(@"Test.txt", "True", @"Test.txt", @"Test.txt")] - [InlineData(ProjectPath + @"\Test.txt", "True", @"Test.txt", ProjectPath + @"\Test.txt")] - [InlineData(@"C:\Test.txt", "True", @"Test.txt", @"C:\Test.txt")] - public async Task GetPackageFilePropertyValue(string? existingInclude, string? pack, string? propertyValue, string? expectedValue) + [Theory] + [InlineData(null, null, null, null)] + [InlineData(null, null, @"Test.txt", @"Test.txt")] + [InlineData(@"..\..\..\Test.txt", "True", @"Test.txt", @"..\..\..\Test.txt")] + [InlineData(@"..\..\..\Test.txt", "False", @"docs\Test.txt", @"docs\Test.txt")] + [InlineData(@"..\..\..\Test.txt", null, @"docs\Test.txt", @"docs\Test.txt")] + [InlineData(@"TotallyDifferentFile.txt", "True", @"docs\Test.txt", @"docs\Test.txt")] + [InlineData(@"..\..\..\Test.txt", "True", @"docs\Test.txt", @"..\..\..\Test.txt")] + [InlineData(@"Documents\Test.txt", "True", @"docs\Test.txt", @"Documents\Test.txt")] + [InlineData(@"Test.txt", "True", @"Test.txt", @"Test.txt")] + [InlineData(ProjectPath + @"\Test.txt", "True", @"Test.txt", ProjectPath + @"\Test.txt")] + [InlineData(@"C:\Test.txt", "True", @"Test.txt", @"C:\Test.txt")] + public async Task GetPackageFilePropertyValue(string? existingInclude, string? pack, string? propertyValue, string? expectedValue) + { + var projectItemProvider = IProjectItemProviderFactory.GetItemsAsync(() => existingInclude is not null ? new[] { - var projectItemProvider = IProjectItemProviderFactory.GetItemsAsync(() => existingInclude is not null ? new[] - { - IProjectItemFactory.Create(existingInclude, pack is not null - ? IProjectPropertiesFactory.CreateWithPropertyAndValue("Pack", pack) - : EmptyProjectProperties) - } : Enumerable.Empty()); - var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: ProjectPath); - var provider = new TestValueProvider(TestPropertyName, projectItemProvider, unconfiguredProject); + IProjectItemFactory.Create(existingInclude, pack is not null + ? IProjectPropertiesFactory.CreateWithPropertyAndValue("Pack", pack) + : EmptyProjectProperties) + } : Enumerable.Empty()); + var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: ProjectPath); + var provider = new TestValueProvider(TestPropertyName, projectItemProvider, unconfiguredProject); - var unevaluatedActualValue = await provider.OnGetUnevaluatedPropertyValueAsync(string.Empty, propertyValue!, null!); - Assert.Equal(expected: expectedValue, actual: unevaluatedActualValue); + var unevaluatedActualValue = await provider.OnGetUnevaluatedPropertyValueAsync(string.Empty, propertyValue!, null!); + Assert.Equal(expected: expectedValue, actual: unevaluatedActualValue); - var evaluatedActualValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, propertyValue!, null!); - Assert.Equal(expected: expectedValue, actual: evaluatedActualValue); - } + var evaluatedActualValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, propertyValue!, null!); + Assert.Equal(expected: expectedValue, actual: evaluatedActualValue); + } - [Theory] - [InlineData(null, null, null, null, "", "")] - [InlineData(null, null, null, null, @"Test.txt", @"Test.txt")] - [InlineData(null, null, "True", "", @"Test.txt", @"Test.txt")] - [InlineData(null, null, "True", "", "", "")] - [InlineData(null, null, "True", "docs", "", "")] - [InlineData(@"..\..\..\Test.txt", null, "True", null, @"Test.txt", @"Test.txt")] - [InlineData(@"..\..\..\TotallyDifferentFile.txt", null, "True", null, @"Test.txt", @"Test.txt")] - [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", "False", "docs", @"InAFolder\Test.txt", @"Test.txt")] - [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", "True", "docs", @"InAFolder\Test.txt", @"docs\Test.txt")] - [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", null, null, "", "")] - [InlineData(@"TotallyDifferentFile.txt", @"docs\TotallyDifferentFile.txt", "True", "docs", @"C:\Test.txt", @"docs\Test.txt")] - [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", "True", "docs", ProjectPath + @"\NewOne.txt", @"docs\NewOne.txt")] - [InlineData(@"Documents\Test.txt", @"docs\Test.txt", "True", "docs", ProjectPath + @"\TotallyDifferentFile.txt", @"docs\TotallyDifferentFile.txt")] - [InlineData(@"Test.txt", @"Test.txt", "True", "", @"Test.txt", @"Test.txt")] - [InlineData(ProjectPath + @"\Test.txt", @"docs\Test.txt", "True", "", "NewOne.txt", "NewOne.txt")] - [InlineData(ProjectPath + @"\Test.txt", @"docs\Test.txt", "True", @"in\this\folder", "NewOne.txt", @"in\this\folder\NewOne.txt")] - [InlineData("", "", "True", "", @"Test.txt", "Test.txt")] - [InlineData("", "", "True", "docs", @"Test.txt", @"docs\Test.txt")] - public async Task SetPackageFilePropertyValue(string? existingInclude, string? existingPropertyValue, string? pack, string? packagePath, string newPropertyValue, string expectedValue) + [Theory] + [InlineData(null, null, null, null, "", "")] + [InlineData(null, null, null, null, @"Test.txt", @"Test.txt")] + [InlineData(null, null, "True", "", @"Test.txt", @"Test.txt")] + [InlineData(null, null, "True", "", "", "")] + [InlineData(null, null, "True", "docs", "", "")] + [InlineData(@"..\..\..\Test.txt", null, "True", null, @"Test.txt", @"Test.txt")] + [InlineData(@"..\..\..\TotallyDifferentFile.txt", null, "True", null, @"Test.txt", @"Test.txt")] + [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", "False", "docs", @"InAFolder\Test.txt", @"Test.txt")] + [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", "True", "docs", @"InAFolder\Test.txt", @"docs\Test.txt")] + [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", null, null, "", "")] + [InlineData(@"TotallyDifferentFile.txt", @"docs\TotallyDifferentFile.txt", "True", "docs", @"C:\Test.txt", @"docs\Test.txt")] + [InlineData(@"..\..\..\Test.txt", @"docs\Test.txt", "True", "docs", ProjectPath + @"\NewOne.txt", @"docs\NewOne.txt")] + [InlineData(@"Documents\Test.txt", @"docs\Test.txt", "True", "docs", ProjectPath + @"\TotallyDifferentFile.txt", @"docs\TotallyDifferentFile.txt")] + [InlineData(@"Test.txt", @"Test.txt", "True", "", @"Test.txt", @"Test.txt")] + [InlineData(ProjectPath + @"\Test.txt", @"docs\Test.txt", "True", "", "NewOne.txt", "NewOne.txt")] + [InlineData(ProjectPath + @"\Test.txt", @"docs\Test.txt", "True", @"in\this\folder", "NewOne.txt", @"in\this\folder\NewOne.txt")] + [InlineData("", "", "True", "", @"Test.txt", "Test.txt")] + [InlineData("", "", "True", "docs", @"Test.txt", @"docs\Test.txt")] + public async Task SetPackageFilePropertyValue(string? existingInclude, string? existingPropertyValue, string? pack, string? packagePath, string newPropertyValue, string expectedValue) + { + var existingMetadata = new Dictionary(); + if(pack is not null) { - var existingMetadata = new Dictionary(); - if(pack is not null) - { - existingMetadata.Add("Pack", pack); - } - if (packagePath is not null) - { - existingMetadata.Add("PackagePath", packagePath); - } - var projectItemProvider = IProjectItemProviderFactory.GetItemsAsync(() => existingInclude is not null ? new[] - { - IProjectItemFactory.Create(existingInclude, IProjectPropertiesFactory.CreateWithPropertiesAndValues(existingMetadata)) - } : Enumerable.Empty()); - var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: ProjectPath); - var provider = new TestValueProvider(TestPropertyName, projectItemProvider, unconfiguredProject); - - var existingProperties = existingPropertyValue is not null - ? IProjectPropertiesFactory.CreateWithPropertyAndValue(TestPropertyName, existingPropertyValue) - : EmptyProjectProperties; - var actualValue = await provider.OnSetPropertyValueAsync(string.Empty, newPropertyValue, existingProperties); - Assert.Equal(expected: expectedValue, actual: actualValue); + existingMetadata.Add("Pack", pack); } + if (packagePath is not null) + { + existingMetadata.Add("PackagePath", packagePath); + } + var projectItemProvider = IProjectItemProviderFactory.GetItemsAsync(() => existingInclude is not null ? new[] + { + IProjectItemFactory.Create(existingInclude, IProjectPropertiesFactory.CreateWithPropertiesAndValues(existingMetadata)) + } : Enumerable.Empty()); + var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: ProjectPath); + var provider = new TestValueProvider(TestPropertyName, projectItemProvider, unconfiguredProject); - private class TestValueProvider : PackageFilePropertyValueProviderBase + var existingProperties = existingPropertyValue is not null + ? IProjectPropertiesFactory.CreateWithPropertyAndValue(TestPropertyName, existingPropertyValue) + : EmptyProjectProperties; + var actualValue = await provider.OnSetPropertyValueAsync(string.Empty, newPropertyValue, existingProperties); + Assert.Equal(expected: expectedValue, actual: actualValue); + } + + private class TestValueProvider : PackageFilePropertyValueProviderBase + { + public TestValueProvider(string propertyName, IProjectItemProvider sourceItemsProvider, UnconfiguredProject unconfiguredProject) + : base(propertyName, sourceItemsProvider, unconfiguredProject) { - public TestValueProvider(string propertyName, IProjectItemProvider sourceItemsProvider, UnconfiguredProject unconfiguredProject) - : base(propertyName, sourceItemsProvider, unconfiguredProject) - { - } } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageLicenseKindValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageLicenseKindValueProviderTests.cs index aa791564a5..357971575d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageLicenseKindValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/PackagePropertyPage/PackageLicenseKindValueProviderTests.cs @@ -1,73 +1,72 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class PackageLicenseKindValueProviderTests { - public class PackageLicenseKindValueProviderTests + [Theory] + // Expression File Stored value Expected value + [InlineData(null, null, null, "None")] + [InlineData(null, null, "File", "File")] + [InlineData(null, @"C:\license.txt", null, "File")] + [InlineData(null, @"C:\license.txt", "Expression", "File")] + [InlineData("alpha", null, null, "Expression")] + [InlineData("alpha", @"C:\license.txt", null, "Expression")] + [InlineData("alpha", @"C:\license.txt", "None", "Expression")] + public async Task GetPackageLicenseKind(string? expressionPropertyValue, string? filePropertyValue, string? storedValue, string expectedValue) { - [Theory] - // Expression File Stored value Expected value - [InlineData(null, null, null, "None")] - [InlineData(null, null, "File", "File")] - [InlineData(null, @"C:\license.txt", null, "File")] - [InlineData(null, @"C:\license.txt", "Expression", "File")] - [InlineData("alpha", null, null, "Expression")] - [InlineData("alpha", @"C:\license.txt", null, "Expression")] - [InlineData("alpha", @"C:\license.txt", "None", "Expression")] - public async Task GetPackageLicenseKind(string? expressionPropertyValue, string? filePropertyValue, string? storedValue, string expectedValue) + Dictionary? storedValues = null; + if (storedValue is not null) { - Dictionary? storedValues = null; - if (storedValue is not null) - { - storedValues = new Dictionary - { - { "PackageLicenseKind", storedValue } - }; - } - var storage = ITemporaryPropertyStorageFactory.Create(storedValues); - var provider = new PackageLicenseKindValueProvider(storage); - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(new Dictionary + storedValues = new Dictionary { - { "PackageLicenseExpression", expressionPropertyValue }, - { "PackageLicenseFile", filePropertyValue } - }); + { "PackageLicenseKind", storedValue } + }; + } + var storage = ITemporaryPropertyStorageFactory.Create(storedValues); + var provider = new PackageLicenseKindValueProvider(storage); + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(new Dictionary + { + { "PackageLicenseExpression", expressionPropertyValue }, + { "PackageLicenseFile", filePropertyValue } + }); - var kindValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, defaultProperties); + var kindValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, defaultProperties); - Assert.Equal(expected: expectedValue, actual: kindValue); - } + Assert.Equal(expected: expectedValue, actual: kindValue); + } - [Theory] + [Theory] - // New value Current Current Expected Expected Expected - // Expression File Expression File stored value - [InlineData("Expression", null, null, null, null, "Expression")] - [InlineData("Expression", "alpha", null, "alpha", null, "Expression")] - [InlineData("Expression", null, @"C:\license.txt", null, null, "Expression")] - [InlineData("File", null, null, null, null, "File")] - [InlineData("File", "alpha", @"C:\license.txt", null, @"C:\license.txt", "File")] - [InlineData("File", "alpha", null, null, null, "File")] - [InlineData("None", "alpha", null, null, null, "None")] - [InlineData("None", null, @"C:\license.txt", null, null, "None")] - public async Task SetPackageLicenseKind(string newValue, string? currentExpressionPropertyValue, string? currentFilePropertyValue, string? expectedExpressionPropertyValue, string? expectedFilePropertyValue, string? expectedStoredValue) - { - Dictionary storageDictionary = new(); - var storage = ITemporaryPropertyStorageFactory.Create(storageDictionary); + // New value Current Current Expected Expected Expected + // Expression File Expression File stored value + [InlineData("Expression", null, null, null, null, "Expression")] + [InlineData("Expression", "alpha", null, "alpha", null, "Expression")] + [InlineData("Expression", null, @"C:\license.txt", null, null, "Expression")] + [InlineData("File", null, null, null, null, "File")] + [InlineData("File", "alpha", @"C:\license.txt", null, @"C:\license.txt", "File")] + [InlineData("File", "alpha", null, null, null, "File")] + [InlineData("None", "alpha", null, null, null, "None")] + [InlineData("None", null, @"C:\license.txt", null, null, "None")] + public async Task SetPackageLicenseKind(string newValue, string? currentExpressionPropertyValue, string? currentFilePropertyValue, string? expectedExpressionPropertyValue, string? expectedFilePropertyValue, string? expectedStoredValue) + { + Dictionary storageDictionary = new(); + var storage = ITemporaryPropertyStorageFactory.Create(storageDictionary); - Dictionary defaultPropertiesDictionary = new(); - defaultPropertiesDictionary["PackageLicenseExpression"] = currentExpressionPropertyValue; - defaultPropertiesDictionary["PackageLicenseFile"] = currentFilePropertyValue; - var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(defaultPropertiesDictionary); + Dictionary defaultPropertiesDictionary = new(); + defaultPropertiesDictionary["PackageLicenseExpression"] = currentExpressionPropertyValue; + defaultPropertiesDictionary["PackageLicenseFile"] = currentFilePropertyValue; + var defaultProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues(defaultPropertiesDictionary); - var provider = new PackageLicenseKindValueProvider(storage); - await provider.OnSetPropertyValueAsync("", newValue, defaultProperties); + var provider = new PackageLicenseKindValueProvider(storage); + await provider.OnSetPropertyValueAsync("", newValue, defaultProperties); - defaultPropertiesDictionary.TryGetValue("PackageLicenseExpression", out string? finalExpressionPropertyValue); - defaultPropertiesDictionary.TryGetValue("PackageLicenseFile", out string? finalFilePropertyValue); - storageDictionary.TryGetValue("PackageLicenseKind", out string? finalStoredValue); + defaultPropertiesDictionary.TryGetValue("PackageLicenseExpression", out string? finalExpressionPropertyValue); + defaultPropertiesDictionary.TryGetValue("PackageLicenseFile", out string? finalFilePropertyValue); + storageDictionary.TryGetValue("PackageLicenseKind", out string? finalStoredValue); - Assert.Equal(expectedExpressionPropertyValue, finalExpressionPropertyValue); - Assert.Equal(expectedFilePropertyValue, finalFilePropertyValue); - Assert.Equal(expectedStoredValue, finalStoredValue); - } + Assert.Equal(expectedExpressionPropertyValue, finalExpressionPropertyValue); + Assert.Equal(expectedFilePropertyValue, finalFilePropertyValue); + Assert.Equal(expectedStoredValue, finalStoredValue); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/TargetFrameworkValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/TargetFrameworkValueProviderTests.cs index 8eb3735522..46a12ca3cc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/TargetFrameworkValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/InterceptingProjectProperties/TargetFrameworkValueProviderTests.cs @@ -3,43 +3,42 @@ using System.Runtime.Versioning; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.ProjectPropertiesProviders +namespace Microsoft.VisualStudio.ProjectSystem.ProjectPropertiesProviders; + +public class TargetFrameworkValueProviderTests { - public class TargetFrameworkValueProviderTests + private const string TargetFrameworkPropertyName = "TargetFramework"; + + private static InterceptedProjectPropertiesProviderBase CreateInstance(FrameworkName configuredTargetFramework) + { + var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworkMonikerProperty, configuredTargetFramework.FullName); + + var project = UnconfiguredProjectFactory.Create(); + var properties = ProjectPropertiesFactory.Create(project, data); + var delegateProvider = IProjectPropertiesProviderFactory.Create(); + + var instancePropertiesMock = IProjectPropertiesFactory + .MockWithProperty(TargetFrameworkPropertyName); + + var instanceProperties = instancePropertiesMock.Object; + var instanceProvider = IProjectInstancePropertiesProviderFactory.ImplementsGetCommonProperties(instanceProperties); + + var targetFrameworkProvider = new TargetFrameworkValueProvider(properties); + var providerMetadata = IInterceptingPropertyValueProviderMetadataFactory.Create(TargetFrameworkPropertyName); + var lazyArray = new[] { new Lazy( + () => targetFrameworkProvider, providerMetadata) }; + return new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(delegateProvider, instanceProvider, project, lazyArray); + } + + [Fact] + public async Task VerifyGetTargetFrameworkPropertyAsync() { - private const string TargetFrameworkPropertyName = "TargetFramework"; - - private static InterceptedProjectPropertiesProviderBase CreateInstance(FrameworkName configuredTargetFramework) - { - var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TargetFrameworkMonikerProperty, configuredTargetFramework.FullName); - - var project = UnconfiguredProjectFactory.Create(); - var properties = ProjectPropertiesFactory.Create(project, data); - var delegateProvider = IProjectPropertiesProviderFactory.Create(); - - var instancePropertiesMock = IProjectPropertiesFactory - .MockWithProperty(TargetFrameworkPropertyName); - - var instanceProperties = instancePropertiesMock.Object; - var instanceProvider = IProjectInstancePropertiesProviderFactory.ImplementsGetCommonProperties(instanceProperties); - - var targetFrameworkProvider = new TargetFrameworkValueProvider(properties); - var providerMetadata = IInterceptingPropertyValueProviderMetadataFactory.Create(TargetFrameworkPropertyName); - var lazyArray = new[] { new Lazy( - () => targetFrameworkProvider, providerMetadata) }; - return new ProjectFileInterceptedViaSnapshotProjectPropertiesProvider(delegateProvider, instanceProvider, project, lazyArray); - } - - [Fact] - public async Task VerifyGetTargetFrameworkPropertyAsync() - { - var configuredTargetFramework = new FrameworkName(".NETFramework", new Version(4, 5)); - var expectedTargetFrameworkPropertyValue = (uint)0x40005; - var provider = CreateInstance(configuredTargetFramework); - var properties = provider.GetCommonProperties(null!); - var propertyValueStr = await properties.GetEvaluatedPropertyValueAsync(TargetFrameworkPropertyName); - Assert.True(uint.TryParse(propertyValueStr, out uint propertyValue)); - Assert.Equal(expectedTargetFrameworkPropertyValue, propertyValue); - } + var configuredTargetFramework = new FrameworkName(".NETFramework", new Version(4, 5)); + var expectedTargetFrameworkPropertyValue = (uint)0x40005; + var provider = CreateInstance(configuredTargetFramework); + var properties = provider.GetCommonProperties(null!); + var propertyValueStr = await properties.GetEvaluatedPropertyValueAsync(TargetFrameworkPropertyName); + Assert.True(uint.TryParse(propertyValueStr, out uint propertyValue)); + Assert.Equal(expectedTargetFrameworkPropertyValue, propertyValue); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectItemProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectItemProviderTests.cs index 049864acb2..8c3936611d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectItemProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectItemProviderTests.cs @@ -2,618 +2,617 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class LaunchProfilesProjectItemProviderTests { - public class LaunchProfilesProjectItemProviderTests + [Fact] + public async Task GetItemTypesAsync_ReturnsLaunchProfile() { - [Fact] - public async Task GetItemTypesAsync_ReturnsLaunchProfile() - { - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - ILaunchSettingsProviderFactory.Create()); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + ILaunchSettingsProviderFactory.Create()); - var itemTypes = await itemProvider.GetItemTypesAsync(); + var itemTypes = await itemProvider.GetItemTypesAsync(); - Assert.Single(itemTypes, LaunchProfileProjectItemProvider.ItemType); - } + Assert.Single(itemTypes, LaunchProfileProjectItemProvider.ItemType); + } - [Fact] - public async Task WhenThereAreNoLaunchProfiles_GetExistingItemTypesAsyncReturnsAnEmptySet() - { - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new LaunchProfile[0]); + [Fact] + public async Task WhenThereAreNoLaunchProfiles_GetExistingItemTypesAsyncReturnsAnEmptySet() + { + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new LaunchProfile[0]); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var existingItemTypes = await itemProvider.GetExistingItemTypesAsync(); + var existingItemTypes = await itemProvider.GetExistingItemTypesAsync(); - Assert.Empty(existingItemTypes); - } + Assert.Empty(existingItemTypes); + } - [Fact] - public async Task WhenThereAreLaunchProfiles_GetExistingItemTypesAsyncReturnsASingleItem() - { - var profile = new WritableLaunchProfile { Name = "Test" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile.ToLaunchProfile() }); + [Fact] + public async Task WhenThereAreLaunchProfiles_GetExistingItemTypesAsyncReturnsASingleItem() + { + var profile = new WritableLaunchProfile { Name = "Test" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var existingItemTypes = await itemProvider.GetExistingItemTypesAsync(); + var existingItemTypes = await itemProvider.GetExistingItemTypesAsync(); - Assert.Single(existingItemTypes, LaunchProfileProjectItemProvider.ItemType); - } + Assert.Single(existingItemTypes, LaunchProfileProjectItemProvider.ItemType); + } - [Fact] - public async Task WhenThereAreNoLaunchProfiles_GetItemsAsyncReturnsAnEmptyEnumerable() - { - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new LaunchProfile[0]); + [Fact] + public async Task WhenThereAreNoLaunchProfiles_GetItemsAsyncReturnsAnEmptyEnumerable() + { + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new LaunchProfile[0]); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var items = await itemProvider.GetItemsAsync(); + var items = await itemProvider.GetItemsAsync(); - Assert.Empty(items); - } + Assert.Empty(items); + } - [Fact] - public async Task WhenThereAreLaunchProfiles_GetItemsAsyncReturnsAnItemForEachProfile() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + [Fact] + public async Task WhenThereAreLaunchProfiles_GetItemsAsyncReturnsAnItemForEachProfile() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var items = await itemProvider.GetItemsAsync(); + var items = await itemProvider.GetItemsAsync(); - Assert.Collection(items, - item => Assert.Equal("Profile1", item.EvaluatedInclude), - item => Assert.Equal("Profile2", item.EvaluatedInclude)); - } + Assert.Collection(items, + item => Assert.Equal("Profile1", item.EvaluatedInclude), + item => Assert.Equal("Profile2", item.EvaluatedInclude)); + } - [Fact] - public async Task WhenAskedForLaunchProfileItemTypes_GetItemsAsyncReturnsAnItemForEachProfile() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + [Fact] + public async Task WhenAskedForLaunchProfileItemTypes_GetItemsAsyncReturnsAnItemForEachProfile() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var items = await itemProvider.GetItemsAsync(LaunchProfileProjectItemProvider.ItemType); + var items = await itemProvider.GetItemsAsync(LaunchProfileProjectItemProvider.ItemType); - Assert.Collection(items, - item => Assert.Equal("Profile1", item.EvaluatedInclude), - item => Assert.Equal("Profile2", item.EvaluatedInclude)); + Assert.Collection(items, + item => Assert.Equal("Profile1", item.EvaluatedInclude), + item => Assert.Equal("Profile2", item.EvaluatedInclude)); - items = await itemProvider.GetItemsAsync(LaunchProfileProjectItemProvider.ItemType, "Profile2"); + items = await itemProvider.GetItemsAsync(LaunchProfileProjectItemProvider.ItemType, "Profile2"); - Assert.Collection(items, - item => Assert.Equal("Profile2", item.EvaluatedInclude)); - } + Assert.Collection(items, + item => Assert.Equal("Profile2", item.EvaluatedInclude)); + } - [Fact] - public async Task WhenAskedForOtherItemTypes_GetItemsAsyncReturnsAnEmptyEnumerable() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + [Fact] + public async Task WhenAskedForOtherItemTypes_GetItemsAsyncReturnsAnEmptyEnumerable() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var items = await itemProvider.GetItemsAsync("RandomItemType"); + var items = await itemProvider.GetItemsAsync("RandomItemType"); - Assert.Empty(items); + Assert.Empty(items); - items = await itemProvider.GetItemsAsync("RandomItemType", "Profile2"); + items = await itemProvider.GetItemsAsync("RandomItemType", "Profile2"); - Assert.Empty(items); - } + Assert.Empty(items); + } - [Fact] - public async Task WhenProjectPropertyContextHasNoItemType_GetItemsAsyncReturnsAnItemWithAMatchingName() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + [Fact] + public async Task WhenProjectPropertyContextHasNoItemType_GetItemsAsyncReturnsAnItemWithAMatchingName() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var context = new TestProjectPropertiesContext( - isProjectFile: true, - file: "Foo", - itemType: null, - itemName: "Profile2"); + var context = new TestProjectPropertiesContext( + isProjectFile: true, + file: "Foo", + itemType: null, + itemName: "Profile2"); - var item = await itemProvider.GetItemAsync(context); + var item = await itemProvider.GetItemAsync(context); - Assert.NotNull(item); - Assert.Equal("Profile2", item.EvaluatedInclude); - } + Assert.NotNull(item); + Assert.Equal("Profile2", item.EvaluatedInclude); + } - [Fact] - public async Task WhenProjectPropertyContextHasTheWrongItemType_GetItemsAsyncReturnsNull() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + [Fact] + public async Task WhenProjectPropertyContextHasTheWrongItemType_GetItemsAsyncReturnsNull() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var context = new TestProjectPropertiesContext( - isProjectFile: true, - file: "Foo", - itemType: "RandomItemType", - itemName: "Profile2"); + var context = new TestProjectPropertiesContext( + isProjectFile: true, + file: "Foo", + itemType: "RandomItemType", + itemName: "Profile2"); - var item = await itemProvider.GetItemAsync(context); + var item = await itemProvider.GetItemAsync(context); - Assert.Null(item); - } + Assert.Null(item); + } - [Fact] - public async Task WhenProjectPropertyContextHasLaunchProfileItemType_GetItemsAsyncReturnsAnItemWithAMatchingName() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + [Fact] + public async Task WhenProjectPropertyContextHasLaunchProfileItemType_GetItemsAsyncReturnsAnItemWithAMatchingName() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var context = new TestProjectPropertiesContext( - isProjectFile: true, - file: "Foo", - itemType: LaunchProfileProjectItemProvider.ItemType, - itemName: "Profile2"); + var context = new TestProjectPropertiesContext( + isProjectFile: true, + file: "Foo", + itemType: LaunchProfileProjectItemProvider.ItemType, + itemName: "Profile2"); - var item = await itemProvider.GetItemAsync(context); + var item = await itemProvider.GetItemAsync(context); - Assert.NotNull(item); - Assert.Equal("Profile2", item.EvaluatedInclude); - } + Assert.NotNull(item); + Assert.Equal("Profile2", item.EvaluatedInclude); + } - [Fact] - public async Task WhenGivenMultipleProjectPropertyContexts_GetItemsAsyncReturnsNullOrAnItemForEach() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var profile3 = new WritableLaunchProfile { Name = "Profile3" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] - { - profile1.ToLaunchProfile(), - profile2.ToLaunchProfile(), - profile3.ToLaunchProfile() - }); - - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); - - List contexts = new() + [Fact] + public async Task WhenGivenMultipleProjectPropertyContexts_GetItemsAsyncReturnsNullOrAnItemForEach() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var profile3 = new WritableLaunchProfile { Name = "Profile3" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { - new TestProjectPropertiesContext(true, "Foo", null, "Profile3"), - new TestProjectPropertiesContext(true, "Foo", "RandomItemType", "Profile2"), - new TestProjectPropertiesContext(true, "Foo", LaunchProfileProjectItemProvider.ItemType, "Profile1") - }; - - var items = await itemProvider.GetItemsAsync(contexts); + profile1.ToLaunchProfile(), + profile2.ToLaunchProfile(), + profile3.ToLaunchProfile() + }); - Assert.Collection(items, - item => Assert.Equal("Profile3", item!.EvaluatedInclude), - Assert.Null, - item => Assert.Equal("Profile1", item!.EvaluatedInclude)); - } + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - [Fact] - public async Task WhenFindingAnItemByName_TheMatchingItemIsReturnedIfItExists() + List contexts = new() { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + new TestProjectPropertiesContext(true, "Foo", null, "Profile3"), + new TestProjectPropertiesContext(true, "Foo", "RandomItemType", "Profile2"), + new TestProjectPropertiesContext(true, "Foo", LaunchProfileProjectItemProvider.ItemType, "Profile1") + }; - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var items = await itemProvider.GetItemsAsync(contexts); - var item = await itemProvider.FindItemByNameAsync("Profile2"); + Assert.Collection(items, + item => Assert.Equal("Profile3", item!.EvaluatedInclude), + Assert.Null, + item => Assert.Equal("Profile1", item!.EvaluatedInclude)); + } - Assert.NotNull(item); - Assert.Equal(expected: "Profile2", actual: item.EvaluatedInclude); - } + [Fact] + public async Task WhenFindingAnItemByName_TheMatchingItemIsReturnedIfItExists() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - [Fact] - public async Task WhenFindingAnItemByName_NullIsReturnedIfNoMatchingItemExists() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var item = await itemProvider.FindItemByNameAsync("Profile2"); - var item = await itemProvider.FindItemByNameAsync("Profile3"); + Assert.NotNull(item); + Assert.Equal(expected: "Profile2", actual: item.EvaluatedInclude); + } - Assert.Null(item); - } + [Fact] + public async Task WhenFindingAnItemByName_NullIsReturnedIfNoMatchingItemExists() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - [Fact] - public async Task WhenAddingAnItem_AnExceptionIsThrownIfTheItemTypeIsWrong() + var item = await itemProvider.FindItemByNameAsync("Profile3"); + + Assert.Null(item); + } + + [Fact] + public async Task WhenAddingAnItem_AnExceptionIsThrownIfTheItemTypeIsWrong() + { + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create(); + + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); + + await Assert.ThrowsAsync(async () => { - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create(); + var item = await itemProvider.AddAsync(itemType: "RandomItemType", include: "Alpha Profile"); + }); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact(Skip = "Item metadata has not yet been implemented.")] + public Task WhenAddingAnItem_TheReturnedItemHasAllTheExpectedMetadata() + { + throw new NotImplementedException(); + } - await Assert.ThrowsAsync(async () => + [Fact] + public async Task WhenAddingAnItem_TheReturnedItemHasTheCorrectName() + { + ILaunchProfile? newProfile = null; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + addOrUpdateProfileCallback: (p, a) => + { + newProfile = p; + }, + getProfilesCallback: initialProfiles => { - var item = await itemProvider.AddAsync(itemType: "RandomItemType", include: "Alpha Profile"); + Assert.NotNull(newProfile); + return initialProfiles.Add(newProfile); }); - } - - [Fact(Skip = "Item metadata has not yet been implemented.")] - public Task WhenAddingAnItem_TheReturnedItemHasAllTheExpectedMetadata() - { - throw new NotImplementedException(); - } - [Fact] - public async Task WhenAddingAnItem_TheReturnedItemHasTheCorrectName() - { - ILaunchProfile? newProfile = null; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - addOrUpdateProfileCallback: (p, a) => - { - newProfile = p; - }, - getProfilesCallback: initialProfiles => - { - Assert.NotNull(newProfile); - return initialProfiles.Add(newProfile); - }); - - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); - - var item = await itemProvider.AddAsync(itemType: LaunchProfileProjectItemProvider.ItemType, include: "Alpha Profile"); - - Assert.Equal(expected: "Alpha Profile", actual: item!.EvaluatedInclude); - Assert.Equal(expected: "Alpha Profile", actual: item!.UnevaluatedInclude); - } + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - [Fact] - public async Task WhenAddingMultipleItems_TheReturnedItemsHaveTheCorrectNames() - { - ImmutableList newProfiles = ImmutableList.Empty; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - addOrUpdateProfileCallback: (p, a) => - { - newProfiles = newProfiles.Add(p); - }, - getProfilesCallback: initialProfiles => - { - return initialProfiles.AddRange(newProfiles); - }); - - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); - - var items = await itemProvider.AddAsync( - new Tuple>?>[] - { - new(LaunchProfileProjectItemProvider.ItemType, "Alpha Profile", null), - new(LaunchProfileProjectItemProvider.ItemType, "Beta Profile", null), - new(LaunchProfileProjectItemProvider.ItemType, "Gamma Profile", null), - - }); - - Assert.Collection(items, - item => { Assert.Equal("Alpha Profile", item.EvaluatedInclude); Assert.Equal("Alpha Profile", item.UnevaluatedInclude); }, - item => { Assert.Equal("Beta Profile", item.EvaluatedInclude); Assert.Equal("Beta Profile", item.UnevaluatedInclude); }, - item => { Assert.Equal("Gamma Profile", item.EvaluatedInclude); Assert.Equal("Gamma Profile", item.UnevaluatedInclude); }); - } + var item = await itemProvider.AddAsync(itemType: LaunchProfileProjectItemProvider.ItemType, include: "Alpha Profile"); - [Fact] - public async Task WhenAddingAnItemAsAPath_AnInvalidOperationExceptionIsThrown() - { - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create(); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + Assert.Equal(expected: "Alpha Profile", actual: item!.EvaluatedInclude); + Assert.Equal(expected: "Alpha Profile", actual: item!.UnevaluatedInclude); + } - await Assert.ThrowsAsync(async () => + [Fact] + public async Task WhenAddingMultipleItems_TheReturnedItemsHaveTheCorrectNames() + { + ImmutableList newProfiles = ImmutableList.Empty; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + addOrUpdateProfileCallback: (p, a) => { - await itemProvider.AddAsync(@"C:\alpha\beta\gamma\delta.fakeextension"); + newProfiles = newProfiles.Add(p); + }, + getProfilesCallback: initialProfiles => + { + return initialProfiles.AddRange(newProfiles); }); - } - [Fact] - public async Task WhenAddingItemsAsPaths_AnInvalidOperationExceptionIsThrown() - { - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create(); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - await Assert.ThrowsAsync(async () => + var items = await itemProvider.AddAsync( + new Tuple>?>[] { - await itemProvider.AddAsync(new[] - { - @"C:\alpha\beta\gamma\delta.fakeextension", - @"C:\epsilon\phi\kappa\psi.fakeextension" - }); + new(LaunchProfileProjectItemProvider.ItemType, "Alpha Profile", null), + new(LaunchProfileProjectItemProvider.ItemType, "Beta Profile", null), + new(LaunchProfileProjectItemProvider.ItemType, "Gamma Profile", null), + }); - } - [Fact] - public async Task WhenRenamingAnItem_AnInvalidOperationExceptionIsThrown() + Assert.Collection(items, + item => { Assert.Equal("Alpha Profile", item.EvaluatedInclude); Assert.Equal("Alpha Profile", item.UnevaluatedInclude); }, + item => { Assert.Equal("Beta Profile", item.EvaluatedInclude); Assert.Equal("Beta Profile", item.UnevaluatedInclude); }, + item => { Assert.Equal("Gamma Profile", item.EvaluatedInclude); Assert.Equal("Gamma Profile", item.UnevaluatedInclude); }); + } + + [Fact] + public async Task WhenAddingAnItemAsAPath_AnInvalidOperationExceptionIsThrown() + { + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create(); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); + + await Assert.ThrowsAsync(async () => { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + await itemProvider.AddAsync(@"C:\alpha\beta\gamma\delta.fakeextension"); + }); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); - var itemToRename = await itemProvider.FindItemByNameAsync("Profile1"); + [Fact] + public async Task WhenAddingItemsAsPaths_AnInvalidOperationExceptionIsThrown() + { + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create(); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - await Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => + { + await itemProvider.AddAsync(new[] { - await itemProvider.SetUnevaluatedIncludesAsync(new KeyValuePair[] - { - new(itemToRename!, "New Profile Name") - }); + @"C:\alpha\beta\gamma\delta.fakeextension", + @"C:\epsilon\phi\kappa\psi.fakeextension" }); - } + }); + } + + [Fact] + public async Task WhenRenamingAnItem_AnInvalidOperationExceptionIsThrown() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - [Fact] - public async Task WhenRemovingAnItem_TheRemoveIsIgnoredIfTheItemTypeIsWrong() + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); + var itemToRename = await itemProvider.FindItemByNameAsync("Profile1"); + + await Assert.ThrowsAsync(async () => { - string? removedProfileName = null; + await itemProvider.SetUnevaluatedIncludesAsync(new KeyValuePair[] + { + new(itemToRename!, "New Profile Name") + }); + }); + } - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }, - removeProfileCallback: name => removedProfileName = name); + [Fact] + public async Task WhenRemovingAnItem_TheRemoveIsIgnoredIfTheItemTypeIsWrong() + { + string? removedProfileName = null; - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); - - await itemProvider.RemoveAsync(itemType: "RandomItemType", include: "Profile1"); + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }, + removeProfileCallback: name => removedProfileName = name); - Assert.Null(removedProfileName); - } + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); + + await itemProvider.RemoveAsync(itemType: "RandomItemType", include: "Profile1"); - [Fact] - public async Task WhenRemovingAnItem_TheProfileIsRemovedFromTheLaunchSettings() - { - string? removedProfileName = null; + Assert.Null(removedProfileName); + } - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }, - removeProfileCallback: name => removedProfileName = name); + [Fact] + public async Task WhenRemovingAnItem_TheProfileIsRemovedFromTheLaunchSettings() + { + string? removedProfileName = null; - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }, + removeProfileCallback: name => removedProfileName = name); - await itemProvider.RemoveAsync(itemType: LaunchProfileProjectItemProvider.ItemType, include: "Profile1"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - Assert.Equal(expected: "Profile1", actual: removedProfileName); - } + await itemProvider.RemoveAsync(itemType: LaunchProfileProjectItemProvider.ItemType, include: "Profile1"); - [Fact] - public async Task WhenRetrievingAProjectItem_TheEvaluatedIncludeIsTheProfileName() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + Assert.Equal(expected: "Profile1", actual: removedProfileName); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact] + public async Task WhenRetrievingAProjectItem_TheEvaluatedIncludeIsTheProfileName() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - Assert.NotNull(projectItem); + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - Assert.Equal(expected: "Profile2", actual: projectItem.EvaluatedInclude); - } + Assert.NotNull(projectItem); - [Fact] - public async Task WhenRetrievingAProjectItem_TheUnevaluatedIncludeIsTheProfileName() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + Assert.Equal(expected: "Profile2", actual: projectItem.EvaluatedInclude); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact] + public async Task WhenRetrievingAProjectItem_TheUnevaluatedIncludeIsTheProfileName() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - Assert.NotNull(projectItem); + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - Assert.Equal(expected: "Profile2", actual: projectItem.UnevaluatedInclude); - } + Assert.NotNull(projectItem); - [Fact] - public async Task WhenRetrievingAProjectItem_TheItemTypeIsLaunchProfile() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + Assert.Equal(expected: "Profile2", actual: projectItem.UnevaluatedInclude); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact] + public async Task WhenRetrievingAProjectItem_TheItemTypeIsLaunchProfile() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - Assert.NotNull(projectItem); + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - Assert.Equal(expected: LaunchProfileProjectItemProvider.ItemType, actual: projectItem.ItemType); - } + Assert.NotNull(projectItem); - [Fact] - public async Task WhenRetrievingAProjectItem_TheIncludesAsPathsAreTheEmptyString() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + Assert.Equal(expected: LaunchProfileProjectItemProvider.ItemType, actual: projectItem.ItemType); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact] + public async Task WhenRetrievingAProjectItem_TheIncludesAsPathsAreTheEmptyString() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - Assert.NotNull(projectItem); + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - Assert.Equal(expected: string.Empty, actual: projectItem.EvaluatedIncludeAsFullPath); - Assert.Equal(expected: string.Empty, actual: projectItem.EvaluatedIncludeAsRelativePath); - } + Assert.NotNull(projectItem); - [Fact] - public async Task WhenSettingTheUnevaluatedIncludeOnAProjectItem_AnInvalidOperationExceptionIsThrown() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + Assert.Equal(expected: string.Empty, actual: projectItem.EvaluatedIncludeAsFullPath); + Assert.Equal(expected: string.Empty, actual: projectItem.EvaluatedIncludeAsRelativePath); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact] + public async Task WhenSettingTheUnevaluatedIncludeOnAProjectItem_AnInvalidOperationExceptionIsThrown() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - await Assert.ThrowsAsync(async () => - { - await projectItem!.SetUnevaluatedIncludeAsync("New Profile Name"); - }); - } + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - [Fact] - public async Task WhenSettingTheItemTypeOnAProjectItem_AnInvalidOperationExceptionIsThrown() + await Assert.ThrowsAsync(async () => { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + await projectItem!.SetUnevaluatedIncludeAsync("New Profile Name"); + }); + } - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + [Fact] + public async Task WhenSettingTheItemTypeOnAProjectItem_AnInvalidOperationExceptionIsThrown() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - await Assert.ThrowsAsync(async () => - { - await projectItem!.SetItemTypeAsync("RandomItemType"); - }); - } + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - [Fact] - public async Task WhenRetrievingAProjectItem_ThePropertiesContextHasTheExpectedValues() + await Assert.ThrowsAsync(async () => { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.ImplementFullPath(@"C:\alpha\beta\gamma.csproj"), - launchSettingsProvider); - - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - var propertiesContext = projectItem!.PropertiesContext!; - - Assert.Equal(expected: @"C:\alpha\beta\gamma.csproj", actual: propertiesContext.File); - Assert.True(propertiesContext.IsProjectFile); - Assert.Equal(expected: "Profile2", actual: propertiesContext.ItemName); - Assert.Equal(expected: LaunchProfileProjectItemProvider.ItemType, actual: propertiesContext.ItemType); - } + await projectItem!.SetItemTypeAsync("RandomItemType"); + }); + } - [Fact] - public async Task WhenTellingAProjectItemToRemoveItself_TheLaunchProfileIsRemoved() - { - string? removedProfileName = null; + [Fact] + public async Task WhenRetrievingAProjectItem_ThePropertiesContextHasTheExpectedValues() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.ImplementFullPath(@"C:\alpha\beta\gamma.csproj"), + launchSettingsProvider); + + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var propertiesContext = projectItem!.PropertiesContext!; + + Assert.Equal(expected: @"C:\alpha\beta\gamma.csproj", actual: propertiesContext.File); + Assert.True(propertiesContext.IsProjectFile); + Assert.Equal(expected: "Profile2", actual: propertiesContext.ItemName); + Assert.Equal(expected: LaunchProfileProjectItemProvider.ItemType, actual: propertiesContext.ItemType); + } + + [Fact] + public async Task WhenTellingAProjectItemToRemoveItself_TheLaunchProfileIsRemoved() + { + string? removedProfileName = null; - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }, - removeProfileCallback: name => removedProfileName = name); + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }, + removeProfileCallback: name => removedProfileName = name); - var itemProvider = new LaunchProfileProjectItemProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider); + var itemProvider = new LaunchProfileProjectItemProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider); - var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); + var projectItem = await itemProvider.FindItemByNameAsync("Profile2"); - await projectItem!.RemoveAsync(); + await projectItem!.RemoveAsync(); - Assert.Equal(expected: "Profile2", actual: removedProfileName); - } + Assert.Equal(expected: "Profile2", actual: removedProfileName); + } - private class TestProjectPropertiesContext : IProjectPropertiesContext + private class TestProjectPropertiesContext : IProjectPropertiesContext + { + public TestProjectPropertiesContext(bool isProjectFile, string file, string? itemType, string? itemName) { - public TestProjectPropertiesContext(bool isProjectFile, string file, string? itemType, string? itemName) - { - IsProjectFile = isProjectFile; - File = file; - ItemType = itemType; - ItemName = itemName; - } - - public bool IsProjectFile { get; } - public string File { get; } - public string? ItemType { get; } - public string? ItemName { get; } + IsProjectFile = isProjectFile; + File = file; + ItemType = itemType; + ItemName = itemName; } + + public bool IsProjectFile { get; } + public string File { get; } + public string? ItemType { get; } + public string? ItemName { get; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesProviderTests.cs index 4924930acc..8fa030faf1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesProviderTests.cs @@ -2,165 +2,164 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class LaunchProfilesProjectPropertiesProviderTests { - public class LaunchProfilesProjectPropertiesProviderTests + private const string DefaultTestProjectPath = @"C:\alpha\beta\gamma.csproj"; + + private static readonly IEnumerable> EmptyLaunchProfileExtensionValueProviders = + Enumerable.Empty>(); + private static readonly IEnumerable> EmptyGlobalSettingExtensionValueProviders = + Enumerable.Empty>(); + + [Fact] + public void DefaultProjectPath_IsTheProjectPath() + { + var provider = new LaunchProfileProjectPropertiesProvider( + CreateDefaultTestProject(), + ILaunchSettingsProviderFactory.Create(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var defaultProjectPath = provider.DefaultProjectPath; + Assert.Equal(expected: DefaultTestProjectPath, actual: defaultProjectPath); + } + + [Fact] + public void WhenRetrievingProjectLevelProperties_NullIsReturned() + { + var project = UnconfiguredProjectFactory.Create(); + var provider = new LaunchProfileProjectPropertiesProvider( + project, + ILaunchSettingsProviderFactory.Create(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var commonProperties = provider.GetCommonProperties(); + + Assert.Null(commonProperties); + } + + [Fact] + public void WhenRetrievingItemTypeProperties_NullIsReturned() + { + var project = UnconfiguredProjectFactory.Create(); + var provider = new LaunchProfileProjectPropertiesProvider( + project, + ILaunchSettingsProviderFactory.Create(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var itemTypeProperties = provider.GetItemTypeProperties(LaunchProfileProjectItemProvider.ItemType); + + Assert.Null(itemTypeProperties); + } + + [Fact] + public void WhenRetrievingItemProperties_NullIsReturnedIfTheItemIsNull() + { + var project = UnconfiguredProjectFactory.Create(); + var provider = new LaunchProfileProjectPropertiesProvider( + project, + ILaunchSettingsProviderFactory.Create(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var itemProperties = provider.GetItemProperties(LaunchProfileProjectItemProvider.ItemType, item: null); + + Assert.Null(itemProperties); + } + + [Fact] + public void WhenRetrievingItemProperties_PropertiesAreReturnedIfTheItemTypeMatches() + { + var provider = new LaunchProfileProjectPropertiesProvider( + UnconfiguredProjectFactory.Create(), + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var properties = provider.GetItemProperties( + itemType: LaunchProfileProjectItemProvider.ItemType, + item: "Profile1"); + + Assert.NotNull(properties); + } + + [Fact] + public void WhenRetrievingItemProperties_PropertiesAreReturnedIfTheItemTypeIsNull() + { + var provider = new LaunchProfileProjectPropertiesProvider( + UnconfiguredProjectFactory.Create(), + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var properties = provider.GetItemProperties( + itemType: null, + item: "Profile1"); + + Assert.NotNull(properties); + } + + [Fact] + public void WhenRetrievingItemProperties_NullIsReturnedIfTheItemTypeDoesNotMatch() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + + var provider = new LaunchProfileProjectPropertiesProvider( + UnconfiguredProjectFactory.Create(), + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var properties = provider.GetItemProperties( + itemType: "RandomItemType", + item: "Profile1"); + + Assert.Null(properties); + } + + [Fact] + public void WhenRetrievingItemProperties_NullIsReturnedWhenTheFilePathIsNotTheProjectPath() + { + var provider = new LaunchProfileProjectPropertiesProvider( + CreateDefaultTestProject(), + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var properties = provider.GetProperties( + file: @"C:\sigma\lambda\other.csproj", + itemType: LaunchProfileProjectItemProvider.ItemType, + item: "Profile1"); + + Assert.Null(properties); + } + + /// + /// Creates an where the + /// is set to . + /// + private static UnconfiguredProject CreateDefaultTestProject() + { + return UnconfiguredProjectFactory.ImplementFullPath(DefaultTestProjectPath); + } + + /// + /// Creates an with two empty profiles named + /// "Profile1" and "Profile2". + /// + private static ILaunchSettingsProvider3 CreateDefaultTestLaunchSettings() { - private const string DefaultTestProjectPath = @"C:\alpha\beta\gamma.csproj"; - - private static readonly IEnumerable> EmptyLaunchProfileExtensionValueProviders = - Enumerable.Empty>(); - private static readonly IEnumerable> EmptyGlobalSettingExtensionValueProviders = - Enumerable.Empty>(); - - [Fact] - public void DefaultProjectPath_IsTheProjectPath() - { - var provider = new LaunchProfileProjectPropertiesProvider( - CreateDefaultTestProject(), - ILaunchSettingsProviderFactory.Create(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var defaultProjectPath = provider.DefaultProjectPath; - Assert.Equal(expected: DefaultTestProjectPath, actual: defaultProjectPath); - } - - [Fact] - public void WhenRetrievingProjectLevelProperties_NullIsReturned() - { - var project = UnconfiguredProjectFactory.Create(); - var provider = new LaunchProfileProjectPropertiesProvider( - project, - ILaunchSettingsProviderFactory.Create(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var commonProperties = provider.GetCommonProperties(); - - Assert.Null(commonProperties); - } - - [Fact] - public void WhenRetrievingItemTypeProperties_NullIsReturned() - { - var project = UnconfiguredProjectFactory.Create(); - var provider = new LaunchProfileProjectPropertiesProvider( - project, - ILaunchSettingsProviderFactory.Create(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var itemTypeProperties = provider.GetItemTypeProperties(LaunchProfileProjectItemProvider.ItemType); - - Assert.Null(itemTypeProperties); - } - - [Fact] - public void WhenRetrievingItemProperties_NullIsReturnedIfTheItemIsNull() - { - var project = UnconfiguredProjectFactory.Create(); - var provider = new LaunchProfileProjectPropertiesProvider( - project, - ILaunchSettingsProviderFactory.Create(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var itemProperties = provider.GetItemProperties(LaunchProfileProjectItemProvider.ItemType, item: null); - - Assert.Null(itemProperties); - } - - [Fact] - public void WhenRetrievingItemProperties_PropertiesAreReturnedIfTheItemTypeMatches() - { - var provider = new LaunchProfileProjectPropertiesProvider( - UnconfiguredProjectFactory.Create(), - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var properties = provider.GetItemProperties( - itemType: LaunchProfileProjectItemProvider.ItemType, - item: "Profile1"); - - Assert.NotNull(properties); - } - - [Fact] - public void WhenRetrievingItemProperties_PropertiesAreReturnedIfTheItemTypeIsNull() - { - var provider = new LaunchProfileProjectPropertiesProvider( - UnconfiguredProjectFactory.Create(), - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var properties = provider.GetItemProperties( - itemType: null, - item: "Profile1"); - - Assert.NotNull(properties); - } - - [Fact] - public void WhenRetrievingItemProperties_NullIsReturnedIfTheItemTypeDoesNotMatch() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - - var provider = new LaunchProfileProjectPropertiesProvider( - UnconfiguredProjectFactory.Create(), - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var properties = provider.GetItemProperties( - itemType: "RandomItemType", - item: "Profile1"); - - Assert.Null(properties); - } - - [Fact] - public void WhenRetrievingItemProperties_NullIsReturnedWhenTheFilePathIsNotTheProjectPath() - { - var provider = new LaunchProfileProjectPropertiesProvider( - CreateDefaultTestProject(), - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var properties = provider.GetProperties( - file: @"C:\sigma\lambda\other.csproj", - itemType: LaunchProfileProjectItemProvider.ItemType, - item: "Profile1"); - - Assert.Null(properties); - } - - /// - /// Creates an where the - /// is set to . - /// - private static UnconfiguredProject CreateDefaultTestProject() - { - return UnconfiguredProjectFactory.ImplementFullPath(DefaultTestProjectPath); - } - - /// - /// Creates an with two empty profiles named - /// "Profile1" and "Profile2". - /// - private static ILaunchSettingsProvider3 CreateDefaultTestLaunchSettings() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - return launchSettingsProvider; - } + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + return launchSettingsProvider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesTests.cs index 495663960f..1eec9a2984 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/LaunchProfilesProjectPropertiesTests.cs @@ -4,694 +4,693 @@ using Microsoft.VisualStudio.Mocks; using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class LaunchProfilesProjectPropertiesTests { - public class LaunchProfilesProjectPropertiesTests + private const string DefaultTestProjectPath = @"C:\alpha\beta\gamma.csproj"; + + private static readonly ImmutableArray> EmptyLaunchProfileExtensionValueProviders = + ImmutableArray>.Empty; + private static readonly ImmutableArray> EmptyGlobalSettingExtensionValueProviders = + ImmutableArray>.Empty; + + [Fact] + public void WhenRetrievingItemProperties_TheContextHasTheExpectedValues() { - private const string DefaultTestProjectPath = @"C:\alpha\beta\gamma.csproj"; + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var context = properties.Context; + + Assert.Equal(expected: DefaultTestProjectPath, actual: context.File); + Assert.True(context.IsProjectFile); + Assert.Equal(expected: "Profile1", actual: context.ItemName); + Assert.Equal(expected: LaunchProfileProjectItemProvider.ItemType, actual: context.ItemType); + } - private static readonly ImmutableArray> EmptyLaunchProfileExtensionValueProviders = - ImmutableArray>.Empty; - private static readonly ImmutableArray> EmptyGlobalSettingExtensionValueProviders = - ImmutableArray>.Empty; + [Fact] + public void WhenRetrievingItemProperties_TheFilePathIsTheProjectPath() + { + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + Assert.Equal(expected: DefaultTestProjectPath, actual: properties.FileFullPath); + } - [Fact] - public void WhenRetrievingItemProperties_TheContextHasTheExpectedValues() - { - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var context = properties.Context; - - Assert.Equal(expected: DefaultTestProjectPath, actual: context.File); - Assert.True(context.IsProjectFile); - Assert.Equal(expected: "Profile1", actual: context.ItemName); - Assert.Equal(expected: LaunchProfileProjectItemProvider.ItemType, actual: context.ItemType); - } + [Fact] + public void WhenRetrievingItemProperties_ThePropertyKindIsItemGroup() + { + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + Assert.Equal(expected: PropertyKind.ItemGroup, actual: properties.PropertyKind); + } - [Fact] - public void WhenRetrievingItemProperties_TheFilePathIsTheProjectPath() - { - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - Assert.Equal(expected: DefaultTestProjectPath, actual: properties.FileFullPath); - } + [Fact] + public async Task WhenRetrievingItemPropertyNames_AllStandardProfilePropertyNamesAreReturnedEvenIfNotDefined() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var propertyNames = await properties.GetPropertyNamesAsync(); + + Assert.Contains("CommandName", propertyNames); + Assert.Contains("ExecutablePath", propertyNames); + Assert.Contains("CommandLineArguments", propertyNames); + Assert.Contains("WorkingDirectory", propertyNames); + Assert.Contains("LaunchBrowser", propertyNames); + Assert.Contains("LaunchUrl", propertyNames); + Assert.Contains("EnvironmentVariables", propertyNames); + } - [Fact] - public void WhenRetrievingItemProperties_ThePropertyKindIsItemGroup() + [Fact] + public async Task WhenRetrievingStandardPropertyValues_TheEmptyStringIsReturnedForUndefinedProperties() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var standardPropertyNames = new[] { - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - Assert.Equal(expected: PropertyKind.ItemGroup, actual: properties.PropertyKind); + "CommandName", + "ExecutablePath", + "CommandLineArguments", + "WorkingDirectory", + "LaunchUrl", + "EnvironmentVariables" + }; + + foreach (var standardPropertyName in standardPropertyNames) + { + var evaluatedValue = await properties.GetEvaluatedPropertyValueAsync(standardPropertyName); + Assert.Equal(expected: string.Empty, actual: evaluatedValue); + var unevaluatedValue = await properties.GetUnevaluatedPropertyValueAsync(standardPropertyName); + Assert.Equal(expected: string.Empty, actual: unevaluatedValue); } + } + + [Fact] + public async Task WhenRetrievingTheLaunchBrowserValue_TheDefaultValueIsFalse() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var evaluatedValue = await properties.GetEvaluatedPropertyValueAsync("LaunchBrowser"); + Assert.Equal(expected: "false", actual: evaluatedValue); + var unevaluatedValue = await properties.GetUnevaluatedPropertyValueAsync("LaunchBrowser"); + Assert.Equal(expected: "false", actual: unevaluatedValue); + } - [Fact] - public async Task WhenRetrievingItemPropertyNames_AllStandardProfilePropertyNamesAreReturnedEvenIfNotDefined() + [Fact] + public async Task WhenRetrievingStandardPropertyValues_TheExpectedValuesAreReturned() + { + var profile1 = new WritableLaunchProfile + { + Name = "Profile1", + CommandLineArgs = "alpha beta gamma", + CommandName = "epsilon", + EnvironmentVariables = { ["One"] = "1", ["Two"] = "2" }, + ExecutablePath = @"D:\five\six\seven\eight.exe", + LaunchBrowser = true, + LaunchUrl = "https://localhost/profile", + WorkingDirectory = @"C:\users\other\temp" + }; + + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }); + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var expectedValues = new Dictionary + { + ["CommandLineArguments"] = "alpha beta gamma", + ["CommandName"] = "epsilon", + ["EnvironmentVariables"] = "One=1,Two=2", + ["ExecutablePath"] = @"D:\five\six\seven\eight.exe", + ["LaunchBrowser"] = "true", + ["LaunchUrl"] = "https://localhost/profile", + ["WorkingDirectory"] = @"C:\users\other\temp", + }; + + foreach (var (propertyName, expectedPropertyValue) in expectedValues) { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var propertyNames = await properties.GetPropertyNamesAsync(); - - Assert.Contains("CommandName", propertyNames); - Assert.Contains("ExecutablePath", propertyNames); - Assert.Contains("CommandLineArguments", propertyNames); - Assert.Contains("WorkingDirectory", propertyNames); - Assert.Contains("LaunchBrowser", propertyNames); - Assert.Contains("LaunchUrl", propertyNames); - Assert.Contains("EnvironmentVariables", propertyNames); + var actualUnevaluatedValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); + var actualEvaluatedValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); + Assert.Equal(expectedPropertyValue, actualUnevaluatedValue); + Assert.Equal(expectedPropertyValue, actualEvaluatedValue); } + } - [Fact] - public async Task WhenRetrievingStandardPropertyValues_TheEmptyStringIsReturnedForUndefinedProperties() + [Fact] + public async Task WhenSettingStandardPropertyValues_StandardCallbacksAreFound() + { + var profile1 = new WritableLaunchProfile { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var standardPropertyNames = new[] + Name = "Profile1", + CommandName = "epsilon", + }; + + bool callbackInvoked; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }, + tryUpdateProfileCallback: (profile, action) => { - "CommandName", - "ExecutablePath", - "CommandLineArguments", - "WorkingDirectory", - "LaunchUrl", - "EnvironmentVariables" - }; - - foreach (var standardPropertyName in standardPropertyNames) - { - var evaluatedValue = await properties.GetEvaluatedPropertyValueAsync(standardPropertyName); - Assert.Equal(expected: string.Empty, actual: evaluatedValue); - var unevaluatedValue = await properties.GetUnevaluatedPropertyValueAsync(standardPropertyName); - Assert.Equal(expected: string.Empty, actual: unevaluatedValue); - } - } - - [Fact] - public async Task WhenRetrievingTheLaunchBrowserValue_TheDefaultValueIsFalse() + callbackInvoked = true; + }); + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var newValues = new Dictionary + { + ["CommandLineArguments"] = "delta epsilon", + ["CommandName"] = "arugula", + ["EnvironmentVariables"] = "Three=3,Four=4", + ["ExecutablePath"] = @"D:\nine\ten.exe", + ["LaunchBrowser"] = "false", + ["LaunchUrl"] = "https://localhost/myOtherProfile", + ["WorkingDirectory"] = @"D:\aardvark", + }; + + foreach (var (propertyName, newPropertyValue) in newValues) { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var evaluatedValue = await properties.GetEvaluatedPropertyValueAsync("LaunchBrowser"); - Assert.Equal(expected: "false", actual: evaluatedValue); - var unevaluatedValue = await properties.GetUnevaluatedPropertyValueAsync("LaunchBrowser"); - Assert.Equal(expected: "false", actual: unevaluatedValue); + callbackInvoked = false; + await properties.SetPropertyValueAsync(propertyName, newPropertyValue); + Assert.True(callbackInvoked); } + } - [Fact] - public async Task WhenRetrievingStandardPropertyValues_TheExpectedValuesAreReturned() - { - var profile1 = new WritableLaunchProfile - { - Name = "Profile1", - CommandLineArgs = "alpha beta gamma", - CommandName = "epsilon", - EnvironmentVariables = { ["One"] = "1", ["Two"] = "2" }, - ExecutablePath = @"D:\five\six\seven\eight.exe", - LaunchBrowser = true, - LaunchUrl = "https://localhost/profile", - WorkingDirectory = @"C:\users\other\temp" - }; - - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }); - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var expectedValues = new Dictionary + [Fact] + public async Task WhenRetrievingAnExtensionProperty_TheExtensionValueProviderIsCalled() + { + string? requestedPropertyName = null; + var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( + (propertyName, profile, globals, rule) => { - ["CommandLineArguments"] = "alpha beta gamma", - ["CommandName"] = "epsilon", - ["EnvironmentVariables"] = "One=1,Two=2", - ["ExecutablePath"] = @"D:\five\six\seven\eight.exe", - ["LaunchBrowser"] = "true", - ["LaunchUrl"] = "https://localhost/profile", - ["WorkingDirectory"] = @"C:\users\other\temp", - }; - - foreach (var (propertyName, expectedPropertyValue) in expectedValues) + requestedPropertyName = propertyName; + return "alpha"; + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + ImmutableArray.Create(lazy), + EmptyGlobalSettingExtensionValueProviders); + + var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); + Assert.Equal(expected: "MyProperty", actual: requestedPropertyName); + Assert.Equal(expected: "alpha", actual: propertyValue); + } + + [Fact] + public async Task WhenRetrievingAnExtensionProperty_TheRuleIsPassedToTheExtensionValueProvider() + { + bool rulePassed = false; + var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( + (propertyName, profile, globals, rule) => { - var actualUnevaluatedValue = await properties.GetUnevaluatedPropertyValueAsync(propertyName); - var actualEvaluatedValue = await properties.GetEvaluatedPropertyValueAsync(propertyName); - Assert.Equal(expectedPropertyValue, actualUnevaluatedValue); - Assert.Equal(expectedPropertyValue, actualEvaluatedValue); - } - } + rulePassed = rule is not null; + return "alpha"; + }); - [Fact] - public async Task WhenSettingStandardPropertyValues_StandardCallbacksAreFound() - { - var profile1 = new WritableLaunchProfile + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + ImmutableArray.Create(lazy), + EmptyGlobalSettingExtensionValueProviders); + + var rule = new Rule(); + properties.SetRuleContext(rule); + + var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); + Assert.True(rulePassed); + Assert.Equal(expected: "alpha", actual: propertyValue); + } + + [Fact] + public async Task WhenRetrievingPropertyNames_LaunchProfileExtensionNamesAreIncludedForDefinedProperties() + { + var alphaValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( + (propertyName, profile, globals, rule) => { - Name = "Profile1", - CommandName = "epsilon", - }; - - bool callbackInvoked; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }, - tryUpdateProfileCallback: (profile, action) => - { - callbackInvoked = true; - }); - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var newValues = new Dictionary + return "alpha"; + }); + var alphaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("AlphaProperty"); + var alphaLazy = new Lazy( + () => alphaValueProvider, + alphaMetadata); + + var betaValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( + (propertyName, profile, globals, rule) => { - ["CommandLineArguments"] = "delta epsilon", - ["CommandName"] = "arugula", - ["EnvironmentVariables"] = "Three=3,Four=4", - ["ExecutablePath"] = @"D:\nine\ten.exe", - ["LaunchBrowser"] = "false", - ["LaunchUrl"] = "https://localhost/myOtherProfile", - ["WorkingDirectory"] = @"D:\aardvark", - }; - - foreach (var (propertyName, newPropertyValue) in newValues) + return ""; + }); + var betaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("BetaProperty"); + var betaLazy = new Lazy( + () => betaValueProvider, + betaMetadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + ImmutableArray.Create(alphaLazy, betaLazy), + EmptyGlobalSettingExtensionValueProviders); + + var names = await properties.GetPropertyNamesAsync(); + + Assert.Contains("AlphaProperty", names); + Assert.DoesNotContain("BetaProperty", names); + } + + [Fact] + public async Task WhenSettingAnExtensionProperty_TheExtensionValueProviderIsCalled() + { + string? updatedPropertyName = null; + string? updatedPropertyValue = null; + var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( + onSetPropertyValue: (propertyName, value, profile, globals, rule) => { - callbackInvoked = false; - await properties.SetPropertyValueAsync(propertyName, newPropertyValue); - Assert.True(callbackInvoked); - } - } + updatedPropertyName = propertyName; + updatedPropertyValue = value; + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + ImmutableArray.Create(lazy), + EmptyGlobalSettingExtensionValueProviders); + + await properties.SetPropertyValueAsync("MyProperty", "alpha"); + + Assert.Equal(expected: "MyProperty", actual: updatedPropertyName); + Assert.Equal(expected: "alpha", actual: updatedPropertyValue); + } - [Fact] - public async Task WhenRetrievingAnExtensionProperty_TheExtensionValueProviderIsCalled() - { - string? requestedPropertyName = null; - var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( - (propertyName, profile, globals, rule) => - { - requestedPropertyName = propertyName; - return "alpha"; - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - ImmutableArray.Create(lazy), - EmptyGlobalSettingExtensionValueProviders); - - var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); - Assert.Equal(expected: "MyProperty", actual: requestedPropertyName); - Assert.Equal(expected: "alpha", actual: propertyValue); - } + [Fact] + public async Task WhenSettingAnExtensionProperty_TheRuleIsPassedToTheExtensionValueProvider() + { + bool rulePassed = false; + var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( + onSetPropertyValue: (propertyName, value, profile, globals, rule) => + { + rulePassed = rule is not null; + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - [Fact] - public async Task WhenRetrievingAnExtensionProperty_TheRuleIsPassedToTheExtensionValueProvider() - { - bool rulePassed = false; - var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( - (propertyName, profile, globals, rule) => - { - rulePassed = rule is not null; - return "alpha"; - }); - - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - ImmutableArray.Create(lazy), - EmptyGlobalSettingExtensionValueProviders); - - var rule = new Rule(); - properties.SetRuleContext(rule); - - var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); - Assert.True(rulePassed); - Assert.Equal(expected: "alpha", actual: propertyValue); - } + var lazy = new Lazy( + () => extensionValueProvider, + metadata); - [Fact] - public async Task WhenRetrievingPropertyNames_LaunchProfileExtensionNamesAreIncludedForDefinedProperties() - { - var alphaValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( - (propertyName, profile, globals, rule) => - { - return "alpha"; - }); - var alphaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("AlphaProperty"); - var alphaLazy = new Lazy( - () => alphaValueProvider, - alphaMetadata); - - var betaValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( - (propertyName, profile, globals, rule) => - { - return ""; - }); - var betaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("BetaProperty"); - var betaLazy = new Lazy( - () => betaValueProvider, - betaMetadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - ImmutableArray.Create(alphaLazy, betaLazy), - EmptyGlobalSettingExtensionValueProviders); - - var names = await properties.GetPropertyNamesAsync(); - - Assert.Contains("AlphaProperty", names); - Assert.DoesNotContain("BetaProperty", names); - } + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + ImmutableArray.Create(lazy), + EmptyGlobalSettingExtensionValueProviders); + properties.SetRuleContext(new Rule()); - [Fact] - public async Task WhenSettingAnExtensionProperty_TheExtensionValueProviderIsCalled() - { - string? updatedPropertyName = null; - string? updatedPropertyValue = null; - var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( - onSetPropertyValue: (propertyName, value, profile, globals, rule) => - { - updatedPropertyName = propertyName; - updatedPropertyValue = value; - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - ImmutableArray.Create(lazy), - EmptyGlobalSettingExtensionValueProviders); - - await properties.SetPropertyValueAsync("MyProperty", "alpha"); - - Assert.Equal(expected: "MyProperty", actual: updatedPropertyName); - Assert.Equal(expected: "alpha", actual: updatedPropertyValue); - } + await properties.SetPropertyValueAsync("MyProperty", "alpha"); - [Fact] - public async Task WhenSettingAnExtensionProperty_TheRuleIsPassedToTheExtensionValueProvider() - { - bool rulePassed = false; - var extensionValueProvider = ILaunchProfileExtensionValueProviderFactory.Create( - onSetPropertyValue: (propertyName, value, profile, globals, rule) => - { - rulePassed = rule is not null; - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - ImmutableArray.Create(lazy), - EmptyGlobalSettingExtensionValueProviders); - properties.SetRuleContext(new Rule()); - - await properties.SetPropertyValueAsync("MyProperty", "alpha"); - - Assert.True(rulePassed); - } + Assert.True(rulePassed); + } - [Fact] - public async Task WhenRetrievingAGlobalProperty_TheExtensionValueProviderIsCalled() - { - string? requestedPropertyName = null; - var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( - (propertyName, globals, rule) => - { - requestedPropertyName = propertyName; - return "alpha"; - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - ImmutableArray.Create(lazy)); - - var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); - Assert.Equal(expected: "MyProperty", actual: requestedPropertyName); - Assert.Equal(expected: "alpha", actual: propertyValue); - } + [Fact] + public async Task WhenRetrievingAGlobalProperty_TheExtensionValueProviderIsCalled() + { + string? requestedPropertyName = null; + var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( + (propertyName, globals, rule) => + { + requestedPropertyName = propertyName; + return "alpha"; + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + ImmutableArray.Create(lazy)); + + var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); + Assert.Equal(expected: "MyProperty", actual: requestedPropertyName); + Assert.Equal(expected: "alpha", actual: propertyValue); + } - [Fact] - public async Task WhenRetrievingAGlobalProperty_TheRuleIsPassedToTheExtensionValueProvider() - { - bool rulePassed = false; - var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( - (propertyName, globals, rule) => - { - rulePassed = rule is not null; - return "alpha"; - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - ImmutableArray.Create(lazy)); - properties.SetRuleContext(new Rule()); - - var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); - Assert.True(rulePassed); - Assert.Equal(expected: "alpha", actual: propertyValue); - } + [Fact] + public async Task WhenRetrievingAGlobalProperty_TheRuleIsPassedToTheExtensionValueProvider() + { + bool rulePassed = false; + var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( + (propertyName, globals, rule) => + { + rulePassed = rule is not null; + return "alpha"; + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + ImmutableArray.Create(lazy)); + properties.SetRuleContext(new Rule()); + + var propertyValue = await properties.GetEvaluatedPropertyValueAsync("MyProperty"); + Assert.True(rulePassed); + Assert.Equal(expected: "alpha", actual: propertyValue); + } - [Fact] - public async Task WhenSettingAGlobalProperty_TheExtensionValueProviderIsCalled() - { - string? updatedPropertyName = null; - string? updatedPropertyValue = null; - var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( - onSetPropertyValue: (propertyName, value, globals, rule) => - { - updatedPropertyName = propertyName; - updatedPropertyValue = value; - return ImmutableDictionary.Empty.Add(propertyName, value); - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - ImmutableArray.Create(lazy)); - - await properties.SetPropertyValueAsync("MyProperty", "alpha"); - - Assert.Equal(expected: "MyProperty", actual: updatedPropertyName); - Assert.Equal(expected: "alpha", actual: updatedPropertyValue); - } + [Fact] + public async Task WhenSettingAGlobalProperty_TheExtensionValueProviderIsCalled() + { + string? updatedPropertyName = null; + string? updatedPropertyValue = null; + var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( + onSetPropertyValue: (propertyName, value, globals, rule) => + { + updatedPropertyName = propertyName; + updatedPropertyValue = value; + return ImmutableDictionary.Empty.Add(propertyName, value); + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + ImmutableArray.Create(lazy)); + + await properties.SetPropertyValueAsync("MyProperty", "alpha"); + + Assert.Equal(expected: "MyProperty", actual: updatedPropertyName); + Assert.Equal(expected: "alpha", actual: updatedPropertyValue); + } + + [Fact] + public async Task WhenSettingAGlobalProperty_TheRuleIsPassedToTheExtensionValueProvider() + { + bool rulePassed = false; + var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( + onSetPropertyValue: (propertyName, value, globals, rule) => + { + rulePassed = rule is not null; + return ImmutableDictionary.Empty.Add(propertyName, value); + }); + var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); + + var lazy = new Lazy( + () => extensionValueProvider, + metadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + ImmutableArray.Create(lazy)); + properties.SetRuleContext(new Rule()); + + await properties.SetPropertyValueAsync("MyProperty", "alpha"); + + Assert.True(rulePassed); + } + + [Fact] + public async Task WhenRetrievingPropertyNames_GlobalSettingExtensionNamesAreIncludedForDefinedProperties() + { + var alphaValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( + (propertyName, globals, rule) => + { + return "alpha"; + }); + var alphaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("AlphaProperty"); + var alphaLazy = new Lazy( + () => alphaValueProvider, + alphaMetadata); + + var betaValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( + (propertyName, globals, rule) => + { + return ""; + }); + var betaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("BetaProperty"); + var betaLazy = new Lazy( + () => betaValueProvider, + betaMetadata); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + CreateDefaultTestLaunchSettings(), + EmptyLaunchProfileExtensionValueProviders, + ImmutableArray.Create(alphaLazy, betaLazy)); + + var names = await properties.GetPropertyNamesAsync(); + + Assert.Contains("AlphaProperty", names); + Assert.DoesNotContain("BetaProperty", names); + } - [Fact] - public async Task WhenSettingAGlobalProperty_TheRuleIsPassedToTheExtensionValueProvider() + [Fact] + public async Task WhenRetrievingPropertyNames_PropertiesInOtherSettingsAreIncluded() + { + var profile1 = new WritableLaunchProfile { - bool rulePassed = false; - var extensionValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( - onSetPropertyValue: (propertyName, value, globals, rule) => - { - rulePassed = rule is not null; - return ImmutableDictionary.Empty.Add(propertyName, value); - }); - var metadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("MyProperty"); - - var lazy = new Lazy( - () => extensionValueProvider, - metadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - ImmutableArray.Create(lazy)); - properties.SetRuleContext(new Rule()); - - await properties.SetPropertyValueAsync("MyProperty", "alpha"); - - Assert.True(rulePassed); - } + Name = "Profile1", + OtherSettings = { { "alpha", 1 } } + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var names = await properties.GetPropertyNamesAsync(); + + Assert.Contains("alpha", names); + } - [Fact] - public async Task WhenRetrievingPropertyNames_GlobalSettingExtensionNamesAreIncludedForDefinedProperties() + [Fact] + public async Task WhenRetrievingPropertyNames_PropertiesInGlobalSettingsAreNotIncluded() + { + var profile1 = new WritableLaunchProfile { - var alphaValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( - (propertyName, globals, rule) => - { - return "alpha"; - }); - var alphaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("AlphaProperty"); - var alphaLazy = new Lazy( - () => alphaValueProvider, - alphaMetadata); - - var betaValueProvider = IGlobalSettingExtensionValueProviderFactory.Create( - (propertyName, globals, rule) => - { - return ""; - }); - var betaMetadata = ILaunchProfileExtensionValueProviderMetadataFactory.Create("BetaProperty"); - var betaLazy = new Lazy( - () => betaValueProvider, - betaMetadata); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - CreateDefaultTestLaunchSettings(), - EmptyLaunchProfileExtensionValueProviders, - ImmutableArray.Create(alphaLazy, betaLazy)); - - var names = await properties.GetPropertyNamesAsync(); - - Assert.Contains("AlphaProperty", names); - Assert.DoesNotContain("BetaProperty", names); - } + Name = "Profile1", + OtherSettings = { { "alpha", 1 } } + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }, + globalSettings: ImmutableDictionary.Empty.Add("beta", "value")); + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + var names = await properties.GetPropertyNamesAsync(); + + Assert.DoesNotContain("beta", names); + } - [Fact] - public async Task WhenRetrievingPropertyNames_PropertiesInOtherSettingsAreIncluded() + [Fact] + public async Task WhenRetrievingValuesFromOtherSettings_ValuesArePropertyConvertedToStrings() + { + var profile1 = new WritableLaunchProfile { - var profile1 = new WritableLaunchProfile + Name = "Profile1", + OtherSettings = { - Name = "Profile1", - OtherSettings = { { "alpha", 1 } } - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var names = await properties.GetPropertyNamesAsync(); - - Assert.Contains("alpha", names); - } + { "anInteger", 1 }, + { "aBoolean", true }, + { "aString", "Hello, world" }, + { "anEnumStoredAsAsAString", "valueOne" }, + { "anotherString", "Hi, friends!" } + } + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile() }); - [Fact] - public async Task WhenRetrievingPropertyNames_PropertiesInGlobalSettingsAreNotIncluded() + var rule = new Rule { - var profile1 = new WritableLaunchProfile + Properties = { - Name = "Profile1", - OtherSettings = { { "alpha", 1 } } - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }, - globalSettings: ImmutableDictionary.Empty.Add("beta", "value")); - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - var names = await properties.GetPropertyNamesAsync(); - - Assert.DoesNotContain("beta", names); - } + new IntProperty { Name = "anInteger" }, + new BoolProperty { Name = "aBoolean" }, + new StringProperty { Name = "aString" }, + new EnumProperty { Name = "anEnumStoredAsAString" } + // anotherString intentionally not represented + } + }; + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + properties.SetRuleContext(rule); + + var anIntegerValue = await properties.GetEvaluatedPropertyValueAsync("anInteger"); + Assert.Equal(expected: "1", actual: anIntegerValue); + + var aBooleanValue = await properties.GetEvaluatedPropertyValueAsync("aBoolean"); + Assert.Equal(expected: "true", actual: aBooleanValue); - [Fact] - public async Task WhenRetrievingValuesFromOtherSettings_ValuesArePropertyConvertedToStrings() + var aStringValue = await properties.GetEvaluatedPropertyValueAsync("aString"); + Assert.Equal(expected: "Hello, world", actual: aStringValue); + + var anEnumStoredAsAsAStringValue = await properties.GetEvaluatedPropertyValueAsync("anEnumStoredAsAsAString"); + Assert.Equal(expected: "valueOne", actual: anEnumStoredAsAsAStringValue); + + var anotherStringValue = await properties.GetEvaluatedPropertyValueAsync("anotherString"); + Assert.Equal(expected: "Hi, friends!", actual: anotherStringValue); + } + + [Fact] + public async Task WhenSettingValuesNotHandledByExtenders_ValuesOfTheExpectedTypesAreStoredInOtherSettings() + { + var writableProfile = new WritableLaunchProfile { - var profile1 = new WritableLaunchProfile - { - Name = "Profile1", - OtherSettings = - { - { "anInteger", 1 }, - { "aBoolean", true }, - { "aString", "Hello, world" }, - { "anEnumStoredAsAsAString", "valueOne" }, - { "anotherString", "Hi, friends!" } - } - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile() }); - - var rule = new Rule + Name = "Profile1", + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { writableProfile.ToLaunchProfile() }, + tryUpdateProfileCallback: (profile, action) => { - Properties = - { - new IntProperty { Name = "anInteger" }, - new BoolProperty { Name = "aBoolean" }, - new StringProperty { Name = "aString" }, - new EnumProperty { Name = "anEnumStoredAsAString" } - // anotherString intentionally not represented - } - }; - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - properties.SetRuleContext(rule); - - var anIntegerValue = await properties.GetEvaluatedPropertyValueAsync("anInteger"); - Assert.Equal(expected: "1", actual: anIntegerValue); - - var aBooleanValue = await properties.GetEvaluatedPropertyValueAsync("aBoolean"); - Assert.Equal(expected: "true", actual: aBooleanValue); - - var aStringValue = await properties.GetEvaluatedPropertyValueAsync("aString"); - Assert.Equal(expected: "Hello, world", actual: aStringValue); - - var anEnumStoredAsAsAStringValue = await properties.GetEvaluatedPropertyValueAsync("anEnumStoredAsAsAString"); - Assert.Equal(expected: "valueOne", actual: anEnumStoredAsAsAStringValue); - - var anotherStringValue = await properties.GetEvaluatedPropertyValueAsync("anotherString"); - Assert.Equal(expected: "Hi, friends!", actual: anotherStringValue); - } + // Update writableProfile since we're hanging on to it rather than the profile given us by the mock. + action(writableProfile); + }); - [Fact] - public async Task WhenSettingValuesNotHandledByExtenders_ValuesOfTheExpectedTypesAreStoredInOtherSettings() + var rule = new Rule { - var writableProfile = new WritableLaunchProfile + Properties = { - Name = "Profile1", - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { writableProfile.ToLaunchProfile() }, - tryUpdateProfileCallback: (profile, action) => - { - // Update writableProfile since we're hanging on to it rather than the profile given us by the mock. - action(writableProfile); - }); - - var rule = new Rule - { - Properties = - { - new IntProperty { Name = "anInteger" }, - new BoolProperty { Name = "aBoolean" }, - new StringProperty { Name = "aString" }, - new EnumProperty { Name = "anEnumStoredAsAString" } - // anotherString intentionally not represented - } - }; - - var properties = new LaunchProfileProjectProperties( - DefaultTestProjectPath, - "Profile1", - launchSettingsProvider, - EmptyLaunchProfileExtensionValueProviders, - EmptyGlobalSettingExtensionValueProviders); - - properties.SetRuleContext(rule); - - await properties.SetPropertyValueAsync("anInteger", "2"); - await properties.SetPropertyValueAsync("aBoolean", "false"); - await properties.SetPropertyValueAsync("aString", "Hello, world!"); - await properties.SetPropertyValueAsync("anEnumStoredAsAString", "valueTwo"); - await properties.SetPropertyValueAsync("anotherString", "Hello, friends!"); - - Assert.Equal(expected: 2, actual: writableProfile.OtherSettings["anInteger"]); - Assert.Equal(expected: false, actual: writableProfile.OtherSettings["aBoolean"]); - Assert.Equal(expected: "Hello, world!", actual: writableProfile.OtherSettings["aString"]); - Assert.Equal(expected: "valueTwo", actual: writableProfile.OtherSettings["anEnumStoredAsAString"]); - Assert.Equal(expected: "Hello, friends!", actual: writableProfile.OtherSettings["anotherString"]); - } + new IntProperty { Name = "anInteger" }, + new BoolProperty { Name = "aBoolean" }, + new StringProperty { Name = "aString" }, + new EnumProperty { Name = "anEnumStoredAsAString" } + // anotherString intentionally not represented + } + }; + + var properties = new LaunchProfileProjectProperties( + DefaultTestProjectPath, + "Profile1", + launchSettingsProvider, + EmptyLaunchProfileExtensionValueProviders, + EmptyGlobalSettingExtensionValueProviders); + + properties.SetRuleContext(rule); + + await properties.SetPropertyValueAsync("anInteger", "2"); + await properties.SetPropertyValueAsync("aBoolean", "false"); + await properties.SetPropertyValueAsync("aString", "Hello, world!"); + await properties.SetPropertyValueAsync("anEnumStoredAsAString", "valueTwo"); + await properties.SetPropertyValueAsync("anotherString", "Hello, friends!"); + + Assert.Equal(expected: 2, actual: writableProfile.OtherSettings["anInteger"]); + Assert.Equal(expected: false, actual: writableProfile.OtherSettings["aBoolean"]); + Assert.Equal(expected: "Hello, world!", actual: writableProfile.OtherSettings["aString"]); + Assert.Equal(expected: "valueTwo", actual: writableProfile.OtherSettings["anEnumStoredAsAString"]); + Assert.Equal(expected: "Hello, friends!", actual: writableProfile.OtherSettings["anotherString"]); + } - /// - /// Creates an with two empty profiles named - /// "Profile1" and "Profile2". - /// - private static ILaunchSettingsProvider3 CreateDefaultTestLaunchSettings() - { - var profile1 = new WritableLaunchProfile { Name = "Profile1" }; - var profile2 = new WritableLaunchProfile { Name = "Profile2" }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); - return launchSettingsProvider; - } + /// + /// Creates an with two empty profiles named + /// "Profile1" and "Profile2". + /// + private static ILaunchSettingsProvider3 CreateDefaultTestLaunchSettings() + { + var profile1 = new WritableLaunchProfile { Name = "Profile1" }; + var profile2 = new WritableLaunchProfile { Name = "Profile2" }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: new[] { profile1.ToLaunchProfile(), profile2.ToLaunchProfile() }); + return launchSettingsProvider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProviderTests.cs index 7559028a39..73295070f1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/LaunchProfiles/ProjectLaunchProfileExtensionValueProviderTests.cs @@ -2,300 +2,299 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class ProjectLaunchProfileExtensionValueProviderTests { - public class ProjectLaunchProfileExtensionValueProviderTests - { - private static readonly ImmutableDictionary EmptyGlobalSettings = ImmutableDictionary.Empty; + private static readonly ImmutableDictionary EmptyGlobalSettings = ImmutableDictionary.Empty; - [Fact] - public void AuthenticationMode_OnGetPropertyValueAsync_GetsModeFromActiveProfile() + [Fact] + public void AuthenticationMode_OnGetPropertyValueAsync_GetsModeFromActiveProfile() + { + string activeProfileAuthenticationMode = "Windows"; + var profile = new WritableLaunchProfile { - string activeProfileAuthenticationMode = "Windows"; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.RemoteAuthenticationModeProperty, activeProfileAuthenticationMode } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.RemoteAuthenticationModeProperty, activeProfileAuthenticationMode } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.AuthenticationModePropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.AuthenticationModePropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: activeProfileAuthenticationMode, actual: actualValue); - } + Assert.Equal(expected: activeProfileAuthenticationMode, actual: actualValue); + } - [Fact] - public void AuthenticationMode_OnSetPropertyValueAsync_SetsModeInActiveProfile() + [Fact] + public void AuthenticationMode_OnSetPropertyValueAsync_SetsModeInActiveProfile() + { + string activeProfileAuthenticationMode = "Windows"; + var profile = new WritableLaunchProfile { - string activeProfileAuthenticationMode = "Windows"; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.RemoteAuthenticationModeProperty, activeProfileAuthenticationMode } - } - }; + { LaunchProfileExtensions.RemoteAuthenticationModeProperty, activeProfileAuthenticationMode } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.AuthenticationModePropertyName, "NotWindows", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.AuthenticationModePropertyName, "NotWindows", profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "NotWindows", actual: profile.OtherSettings[LaunchProfileExtensions.RemoteAuthenticationModeProperty]); - } + Assert.Equal(expected: "NotWindows", actual: profile.OtherSettings[LaunchProfileExtensions.RemoteAuthenticationModeProperty]); + } - [Fact] - public void NativeDebugging_OnGetPropertyValueAsync_GetsNativeDebuggingFromActiveProfile() + [Fact] + public void NativeDebugging_OnGetPropertyValueAsync_GetsNativeDebuggingFromActiveProfile() + { + bool activeProfileNativeDebugging = true; + var profile = new WritableLaunchProfile { - bool activeProfileNativeDebugging = true; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.NativeDebuggingProperty, activeProfileNativeDebugging } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.NativeDebuggingProperty, activeProfileNativeDebugging } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.NativeDebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.NativeDebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "true", actual: actualValue); - } + Assert.Equal(expected: "true", actual: actualValue); + } - [Fact] - public void NativeDebugging_OnSetPropertyValueAsync_SetsNativeDebuggingInActiveProfile() + [Fact] + public void NativeDebugging_OnSetPropertyValueAsync_SetsNativeDebuggingInActiveProfile() + { + bool activeProfileNativeDebugging = false; + var profile = new WritableLaunchProfile { - bool activeProfileNativeDebugging = false; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.NativeDebuggingProperty, activeProfileNativeDebugging } - } - }; + { LaunchProfileExtensions.NativeDebuggingProperty, activeProfileNativeDebugging } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.NativeDebuggingPropertyName, "true", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.NativeDebuggingPropertyName, "true", profile, EmptyGlobalSettings, rule: null); - Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.NativeDebuggingProperty]); - } + Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.NativeDebuggingProperty]); + } - [Fact] - public void RemoteDebugEnabled_OnGetPropertyValueAsync_GetsRemoteDebuggingFromActiveProfile() + [Fact] + public void RemoteDebugEnabled_OnGetPropertyValueAsync_GetsRemoteDebuggingFromActiveProfile() + { + bool activeProfileRemoteDebugEnabled = true; + var profile = new WritableLaunchProfile { - bool activeProfileRemoteDebugEnabled = true; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.RemoteDebugEnabledProperty, activeProfileRemoteDebugEnabled } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.RemoteDebugEnabledProperty, activeProfileRemoteDebugEnabled } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugEnabledPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugEnabledPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "true", actual: actualValue); - } + Assert.Equal(expected: "true", actual: actualValue); + } - [Fact] - public void RemoteDebugEnabled_OnSetPropertyValueAsync_SetsRemoteDebuggingInActiveProfile() + [Fact] + public void RemoteDebugEnabled_OnSetPropertyValueAsync_SetsRemoteDebuggingInActiveProfile() + { + bool activeProfileRemoteDebugEnabled = false; + var profile = new WritableLaunchProfile { - bool activeProfileRemoteDebugEnabled = false; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.RemoteDebugEnabledProperty, activeProfileRemoteDebugEnabled } - } - }; + { LaunchProfileExtensions.RemoteDebugEnabledProperty, activeProfileRemoteDebugEnabled } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugEnabledPropertyName, "true", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugEnabledPropertyName, "true", profile, EmptyGlobalSettings, rule: null); - Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.RemoteDebugEnabledProperty]); - } + Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.RemoteDebugEnabledProperty]); + } - [Fact] - public void RemoteMachineName_OnGetPropertyValueAsync_GetsNameFromActiveProfile() + [Fact] + public void RemoteMachineName_OnGetPropertyValueAsync_GetsNameFromActiveProfile() + { + string activeProfileRemoteMachineName = "alphaMachine"; + var profile = new WritableLaunchProfile { - string activeProfileRemoteMachineName = "alphaMachine"; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.RemoteDebugMachineProperty, activeProfileRemoteMachineName } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.RemoteDebugMachineProperty, activeProfileRemoteMachineName } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugMachinePropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugMachinePropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: activeProfileRemoteMachineName, actual: actualValue); - } + Assert.Equal(expected: activeProfileRemoteMachineName, actual: actualValue); + } - [Fact] - public void RemoteMachineName_OnSetPropertyValueAsync_SetsNameInActiveProfile() + [Fact] + public void RemoteMachineName_OnSetPropertyValueAsync_SetsNameInActiveProfile() + { + string activeProfileRemoteMachineName = "Tiger"; + var profile = new WritableLaunchProfile { - string activeProfileRemoteMachineName = "Tiger"; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.RemoteDebugMachineProperty, activeProfileRemoteMachineName } - } - }; + { LaunchProfileExtensions.RemoteDebugMachineProperty, activeProfileRemoteMachineName } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugMachinePropertyName, "Cheetah", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.RemoteDebugMachinePropertyName, "Cheetah", profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "Cheetah", actual: profile.OtherSettings[LaunchProfileExtensions.RemoteDebugMachineProperty]); - } + Assert.Equal(expected: "Cheetah", actual: profile.OtherSettings[LaunchProfileExtensions.RemoteDebugMachineProperty]); + } - [Fact] - public void SqlDebugEnabled_OnGetPropertyValueAsync_GetsSettingFromActiveProfile() + [Fact] + public void SqlDebugEnabled_OnGetPropertyValueAsync_GetsSettingFromActiveProfile() + { + bool activeProfileSqlDebugEnabled = true; + var profile = new WritableLaunchProfile { - bool activeProfileSqlDebugEnabled = true; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.SqlDebuggingProperty, activeProfileSqlDebugEnabled } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.SqlDebuggingProperty, activeProfileSqlDebugEnabled } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.SqlDebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.SqlDebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "true", actual: actualValue); - } + Assert.Equal(expected: "true", actual: actualValue); + } - [Fact] - public void SqlDebugEnabled_OnSetPropertyValueAsync_SetsSqlDebugInActiveProfile() + [Fact] + public void SqlDebugEnabled_OnSetPropertyValueAsync_SetsSqlDebugInActiveProfile() + { + bool activeProfileSqlDebugEnabled = false; + var profile = new WritableLaunchProfile { - bool activeProfileSqlDebugEnabled = false; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.SqlDebuggingProperty, activeProfileSqlDebugEnabled } - } - }; + { LaunchProfileExtensions.SqlDebuggingProperty, activeProfileSqlDebugEnabled } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.SqlDebuggingPropertyName, "true", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.SqlDebuggingPropertyName, "true", profile, EmptyGlobalSettings, rule: null); - Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.SqlDebuggingProperty]); - } + Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.SqlDebuggingProperty]); + } - [Fact] - public void HotReloadEnabled_OnGetPropertyValueAsync_GetsDefaultValueWhenNotDefined() - { - var profile = new WritableLaunchProfile().ToLaunchProfile(); + [Fact] + public void HotReloadEnabled_OnGetPropertyValueAsync_GetsDefaultValueWhenNotDefined() + { + var profile = new WritableLaunchProfile().ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.HotReloadEnabledPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.HotReloadEnabledPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "true", actual: actualValue); - } + Assert.Equal(expected: "true", actual: actualValue); + } - [Fact] - public void HotReloadEnabled_OnGetPropertyValueAsync_GetsValueInProfileWhenDefined() + [Fact] + public void HotReloadEnabled_OnGetPropertyValueAsync_GetsValueInProfileWhenDefined() + { + bool hotReloadEnabled = false; + var profile = new WritableLaunchProfile { - bool hotReloadEnabled = false; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.HotReloadEnabledProperty, hotReloadEnabled } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.HotReloadEnabledProperty, hotReloadEnabled } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.HotReloadEnabledPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.HotReloadEnabledPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "false", actual: actualValue); - } + Assert.Equal(expected: "false", actual: actualValue); + } - [Fact] - public void HotReloadEnabled_OnSetPropertyValueAsync_SetsHotReloadToSpecifiedValue() + [Fact] + public void HotReloadEnabled_OnSetPropertyValueAsync_SetsHotReloadToSpecifiedValue() + { + bool hotReloadEnabled = true; + var profile = new WritableLaunchProfile { - bool hotReloadEnabled = true; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.HotReloadEnabledProperty, hotReloadEnabled } - } - }; + { LaunchProfileExtensions.HotReloadEnabledProperty, hotReloadEnabled } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.HotReloadEnabledPropertyName, "false", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.HotReloadEnabledPropertyName, "false", profile, EmptyGlobalSettings, rule: null); - Assert.False((bool)profile.OtherSettings[LaunchProfileExtensions.HotReloadEnabledProperty]); - } + Assert.False((bool)profile.OtherSettings[LaunchProfileExtensions.HotReloadEnabledProperty]); + } - [Fact] - public void WebView2Debugging_OnGetPropertyValueAsync_GetsDefaultValueWhenNotDefined() - { - var profile = new WritableLaunchProfile().ToLaunchProfile(); + [Fact] + public void WebView2Debugging_OnGetPropertyValueAsync_GetsDefaultValueWhenNotDefined() + { + var profile = new WritableLaunchProfile().ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.WebView2DebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.WebView2DebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "false", actual: actualValue); - } + Assert.Equal(expected: "false", actual: actualValue); + } - [Fact] - public void WebView2Debugging_OnGetPropertyValueAsync_GetsValueInProfileWhenDefined() + [Fact] + public void WebView2Debugging_OnGetPropertyValueAsync_GetsValueInProfileWhenDefined() + { + bool webView2Debugging = true; + var profile = new WritableLaunchProfile { - bool webView2Debugging = true; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.JSWebView2DebuggingProperty, webView2Debugging } - } - }.ToLaunchProfile(); + { LaunchProfileExtensions.JSWebView2DebuggingProperty, webView2Debugging } + } + }.ToLaunchProfile(); - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.WebView2DebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); + var actualValue = provider.OnGetPropertyValue(ProjectLaunchProfileExtensionValueProvider.WebView2DebuggingPropertyName, profile, EmptyGlobalSettings, rule: null); - Assert.Equal(expected: "true", actual: actualValue); - } + Assert.Equal(expected: "true", actual: actualValue); + } - [Fact] - public void WebView2Debugging_OnSetPropertyValueAsync_SetsWebView2DebuggingToSpecifiedValue() + [Fact] + public void WebView2Debugging_OnSetPropertyValueAsync_SetsWebView2DebuggingToSpecifiedValue() + { + bool webView2Debugging = false; + var profile = new WritableLaunchProfile { - bool webView2Debugging = false; - var profile = new WritableLaunchProfile + OtherSettings = { - OtherSettings = - { - { LaunchProfileExtensions.JSWebView2DebuggingProperty, webView2Debugging } - } - }; + { LaunchProfileExtensions.JSWebView2DebuggingProperty, webView2Debugging } + } + }; - var provider = new ProjectLaunchProfileExtensionValueProvider(); + var provider = new ProjectLaunchProfileExtensionValueProvider(); - provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.WebView2DebuggingPropertyName, "true", profile, EmptyGlobalSettings, rule: null); + provider.OnSetPropertyValue(ProjectLaunchProfileExtensionValueProvider.WebView2DebuggingPropertyName, "true", profile, EmptyGlobalSettings, rule: null); - Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.JSWebView2DebuggingProperty]); - } + Assert.True((bool)profile.OtherSettings[LaunchProfileExtensions.JSWebView2DebuggingProperty]); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/MetadataExtensionsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/MetadataExtensionsTests.cs index f5666c689b..a204034f15 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/MetadataExtensionsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/MetadataExtensionsTests.cs @@ -1,145 +1,144 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public sealed class MetadataExtensionsTests { - public sealed class MetadataExtensionsTests + [Fact] + public void GetStringProperty_WhenNotFound() + { + var dic = ImmutableDictionary.Empty; + Assert.Null(dic.GetStringProperty("key")); + } + + [Fact] + public void GetStringProperty_WhenFound() { - [Fact] - public void GetStringProperty_WhenNotFound() - { - var dic = ImmutableDictionary.Empty; - Assert.Null(dic.GetStringProperty("key")); - } - - [Fact] - public void GetStringProperty_WhenFound() - { - var dic = ImmutableDictionary.Empty.Add("key", "value"); - Assert.Equal("value", dic.GetStringProperty("key")); - } - - [Fact] - public void GetStringProperty_WhenEmpty() - { - var dic = ImmutableDictionary.Empty.Add("key", ""); - Assert.Null(dic.GetStringProperty("key")); - } - - [Fact] - public void GetStringProperty_WhenWhiteSpace() - { - var dic = ImmutableDictionary.Empty.Add("key", " "); - Assert.Equal(" ", dic.GetStringProperty("key")); - } - - [Fact] - public void TryGetStringProperty_WhenNotFound() - { - var dic = ImmutableDictionary.Empty; - Assert.False(dic.TryGetStringProperty("key", out string? value)); - Assert.Null(value); - } - - [Fact] - public void TryGetStringProperty_WhenFound() - { - var dic = ImmutableDictionary.Empty.Add("key", "value"); - Assert.True(dic.TryGetStringProperty("key", out string? value)); - Assert.Equal("value", value); - } - - [Fact] - public void GetBoolProperty_WhenNotFound() - { - var dic = ImmutableDictionary.Empty; - Assert.Null(dic.GetBoolProperty("key")); - } - - [Theory] - [InlineData("True", true)] - [InlineData("true", true)] - [InlineData("TRUE", true)] - [InlineData("False", false)] - [InlineData("false", false)] - [InlineData("FALSE", false)] - [InlineData("banana", null)] - [InlineData("", null)] - [InlineData(" ", null)] - public void GetBoolProperty_WhenFound(string actual, bool? expected) - { - var dic = ImmutableDictionary.Empty.Add("key", actual); - Assert.Equal(expected, dic.GetBoolProperty("key")); - } - - [Fact] - public void TryGetBoolProperty_WhenNotFound() - { - var dic = ImmutableDictionary.Empty; - Assert.False(dic.TryGetBoolProperty("key", out bool value)); - Assert.False(value); - } - - [Theory] - [InlineData("True", true, true)] - [InlineData("true", true, true)] - [InlineData("TRUE", true, true)] - [InlineData("False", true, false)] - [InlineData("false", true, false)] - [InlineData("FALSE", true, false)] - [InlineData("banana", false, false)] - [InlineData("", false, false)] - [InlineData(" ", false, false)] - public void TryGetBoolProperty_WhenFound(string actual, bool returnValue, bool outValue) - { - var dic = ImmutableDictionary.Empty.Add("key", actual); - Assert.Equal(returnValue, dic.TryGetBoolProperty("key", out bool value)); - Assert.Equal(outValue, value); - } - - [Fact] - public void GetEnumProperty_WhenNotFound() - { - var dic = ImmutableDictionary.Empty; - Assert.Null(dic.GetEnumProperty("key")); - } - - [Theory] - [InlineData("Member1", TestEnum.Member1)] - [InlineData("Member2", TestEnum.Member2)] - [InlineData("Member3", TestEnum.Member3)] - [InlineData("MEMBER1", TestEnum.Member1)] - [InlineData("member1", TestEnum.Member1)] - [InlineData("", null)] - [InlineData(" ", null)] - public void GetEnumProperty_WhenFound(string actual, TestEnum? expected) - { - var dic = ImmutableDictionary.Empty.Add("key", actual); - Assert.Equal(expected, dic.GetEnumProperty("key")); - } - - [Fact] - public void TryGetEnumProperty_WhenNotFound() - { - var dic = ImmutableDictionary.Empty; - Assert.False(dic.TryGetEnumProperty("key", out TestEnum value)); - Assert.Equal(default, value); - } - - [Theory] - [InlineData("Member1", true, TestEnum.Member1)] - [InlineData("Member2", true, TestEnum.Member2)] - [InlineData("Member3", true, TestEnum.Member3)] - [InlineData("MEMBER1", true, TestEnum.Member1)] - [InlineData("member1", true, TestEnum.Member1)] - [InlineData("", false, default(TestEnum))] - [InlineData(" ", false, default(TestEnum))] - public void TryGetEnumProperty_WhenFound(string actual, bool returnValue, TestEnum outValue) - { - var dic = ImmutableDictionary.Empty.Add("key", actual); - Assert.Equal(returnValue, dic.TryGetEnumProperty("key", out TestEnum value)); - Assert.Equal(outValue, value); - } - - public enum TestEnum { Member1, Member2, Member3 } + var dic = ImmutableDictionary.Empty.Add("key", "value"); + Assert.Equal("value", dic.GetStringProperty("key")); } + + [Fact] + public void GetStringProperty_WhenEmpty() + { + var dic = ImmutableDictionary.Empty.Add("key", ""); + Assert.Null(dic.GetStringProperty("key")); + } + + [Fact] + public void GetStringProperty_WhenWhiteSpace() + { + var dic = ImmutableDictionary.Empty.Add("key", " "); + Assert.Equal(" ", dic.GetStringProperty("key")); + } + + [Fact] + public void TryGetStringProperty_WhenNotFound() + { + var dic = ImmutableDictionary.Empty; + Assert.False(dic.TryGetStringProperty("key", out string? value)); + Assert.Null(value); + } + + [Fact] + public void TryGetStringProperty_WhenFound() + { + var dic = ImmutableDictionary.Empty.Add("key", "value"); + Assert.True(dic.TryGetStringProperty("key", out string? value)); + Assert.Equal("value", value); + } + + [Fact] + public void GetBoolProperty_WhenNotFound() + { + var dic = ImmutableDictionary.Empty; + Assert.Null(dic.GetBoolProperty("key")); + } + + [Theory] + [InlineData("True", true)] + [InlineData("true", true)] + [InlineData("TRUE", true)] + [InlineData("False", false)] + [InlineData("false", false)] + [InlineData("FALSE", false)] + [InlineData("banana", null)] + [InlineData("", null)] + [InlineData(" ", null)] + public void GetBoolProperty_WhenFound(string actual, bool? expected) + { + var dic = ImmutableDictionary.Empty.Add("key", actual); + Assert.Equal(expected, dic.GetBoolProperty("key")); + } + + [Fact] + public void TryGetBoolProperty_WhenNotFound() + { + var dic = ImmutableDictionary.Empty; + Assert.False(dic.TryGetBoolProperty("key", out bool value)); + Assert.False(value); + } + + [Theory] + [InlineData("True", true, true)] + [InlineData("true", true, true)] + [InlineData("TRUE", true, true)] + [InlineData("False", true, false)] + [InlineData("false", true, false)] + [InlineData("FALSE", true, false)] + [InlineData("banana", false, false)] + [InlineData("", false, false)] + [InlineData(" ", false, false)] + public void TryGetBoolProperty_WhenFound(string actual, bool returnValue, bool outValue) + { + var dic = ImmutableDictionary.Empty.Add("key", actual); + Assert.Equal(returnValue, dic.TryGetBoolProperty("key", out bool value)); + Assert.Equal(outValue, value); + } + + [Fact] + public void GetEnumProperty_WhenNotFound() + { + var dic = ImmutableDictionary.Empty; + Assert.Null(dic.GetEnumProperty("key")); + } + + [Theory] + [InlineData("Member1", TestEnum.Member1)] + [InlineData("Member2", TestEnum.Member2)] + [InlineData("Member3", TestEnum.Member3)] + [InlineData("MEMBER1", TestEnum.Member1)] + [InlineData("member1", TestEnum.Member1)] + [InlineData("", null)] + [InlineData(" ", null)] + public void GetEnumProperty_WhenFound(string actual, TestEnum? expected) + { + var dic = ImmutableDictionary.Empty.Add("key", actual); + Assert.Equal(expected, dic.GetEnumProperty("key")); + } + + [Fact] + public void TryGetEnumProperty_WhenNotFound() + { + var dic = ImmutableDictionary.Empty; + Assert.False(dic.TryGetEnumProperty("key", out TestEnum value)); + Assert.Equal(default, value); + } + + [Theory] + [InlineData("Member1", true, TestEnum.Member1)] + [InlineData("Member2", true, TestEnum.Member2)] + [InlineData("Member3", true, TestEnum.Member3)] + [InlineData("MEMBER1", true, TestEnum.Member1)] + [InlineData("member1", true, TestEnum.Member1)] + [InlineData("", false, default(TestEnum))] + [InlineData(" ", false, default(TestEnum))] + public void TryGetEnumProperty_WhenFound(string actual, bool returnValue, TestEnum outValue) + { + var dic = ImmutableDictionary.Empty.Add("key", actual); + Assert.Equal(returnValue, dic.TryGetEnumProperty("key", out TestEnum value)); + Assert.Equal(outValue, value); + } + + public enum TestEnum { Member1, Member2, Member3 } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/NeutralLanguageEnumProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/NeutralLanguageEnumProviderTests.cs index 12fc7d9e6d..2bcaba4aa7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/NeutralLanguageEnumProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/NeutralLanguageEnumProviderTests.cs @@ -2,37 +2,36 @@ using Microsoft.VisualStudio.ProjectSystem.Properties.Package; -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class NeutralLanguageEnumProviderTests { - public class NeutralLanguageEnumProviderTests + [Fact] + public async Task GetProviderAsync_ReturnsNonNullGenerator() + { + var provider = new NeutralLanguageEnumProvider(); + var generator = await provider.GetProviderAsync(options: null); + + Assert.NotNull(generator); + } + + [Fact] + public async Task GetListedValuesAsync_ReturnsSpecialNoneValueAsFirstItem() { - [Fact] - public async Task GetProviderAsync_ReturnsNonNullGenerator() - { - var provider = new NeutralLanguageEnumProvider(); - var generator = await provider.GetProviderAsync(options: null); - - Assert.NotNull(generator); - } - - [Fact] - public async Task GetListedValuesAsync_ReturnsSpecialNoneValueAsFirstItem() - { - var provider = new NeutralLanguageEnumProvider(); - var generator = await provider.GetProviderAsync(options: null); - var values = await generator.GetListedValuesAsync(); - var firstValue = values.First(); - - Assert.Equal(expected: NeutralLanguageValueProvider.NoneValue, actual: firstValue.Name); - } - - [Fact] - public async Task TryCreateEnumValueAsync_ReturnsNull() - { - var provider = new NeutralLanguageEnumProvider(); - var generator = await provider.GetProviderAsync(options: null); - - Assert.Null(await generator.TryCreateEnumValueAsync("abc-abc")); - } + var provider = new NeutralLanguageEnumProvider(); + var generator = await provider.GetProviderAsync(options: null); + var values = await generator.GetListedValuesAsync(); + var firstValue = values.First(); + + Assert.Equal(expected: NeutralLanguageValueProvider.NoneValue, actual: firstValue.Name); + } + + [Fact] + public async Task TryCreateEnumValueAsync_ReturnsNull() + { + var provider = new NeutralLanguageEnumProvider(); + var generator = await provider.GetProviderAsync(options: null); + + Assert.Null(await generator.TryCreateEnumValueAsync("abc-abc")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/ProjectPropertiesExtensionTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/ProjectPropertiesExtensionTests.cs index 28c6284506..ad05e549b6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/ProjectPropertiesExtensionTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Properties/ProjectPropertiesExtensionTests.cs @@ -1,100 +1,99 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +public class ProjectPropertiesExtensionTests { - public class ProjectPropertiesExtensionTests + [Fact] + public async Task WhenThePropertyIsSetInTheProject_TheValueIsSavedInTemporaryStorage() { - [Fact] - public async Task WhenThePropertyIsSetInTheProject_TheValueIsSavedInTemporaryStorage() - { - IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( - propertyNameAndValues: new Dictionary() - { - { "MyProperty", "Alpha" } - }, - inheritedPropertyNames: new()); + IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( + propertyNameAndValues: new Dictionary() + { + { "MyProperty", "Alpha" } + }, + inheritedPropertyNames: new()); - Dictionary storedValues = new(); - ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create(values: storedValues); + Dictionary storedValues = new(); + ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create(values: storedValues); - await projectProperties.SaveValueIfCurrentlySetAsync("MyProperty", temporaryPropertyStorage); + await projectProperties.SaveValueIfCurrentlySetAsync("MyProperty", temporaryPropertyStorage); - Assert.Contains(new KeyValuePair("MyProperty", "Alpha"), storedValues); - } + Assert.Contains(new KeyValuePair("MyProperty", "Alpha"), storedValues); + } - [Fact] - public async Task WhenThePropertyIsInherited_TheValueIsNotSavedInTemporaryStorage() - { - IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( - propertyNameAndValues: new Dictionary() - { - { "MyProperty", "Alpha" } - }, - inheritedPropertyNames: new() { "MyProperty" }); + [Fact] + public async Task WhenThePropertyIsInherited_TheValueIsNotSavedInTemporaryStorage() + { + IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( + propertyNameAndValues: new Dictionary() + { + { "MyProperty", "Alpha" } + }, + inheritedPropertyNames: new() { "MyProperty" }); - Dictionary storedValues = new(); - ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create(values: storedValues); + Dictionary storedValues = new(); + ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create(values: storedValues); - await projectProperties.SaveValueIfCurrentlySetAsync("MyProperty", temporaryPropertyStorage); + await projectProperties.SaveValueIfCurrentlySetAsync("MyProperty", temporaryPropertyStorage); - Assert.DoesNotContain(new KeyValuePair("MyProperty", "Alpha"), storedValues); - } + Assert.DoesNotContain(new KeyValuePair("MyProperty", "Alpha"), storedValues); + } - [Fact] - public async Task WhenThePropertyIsSetInTheProject_TheSavedValueIsNotRestored() + [Fact] + public async Task WhenThePropertyIsSetInTheProject_TheSavedValueIsNotRestored() + { + Dictionary propertyNamesAndValues = new() { - Dictionary propertyNamesAndValues = new() - { - { "MyProperty", "Beta" } - }; - IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( - propertyNamesAndValues, - inheritedPropertyNames: new()); + { "MyProperty", "Beta" } + }; + IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( + propertyNamesAndValues, + inheritedPropertyNames: new()); - ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create( - values: new() { { "MyProperty", "Alpha" } }); + ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create( + values: new() { { "MyProperty", "Alpha" } }); - await projectProperties.RestoreValueIfNotCurrentlySetAsync("MyProperty", temporaryPropertyStorage); + await projectProperties.RestoreValueIfNotCurrentlySetAsync("MyProperty", temporaryPropertyStorage); - Assert.Contains(new KeyValuePair("MyProperty", "Beta"), propertyNamesAndValues); - } + Assert.Contains(new KeyValuePair("MyProperty", "Beta"), propertyNamesAndValues); + } - [Fact] - public async Task WhenThePropertyIsInherited_TheSavedValueIsRestored() + [Fact] + public async Task WhenThePropertyIsInherited_TheSavedValueIsRestored() + { + Dictionary propertyNamesAndValues = new() { - Dictionary propertyNamesAndValues = new() - { - { "MyProperty", "Beta" } - }; - IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( - propertyNamesAndValues, - inheritedPropertyNames: new() { "MyProperty" }); + { "MyProperty", "Beta" } + }; + IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( + propertyNamesAndValues, + inheritedPropertyNames: new() { "MyProperty" }); - ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create( - values: new() { { "MyProperty", "Alpha" } }); + ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create( + values: new() { { "MyProperty", "Alpha" } }); - await projectProperties.RestoreValueIfNotCurrentlySetAsync("MyProperty", temporaryPropertyStorage); + await projectProperties.RestoreValueIfNotCurrentlySetAsync("MyProperty", temporaryPropertyStorage); - Assert.Contains(new KeyValuePair("MyProperty", "Alpha"), propertyNamesAndValues); - } + Assert.Contains(new KeyValuePair("MyProperty", "Alpha"), propertyNamesAndValues); + } - [Fact] - public async Task WhenThePropertyIsNotSetAtAll_TheSavedValueIsRestored() + [Fact] + public async Task WhenThePropertyIsNotSetAtAll_TheSavedValueIsRestored() + { + Dictionary propertyNamesAndValues = new() { - Dictionary propertyNamesAndValues = new() - { - { "MyProperty", null } - }; - IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( - propertyNamesAndValues, - inheritedPropertyNames: new() { "MyProperty" }); // If a property isn't defined _at all_ it is considered inherited. + { "MyProperty", null } + }; + IProjectProperties projectProperties = IProjectPropertiesFactory.CreateWithPropertiesAndValues( + propertyNamesAndValues, + inheritedPropertyNames: new() { "MyProperty" }); // If a property isn't defined _at all_ it is considered inherited. - ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create( - values: new() { { "MyProperty", "Alpha" } }); + ITemporaryPropertyStorage temporaryPropertyStorage = ITemporaryPropertyStorageFactory.Create( + values: new() { { "MyProperty", "Alpha" } }); - await projectProperties.RestoreValueIfNotCurrentlySetAsync("MyProperty", temporaryPropertyStorage); + await projectProperties.RestoreValueIfNotCurrentlySetAsync("MyProperty", temporaryPropertyStorage); - Assert.Contains(new KeyValuePair("MyProperty", "Alpha"), propertyNamesAndValues); - } + Assert.Contains(new KeyValuePair("MyProperty", "Alpha"), propertyNamesAndValues); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/References/AlwaysAllowValidProjectReferenceCheckerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/References/AlwaysAllowValidProjectReferenceCheckerTests.cs index e56e448079..788cfe9fbb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/References/AlwaysAllowValidProjectReferenceCheckerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/References/AlwaysAllowValidProjectReferenceCheckerTests.cs @@ -1,138 +1,137 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.References +namespace Microsoft.VisualStudio.ProjectSystem.References; + +public class AlwaysAllowValidProjectReferenceCheckerTests { - public class AlwaysAllowValidProjectReferenceCheckerTests + [Fact] + public void CanAddProjectReferenceAsync_NullAsReferencedProject_ThrowsArgumentNull() { - [Fact] - public void CanAddProjectReferenceAsync_NullAsReferencedProject_ThrowsArgumentNull() - { - var checker = CreateInstance(); - - Assert.ThrowsAsync("referencedProject", () => - { - return checker.CanAddProjectReferenceAsync(null!); - }); - } + var checker = CreateInstance(); - [Fact] - public void CanAddProjectReferencesAsync_NullAsReferencedProjects_ThrowsArgumentNull() + Assert.ThrowsAsync("referencedProject", () => { - var checker = CreateInstance(); + return checker.CanAddProjectReferenceAsync(null!); + }); + } - Assert.ThrowsAsync("referencedProjects", () => - { - return checker.CanAddProjectReferencesAsync(null!); - }); - } + [Fact] + public void CanAddProjectReferencesAsync_NullAsReferencedProjects_ThrowsArgumentNull() + { + var checker = CreateInstance(); - [Fact] - public void CanAddProjectReferencesAsync_EmptyAsReferencedProjects_ThrowsArgument() + Assert.ThrowsAsync("referencedProjects", () => { - var checker = CreateInstance(); + return checker.CanAddProjectReferencesAsync(null!); + }); + } - Assert.ThrowsAsync("referencedProjects", () => - { - return checker.CanAddProjectReferencesAsync(ImmutableHashSet.Empty); - }); - } + [Fact] + public void CanAddProjectReferencesAsync_EmptyAsReferencedProjects_ThrowsArgument() + { + var checker = CreateInstance(); - [Fact] - public void CanBeReferencedAsync_NullAsReferencingProject_ThrowsArgumentNull() + Assert.ThrowsAsync("referencedProjects", () => { - var checker = CreateInstance(); + return checker.CanAddProjectReferencesAsync(ImmutableHashSet.Empty); + }); + } - Assert.ThrowsAsync("referencingProject", () => - { - return checker.CanBeReferencedAsync(null!); - }); - } + [Fact] + public void CanBeReferencedAsync_NullAsReferencingProject_ThrowsArgumentNull() + { + var checker = CreateInstance(); - [Fact] - public async Task CanAddProjectReferenceAsync_ReturnsSupported() + Assert.ThrowsAsync("referencingProject", () => { - var project = new object(); - var checker = CreateInstance(); + return checker.CanBeReferencedAsync(null!); + }); + } - var result = await checker.CanAddProjectReferenceAsync(project); + [Fact] + public async Task CanAddProjectReferenceAsync_ReturnsSupported() + { + var project = new object(); + var checker = CreateInstance(); - Assert.Equal(SupportedCheckResult.Supported, result); - } + var result = await checker.CanAddProjectReferenceAsync(project); - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - [InlineData(5)] - [InlineData(10)] - public async Task CanAddProjectReferencesAsync_ReturnsErrorMessageSetToNull(int count) - { - var checker = CreateInstance(); - var referencedProjects = CreateSet(count); + Assert.Equal(SupportedCheckResult.Supported, result); + } - var result = await checker.CanAddProjectReferencesAsync(referencedProjects); + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(5)] + [InlineData(10)] + public async Task CanAddProjectReferencesAsync_ReturnsErrorMessageSetToNull(int count) + { + var checker = CreateInstance(); + var referencedProjects = CreateSet(count); - Assert.Null(result.ErrorMessage); - } + var result = await checker.CanAddProjectReferencesAsync(referencedProjects); - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - [InlineData(5)] - [InlineData(10)] - public async Task CanAddProjectReferencesAsync_ReturnsAsManyIndividualResultsAsProjects(int count) - { - var checker = CreateInstance(); - var referencedProjects = CreateSet(count); - - var result = await checker.CanAddProjectReferencesAsync(referencedProjects); + Assert.Null(result.ErrorMessage); + } - Assert.Equal(result.IndividualResults.Keys, referencedProjects); - } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(5)] + [InlineData(10)] + public async Task CanAddProjectReferencesAsync_ReturnsAsManyIndividualResultsAsProjects(int count) + { + var checker = CreateInstance(); + var referencedProjects = CreateSet(count); - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - [InlineData(5)] - [InlineData(10)] - public async Task CanAddProjectReferencesAsync_ReturnsAllResultsSetToSupported(int count) - { - var checker = CreateInstance(); - var referencedProjects = CreateSet(count); + var result = await checker.CanAddProjectReferencesAsync(referencedProjects); - var result = await checker.CanAddProjectReferencesAsync(referencedProjects); + Assert.Equal(result.IndividualResults.Keys, referencedProjects); + } - Assert.All(result.IndividualResults.Values, r => Assert.Equal(SupportedCheckResult.Supported, r)); - } + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(5)] + [InlineData(10)] + public async Task CanAddProjectReferencesAsync_ReturnsAllResultsSetToSupported(int count) + { + var checker = CreateInstance(); + var referencedProjects = CreateSet(count); - [Fact] - public async Task CanBeReferencedAsync_ReturnsSupported() - { - var project = new object(); - var checker = CreateInstance(); + var result = await checker.CanAddProjectReferencesAsync(referencedProjects); - var result = await checker.CanBeReferencedAsync(project); + Assert.All(result.IndividualResults.Values, r => Assert.Equal(SupportedCheckResult.Supported, r)); + } - Assert.Equal(SupportedCheckResult.Supported, result); - } + [Fact] + public async Task CanBeReferencedAsync_ReturnsSupported() + { + var project = new object(); + var checker = CreateInstance(); - private static IImmutableSet CreateSet(int count) - { - var builder = ImmutableHashSet.CreateBuilder(); + var result = await checker.CanBeReferencedAsync(project); - for (int i = 0; i < count; i++) - { - builder.Add(new object()); - } + Assert.Equal(SupportedCheckResult.Supported, result); + } - return builder.ToImmutableHashSet(); - } + private static IImmutableSet CreateSet(int count) + { + var builder = ImmutableHashSet.CreateBuilder(); - private static AlwaysAllowValidProjectReferenceChecker CreateInstance() + for (int i = 0; i < count; i++) { - return new AlwaysAllowValidProjectReferenceChecker(); + builder.Add(new object()); } + + return builder.ToImmutableHashSet(); + } + + private static AlwaysAllowValidProjectReferenceChecker CreateInstance() + { + return new AlwaysAllowValidProjectReferenceChecker(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProviderTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProviderTestBase.cs index 76eb708af3..0f765dc410 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProviderTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractAppXamlSpecialFileProviderTestBase.cs @@ -1,63 +1,62 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public abstract class AbstractAppXamlSpecialFileProviderTestBase : AbstractFindByNameSpecialFileProviderTestBase { - public abstract class AbstractAppXamlSpecialFileProviderTestBase : AbstractFindByNameSpecialFileProviderTestBase + protected AbstractAppXamlSpecialFileProviderTestBase(string fileName) + : base(fileName) { - protected AbstractAppXamlSpecialFileProviderTestBase(string fileName) - : base(fileName) - { - } + } - [Theory] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Application.xaml" - """, - @"C:\Project\Application.xaml")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - App.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\App.xaml" - """, - @"C:\Project\App.xaml")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - ThisCanBeAnyName.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\ThisCanBeAnyName.xaml" - """, - @"C:\Project\ThisCanBeAnyName.xaml")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Application.xaml" - App.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\App.xaml" - """, - @"C:\Project\Application.xaml")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties - Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Properties\Application.xaml" - """, - @"C:\Project\Properties\Application.xaml")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties - App.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Properties\App.xaml" - Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Application.xaml" - """, - @"C:\Project\Application.xaml")] // Breadthfirst - public async Task GetFileAsync_WhenItemMarkedWithApplicationManifest_ReturnsPath(string input, string expected) - { - var tree = ProjectTreeParser.Parse(input); - var provider = CreateInstance(tree); + [Theory] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Application.xaml" + """, + @"C:\Project\Application.xaml")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + App.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\App.xaml" + """, + @"C:\Project\App.xaml")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + ThisCanBeAnyName.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\ThisCanBeAnyName.xaml" + """, + @"C:\Project\ThisCanBeAnyName.xaml")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Application.xaml" + App.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\App.xaml" + """, + @"C:\Project\Application.xaml")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties + Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Properties\Application.xaml" + """, + @"C:\Project\Properties\Application.xaml")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties + App.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Properties\App.xaml" + Application.xaml, ItemType: ApplicationDefinition, FilePath: "C:\Project\Application.xaml" + """, + @"C:\Project\Application.xaml")] // Breadthfirst + public async Task GetFileAsync_WhenItemMarkedWithApplicationManifest_ReturnsPath(string input, string expected) + { + var tree = ProjectTreeParser.Parse(input); + var provider = CreateInstance(tree); - var result = await provider.GetFileAsync(SpecialFiles.AppXaml, SpecialFileFlags.FullPath); + var result = await provider.GetFileAsync(SpecialFiles.AppXaml, SpecialFileFlags.FullPath); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProviderTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProviderTestBase.cs index 19d0e08221..6cdb04fa92 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProviderTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameSpecialFileProviderTestBase.cs @@ -2,173 +2,172 @@ using Moq.Protected; -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public abstract class AbstractFindByNameSpecialFileProviderTestBase { - public abstract class AbstractFindByNameSpecialFileProviderTestBase + private readonly string _fileName; + + protected AbstractFindByNameSpecialFileProviderTestBase(string fileName) { - private readonly string _fileName; + _fileName = fileName; + } - protected AbstractFindByNameSpecialFileProviderTestBase(string fileName) - { - _fileName = fileName; - } + [Fact] + public async Task GetFileAsync_ReturnsPathUnderProjectRoot() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + """); + var provider = CreateInstance(tree); - [Fact] - public async Task GetFileAsync_ReturnsPathUnderProjectRoot() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - """); - var provider = CreateInstance(tree); + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + Assert.Equal($@"C:\Project\{_fileName}", result); + } - Assert.Equal($@"C:\Project\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenRootWithFile_ReturnsPath() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" + """); + var provider = CreateInstance(tree); - [Fact] - public async Task GetFileAsync_WhenRootWithFile_ReturnsPath() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" - """); - var provider = CreateInstance(tree); + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + Assert.Equal($@"C:\Project\{_fileName}", result); + } - Assert.Equal($@"C:\Project\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenTreeWithFolderSameName_ReturnsPath() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" + """); - [Fact] - public async Task GetFileAsync_WhenTreeWithFolderSameName_ReturnsPath() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" - """); + var provider = CreateInstance(tree); - var provider = CreateInstance(tree); + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + Assert.Equal($@"C:\Project\{_fileName}", result); + } - Assert.Equal($@"C:\Project\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenTreeWithFolderSameName_ThrowsIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" + """); - [Fact] - public async Task GetFileAsync_WhenTreeWithFolderSameName_ThrowsIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" - """); - - var provider = CreateInstance(tree); - - await Assert.ThrowsAsync(() => - { - return provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - }); - } - - [Fact] - public async Task GetFileAsync_WhenTreeWithExcludedFile_IsAddedToProjectIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk IncludeInProjectCandidate}), FilePath: "C:\Project\{{_fileName}}" - """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementAddFileAsync(path => callCount++); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(physicalProjectTree); - - await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - - Assert.Equal(1, callCount); - } - - [Fact] - public async Task GetFileAsync_WhenTreeWithExistentFile_ReturnsPathIfCreateIfNotExist() + var provider = CreateInstance(tree); + + await Assert.ThrowsAsync(() => { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" - """); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); - var provider = CreateInstance(physicalProjectTree); + return provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + }); + } - string? result = await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + [Fact] + public async Task GetFileAsync_WhenTreeWithExcludedFile_IsAddedToProjectIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk IncludeInProjectCandidate}), FilePath: "C:\Project\{{_fileName}}" + """); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementAddFileAsync(path => callCount++); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + var provider = CreateInstance(physicalProjectTree); + + await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + + Assert.Equal(1, callCount); + } - Assert.Equal($@"C:\Project\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenTreeWithExistentFile_ReturnsPathIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" + """); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); + var provider = CreateInstance(physicalProjectTree); - [Fact] - public async Task GetFileAsync_WhenTreeWithNoFile_IsCreatedIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - """); + string? result = await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(physicalProjectTree); + Assert.Equal($@"C:\Project\{_fileName}", result); + } - await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + [Fact] + public async Task GetFileAsync_WhenTreeWithNoFile_IsCreatedIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + """); - Assert.Equal(1, callCount); - } + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + var provider = CreateInstance(physicalProjectTree); - [Fact] - public async Task GetFileAsync_WhenTreeWithMissingFile_IsCreatedIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {}), FilePath: "C:\Project\{{_fileName}}" - """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(physicalProjectTree); - - await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - - Assert.Equal(1, callCount); - } - - internal AbstractFindByNameSpecialFileProvider CreateInstance(IProjectTree projectTree) - { - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: projectTree); + await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + + Assert.Equal(1, callCount); + } - return CreateInstance(physicalProjectTree); - } + [Fact] + public async Task GetFileAsync_WhenTreeWithMissingFile_IsCreatedIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {}), FilePath: "C:\Project\{{_fileName}}" + """); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + var provider = CreateInstance(physicalProjectTree); + + await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + + Assert.Equal(1, callCount); + } - internal abstract AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree); + internal AbstractFindByNameSpecialFileProvider CreateInstance(IProjectTree projectTree) + { + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: projectTree); - internal static T CreateInstanceWithOverrideCreateFileAsync(IPhysicalProjectTree projectTree, params object[] args) - where T : AbstractFindByNameSpecialFileProvider - { - object[] arguments = new object[args.Length + 1]; - arguments[0] = projectTree; - args.CopyTo(arguments, 1); + return CreateInstance(physicalProjectTree); + } + + internal abstract AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree); + + internal static T CreateInstanceWithOverrideCreateFileAsync(IPhysicalProjectTree projectTree, params object[] args) + where T : AbstractFindByNameSpecialFileProvider + { + object[] arguments = new object[args.Length + 1]; + arguments[0] = projectTree; + args.CopyTo(arguments, 1); - // We override CreateFileAsync to call the CreateEmptyFileAsync which makes writting tests in the base easier - var mock = new Mock(arguments); - mock.Protected().Setup("CreateFileAsync", ItExpr.IsAny()) - .Returns(projectTree.TreeStorage.CreateEmptyFileAsync); + // We override CreateFileAsync to call the CreateEmptyFileAsync which makes writting tests in the base easier + var mock = new Mock(arguments); + mock.Protected().Setup("CreateFileAsync", ItExpr.IsAny()) + .Returns(projectTree.TreeStorage.CreateEmptyFileAsync); - mock.CallBase = true; + mock.CallBase = true; - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase.cs index c234f9f739..b4734e89a8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase.cs @@ -2,250 +2,249 @@ using Moq.Protected; -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public abstract class AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public abstract class AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + private readonly string _fileName; + + protected AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase(string fileName) { - private readonly string _fileName; + _fileName = fileName; + } - protected AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase(string fileName) - { - _fileName = fileName; - } + [Fact] + public async Task GetFileAsync_WhenNoAppDesigner_ReturnsPathUnderAppDesigner() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + """); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); + var provider = CreateInstance(specialFilesManager, tree); - [Fact] - public async Task GetFileAsync_WhenNoAppDesigner_ReturnsPathUnderAppDesigner() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - """); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); - var provider = CreateInstance(specialFilesManager, tree); + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + Assert.Equal($@"C:\Project\Properties\{_fileName}", result); + } - Assert.Equal($@"C:\Project\Properties\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenAppDesigner_ReturnsPathUnderAppDesigner() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity Folder AppDesignerFolder}), FilePath: "C:\Project\Properties" + """); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); + var provider = CreateInstance(specialFilesManager, tree); - [Fact] - public async Task GetFileAsync_WhenAppDesigner_ReturnsPathUnderAppDesigner() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity Folder AppDesignerFolder}), FilePath: "C:\Project\Properties" - """); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); - var provider = CreateInstance(specialFilesManager, tree); + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + Assert.Equal($@"C:\Project\Properties\{_fileName}", result); + } - Assert.Equal($@"C:\Project\Properties\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenAppDesignerNotSupported_ReturnsPathUnderProjectRoot() + { // AppDesigner is turned off + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + """); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - [Fact] - public async Task GetFileAsync_WhenAppDesignerNotSupported_ReturnsPathUnderProjectRoot() - { // AppDesigner is turned off - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - """); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var provider = CreateInstance(specialFilesManager, tree); - var provider = CreateInstance(specialFilesManager, tree); + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + Assert.Equal($@"C:\Project\{_fileName}", result); + } - Assert.Equal($@"C:\Project\{_fileName}", result); - } + [Fact] + public async Task GetFileAsync_WhenAppDesignerWithFile_ReturnsPath() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity Folder AppDesignerFolder}), FilePath: "C:\Project\Properties" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\Properties\{{_fileName}}" + """); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); + var provider = CreateInstance(specialFilesManager, tree); + + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + + Assert.Equal($@"C:\Project\Properties\{_fileName}", result); + } - [Fact] - public async Task GetFileAsync_WhenAppDesignerWithFile_ReturnsPath() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity Folder AppDesignerFolder}), FilePath: "C:\Project\Properties" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\Properties\{{_fileName}}" - """); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); - var provider = CreateInstance(specialFilesManager, tree); - - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - - Assert.Equal($@"C:\Project\Properties\{_fileName}", result); - } - - [Fact] - public async Task GetFileAsync_WhenAppDesignerButRootWithFile_ReturnsPath() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity Folder AppDesignerFolder}), FilePath: "C:\Project\Properties" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" - """); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); - var provider = CreateInstance(specialFilesManager, tree); - - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - - Assert.Equal($@"C:\Project\{_fileName}", result); - } - - [Fact] - public async Task GetFileAsync_WhenRootWithFile_ReturnsPath() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" - """); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + [Fact] + public async Task GetFileAsync_WhenAppDesignerButRootWithFile_ReturnsPath() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity Folder AppDesignerFolder}), FilePath: "C:\Project\Properties" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" + """); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(@"C:\Project\Properties"); + var provider = CreateInstance(specialFilesManager, tree); + + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + + Assert.Equal($@"C:\Project\{_fileName}", result); + } - var provider = CreateInstance(specialFilesManager, tree); + [Fact] + public async Task GetFileAsync_WhenRootWithFile_ReturnsPath() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" + """); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + var provider = CreateInstance(specialFilesManager, tree); - Assert.Equal($@"C:\Project\{_fileName}", result); - } + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - [Fact] - public async Task GetFileAsync_WhenTreeWithFolderSameName_ReturnsPath() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" - """); + Assert.Equal($@"C:\Project\{_fileName}", result); + } - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var provider = CreateInstance(specialFilesManager, tree); + [Fact] + public async Task GetFileAsync_WhenTreeWithFolderSameName_ReturnsPath() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" + """); - var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var provider = CreateInstance(specialFilesManager, tree); - Assert.Equal($@"C:\Project\{_fileName}", result); - } + var result = await provider.GetFileAsync(0, SpecialFileFlags.FullPath); - [Fact] - public async Task GetFileAsync_WhenTreeWithFolderSameName_ThrowsIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" - """); - - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var provider = CreateInstance(specialFilesManager, tree); - - await Assert.ThrowsAsync(() => - { - return provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - }); - } - - [Fact] - public async Task GetFileAsync_WhenTreeWithExcludedFile_IsAddedToProjectIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk IncludeInProjectCandidate}), FilePath: "C:\Project\{{_fileName}}" - """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementAddFileAsync(path => callCount++); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(specialFilesManager, physicalProjectTree); - - await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - - Assert.Equal(1, callCount); - } - - [Fact] - public async Task GetFileAsync_WhenTreeWithExistentFile_ReturnsPathIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" - """); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var provider = CreateInstance(specialFilesManager, physicalProjectTree); - - string? result = await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - - Assert.Equal($@"C:\Project\{_fileName}", result); - } - - [Fact] - public async Task GetFileAsync_WhenTreeWithNoFile_IsCreatedIfCreateIfNotExist() + Assert.Equal($@"C:\Project\{_fileName}", result); + } + + [Fact] + public async Task GetFileAsync_WhenTreeWithFolderSameName_ThrowsIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity Folder}), FilePath: "C:\Project\{{_fileName}}" + """); + + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var provider = CreateInstance(specialFilesManager, tree); + + await Assert.ThrowsAsync(() => { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - """); + return provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + }); + } - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(specialFilesManager, physicalProjectTree); + [Fact] + public async Task GetFileAsync_WhenTreeWithExcludedFile_IsAddedToProjectIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk IncludeInProjectCandidate}), FilePath: "C:\Project\{{_fileName}}" + """); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementAddFileAsync(path => callCount++); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + var provider = CreateInstance(specialFilesManager, physicalProjectTree); + + await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + + Assert.Equal(1, callCount); + } - await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + [Fact] + public async Task GetFileAsync_WhenTreeWithExistentFile_ReturnsPathIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\{{_fileName}}" + """); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var provider = CreateInstance(specialFilesManager, physicalProjectTree); + + string? result = await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + + Assert.Equal($@"C:\Project\{_fileName}", result); + } - Assert.Equal(1, callCount); - } + [Fact] + public async Task GetFileAsync_WhenTreeWithNoFile_IsCreatedIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + """); - [Fact] - public async Task GetFileAsync_WhenTreeWithMissingFile_IsCreatedIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - $$""" - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - {{_fileName}} (flags: {}), FilePath: "C:\Project\{{_fileName}}" - """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); - var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(specialFilesManager, physicalProjectTree); - - await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - - Assert.Equal(1, callCount); - } - - private AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IProjectTree projectTree) - { - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: projectTree); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + var provider = CreateInstance(specialFilesManager, physicalProjectTree); - return CreateInstance(specialFilesManager, physicalProjectTree); - } + await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); - internal abstract AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree); + Assert.Equal(1, callCount); + } - internal static T CreateInstanceWithOverrideCreateFileAsync(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree, params object[] additionalArguments) - where T : AbstractFindByNameUnderAppDesignerSpecialFileProvider - { - object[] arguments = new object[3 + additionalArguments.Length]; - arguments[0] = specialFilesManager; - arguments[1] = projectTree; - arguments[2] = null!; - additionalArguments.CopyTo(arguments, 3); + [Fact] + public async Task GetFileAsync_WhenTreeWithMissingFile_IsCreatedIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + $$""" + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + {{_fileName}} (flags: {}), FilePath: "C:\Project\{{_fileName}}" + """); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateEmptyFileAsync(path => callCount++); + var specialFilesManager = ISpecialFilesManagerFactory.ImplementGetFile(null); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + var provider = CreateInstance(specialFilesManager, physicalProjectTree); + + await provider.GetFileAsync(0, SpecialFileFlags.CreateIfNotExist); + + Assert.Equal(1, callCount); + } + + private AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IProjectTree projectTree) + { + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: projectTree); + + return CreateInstance(specialFilesManager, physicalProjectTree); + } + + internal abstract AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree); + + internal static T CreateInstanceWithOverrideCreateFileAsync(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree, params object[] additionalArguments) + where T : AbstractFindByNameUnderAppDesignerSpecialFileProvider + { + object[] arguments = new object[3 + additionalArguments.Length]; + arguments[0] = specialFilesManager; + arguments[1] = projectTree; + arguments[2] = null!; + additionalArguments.CopyTo(arguments, 3); - // We override CreateFileAsync to call the CreateEmptyFileAsync which makes writting tests in the base easier - var mock = new Mock(arguments); - mock.Protected().Setup("CreateFileCoreAsync", ItExpr.IsAny()) - .Returns(projectTree.TreeStorage.CreateEmptyFileAsync); + // We override CreateFileAsync to call the CreateEmptyFileAsync which makes writting tests in the base easier + var mock = new Mock(arguments); + mock.Protected().Setup("CreateFileCoreAsync", ItExpr.IsAny()) + .Returns(projectTree.TreeStorage.CreateEmptyFileAsync); - mock.CallBase = true; + mock.CallBase = true; - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProviderTests.cs index 31f17785c0..9da9897644 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppDesignerFolderSpecialFileProviderTests.cs @@ -1,290 +1,289 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public class AppDesignerFolderSpecialFileProviderTests { - public class AppDesignerFolderSpecialFileProviderTests - { - [Theory] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\Properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - properties (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - PROPERTIES (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\PROPERTIES")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {Folder UnrecognizedCapability AppDesignerFolder BubbleUp}) - """, - @"C:\Project\Properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) - """, - @"C:\Project\Properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - AssemblyInfo.cs (flags: {}) - """, - @"C:\Project\Properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - Folder (flags: {Folder}) - """, - @"C:\Project\Properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Folder (flags: {Folder}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\Folder\Properties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + [Theory] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\Properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + properties (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + PROPERTIES (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\PROPERTIES")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {Folder UnrecognizedCapability AppDesignerFolder BubbleUp}) + """, + @"C:\Project\Properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) + """, + @"C:\Project\Properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + AssemblyInfo.cs (flags: {}) + """, + @"C:\Project\Properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {Folder AppDesignerFolder BubbleUp}) Folder (flags: {Folder}) - NotCalledProperties (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\Folder\NotCalledProperties")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Folder (flags: {Folder}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\Folder\My Project")] - [InlineData( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Folder1 (flags: {Folder}) - Folder2 (flags: {Folder}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """, - @"C:\Project\Folder2\My Project")] - public async Task GetFile_WhenTreeWithAppDesignerFolder_ReturnsPath(string input, string expected) - { - var tree = ProjectTreeParser.Parse(input); - var treeProvider = new ProjectTreeProvider(); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); - - var provider = CreateInstance(physicalProjectTree); + """, + @"C:\Project\Properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Folder (flags: {Folder}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\Folder\Properties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Folder (flags: {Folder}) + NotCalledProperties (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\Folder\NotCalledProperties")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Folder (flags: {Folder}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\Folder\My Project")] + [InlineData( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Folder1 (flags: {Folder}) + Folder2 (flags: {Folder}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """, + @"C:\Project\Folder2\My Project")] + public async Task GetFile_WhenTreeWithAppDesignerFolder_ReturnsPath(string input, string expected) + { + var tree = ProjectTreeParser.Parse(input); + var treeProvider = new ProjectTreeProvider(); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); - var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.FullPath); + var provider = CreateInstance(physicalProjectTree); - Assert.Equal(expected, result); - } + var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.FullPath); - [Fact] - public async Task GetFile_WhenTreeWithAppDesignerFolder_ReturnsPathIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """); + Assert.Equal(expected, result); + } - var treeProvider = new ProjectTreeProvider(); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); + [Fact] + public async Task GetFile_WhenTreeWithAppDesignerFolder_ReturnsPathIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """); - var provider = CreateInstance(physicalProjectTree); + var treeProvider = new ProjectTreeProvider(); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); - var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + var provider = CreateInstance(physicalProjectTree); - Assert.Equal(@"C:\Project\My Project", result); - } + var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - [Theory] // AppDesignerFolder // Expected return - [InlineData(@"Properties", @"C:\Project\Properties")] - [InlineData(@"My Project", @"C:\Project\My Project")] - [InlineData(@"Folder\AppDesigner", @"C:\Project\Folder\AppDesigner")] - [InlineData(@"", null)] - public async Task GetFile_WhenTreeWithoutAppDesignerFolder_ReturnsDefaultAppDesignerFolder(string input, string expected) - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity Folder}) - """); + Assert.Equal(@"C:\Project\My Project", result); + } - var treeProvider = new ProjectTreeProvider(); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); + [Theory] // AppDesignerFolder // Expected return + [InlineData(@"Properties", @"C:\Project\Properties")] + [InlineData(@"My Project", @"C:\Project\My Project")] + [InlineData(@"Folder\AppDesigner", @"C:\Project\Folder\AppDesigner")] + [InlineData(@"", null)] + public async Task GetFile_WhenTreeWithoutAppDesignerFolder_ReturnsDefaultAppDesignerFolder(string input, string expected) + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity Folder}) + """); - var projectProperties = CreateProperties(appDesignerFolderName: input); + var treeProvider = new ProjectTreeProvider(); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); - var provider = CreateInstance(physicalProjectTree, projectProperties); + var projectProperties = CreateProperties(appDesignerFolderName: input); - var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.FullPath); + var provider = CreateInstance(physicalProjectTree, projectProperties); - Assert.Equal(expected, result); - } + var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.FullPath); - [Fact] - public async Task GetFileAsync_WhenTreeWithFileSameName_ReturnsDefaultAppDesignerFolder() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity FileOnDisk}) - """); + Assert.Equal(expected, result); + } - var treeProvider = new ProjectTreeProvider(); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); + [Fact] + public async Task GetFileAsync_WhenTreeWithFileSameName_ReturnsDefaultAppDesignerFolder() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity FileOnDisk}) + """); - var projectProperties = CreateProperties(appDesignerFolderName: "Properties"); + var treeProvider = new ProjectTreeProvider(); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); - var provider = CreateInstance(physicalProjectTree, projectProperties); + var projectProperties = CreateProperties(appDesignerFolderName: "Properties"); - var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.FullPath); + var provider = CreateInstance(physicalProjectTree, projectProperties); - Assert.Equal(@"C:\Project\Properties", result); - } + var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.FullPath); - [Fact] - public async Task GetFileAsync_WhenTreeWithFileSameName_ThrowsIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\Properties" - """); + Assert.Equal(@"C:\Project\Properties", result); + } - var treeProvider = new ProjectTreeProvider(); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); + [Fact] + public async Task GetFileAsync_WhenTreeWithFileSameName_ThrowsIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity FileOnDisk}), FilePath: "C:\Project\Properties" + """); - var projectProperties = CreateProperties(appDesignerFolderName: "Properties"); + var treeProvider = new ProjectTreeProvider(); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(provider: treeProvider, currentTree: tree); - var provider = CreateInstance(physicalProjectTree, projectProperties); + var projectProperties = CreateProperties(appDesignerFolderName: "Properties"); - await Assert.ThrowsAsync(() => - { - return provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - }); - } + var provider = CreateInstance(physicalProjectTree, projectProperties); - [Fact] - public async Task GetFileAsync_WhenTreeWithExcludedFolder_IsAddedToProjectIfCreateIfNotExist() + await Assert.ThrowsAsync(() => { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity Folder IncludeInProjectCandidate}), FilePath: "C:\Project\Properties" - """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementAddFolderAsync(path => callCount++); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + return provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + }); + } + + [Fact] + public async Task GetFileAsync_WhenTreeWithExcludedFolder_IsAddedToProjectIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity Folder IncludeInProjectCandidate}), FilePath: "C:\Project\Properties" + """); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementAddFolderAsync(path => callCount++); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(physicalProjectTree); + var provider = CreateInstance(physicalProjectTree); - await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - Assert.Equal(1, callCount); - } + Assert.Equal(1, callCount); + } - [Fact] - public async Task GetFileAsync_WhenTreeWithExistentAppDesignerFolder_ReturnsPathIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {FileSystemEntity Folder AppDesignerFolder}) - """); + [Fact] + public async Task GetFileAsync_WhenTreeWithExistentAppDesignerFolder_ReturnsPathIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {FileSystemEntity Folder AppDesignerFolder}) + """); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); - var provider = CreateInstance(physicalProjectTree); + var provider = CreateInstance(physicalProjectTree); - string? result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + string? result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - Assert.Equal(@"C:\Project\Properties", result); - } + Assert.Equal(@"C:\Project\Properties", result); + } - [Fact] - public async Task GetFileAsync_WhenTreeWithNoAppDesignerFolder_IsCreatedIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - """); + [Fact] + public async Task GetFileAsync_WhenTreeWithNoAppDesignerFolder_IsCreatedIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateFolderAsync(path => callCount++); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateFolderAsync(path => callCount++); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var projectProperties = CreateProperties(appDesignerFolderName: "Properties"); + var projectProperties = CreateProperties(appDesignerFolderName: "Properties"); - var provider = CreateInstance(physicalProjectTree, projectProperties); + var provider = CreateInstance(physicalProjectTree, projectProperties); - await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - Assert.Equal(1, callCount); - } + Assert.Equal(1, callCount); + } - [Fact] - public async Task GetFileAsync_WhenTreeWithMissingAppDesignerFolder_IsCreatedIfCreateIfNotExist() - { - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" - Properties (flags: {Folder AppDesignerFolder}) - """); - int callCount = 0; - var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateFolderAsync(path => callCount++); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); + [Fact] + public async Task GetFileAsync_WhenTreeWithMissingAppDesignerFolder_IsCreatedIfCreateIfNotExist() + { + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot}), FilePath: "C:\Project\Project.csproj" + Properties (flags: {Folder AppDesignerFolder}) + """); + int callCount = 0; + var storage = IPhysicalProjectTreeStorageFactory.ImplementCreateFolderAsync(path => callCount++); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree, storage: storage); - var provider = CreateInstance(physicalProjectTree); + var provider = CreateInstance(physicalProjectTree); - await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - Assert.Equal(1, callCount); - } + Assert.Equal(1, callCount); + } - [Fact] - public async Task GetFileAsync_WhenRootMarkedWithDisableAddItemFolder_ReturnsNull() - { // Mimics an extension turning on DisableAddItem flag for our parent - var tree = ProjectTreeParser.Parse( - """ - Project (flags: {ProjectRoot DisableAddItemFolder}), FilePath: "C:\Project\Project.csproj" - """); - var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); + [Fact] + public async Task GetFileAsync_WhenRootMarkedWithDisableAddItemFolder_ReturnsNull() + { // Mimics an extension turning on DisableAddItem flag for our parent + var tree = ProjectTreeParser.Parse( + """ + Project (flags: {ProjectRoot DisableAddItemFolder}), FilePath: "C:\Project\Project.csproj" + """); + var physicalProjectTree = IPhysicalProjectTreeFactory.Create(currentTree: tree); - var provider = CreateInstance(physicalProjectTree); + var provider = CreateInstance(physicalProjectTree); - var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); + var result = await provider.GetFileAsync(SpecialFiles.AppDesigner, SpecialFileFlags.CreateIfNotExist); - Assert.Null(result); - } + Assert.Null(result); + } - private static ProjectProperties CreateProperties(string appDesignerFolderName) - { - return ProjectPropertiesFactory.Create(UnconfiguredProjectFactory.Create(), new[] { - new PropertyPageData(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, appDesignerFolderName) - }); - } + private static ProjectProperties CreateProperties(string appDesignerFolderName) + { + return ProjectPropertiesFactory.Create(UnconfiguredProjectFactory.Create(), new[] { + new PropertyPageData(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, appDesignerFolderName) + }); + } - private static AppDesignerFolderSpecialFileProvider CreateInstance(IPhysicalProjectTree? physicalProjectTree = null, ProjectProperties? properties = null) - { - physicalProjectTree ??= IPhysicalProjectTreeFactory.Create(); - properties ??= CreateProperties(appDesignerFolderName: "Properties"); + private static AppDesignerFolderSpecialFileProvider CreateInstance(IPhysicalProjectTree? physicalProjectTree = null, ProjectProperties? properties = null) + { + physicalProjectTree ??= IPhysicalProjectTreeFactory.Create(); + properties ??= CreateProperties(appDesignerFolderName: "Properties"); - return new AppDesignerFolderSpecialFileProvider(physicalProjectTree, properties); - } + return new AppDesignerFolderSpecialFileProvider(physicalProjectTree, properties); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProviderTests.cs index 1621fc963f..2cfe9c390d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppManifestSpecialFileProviderTests.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public class AppManifestSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public class AppManifestSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + public AppManifestSpecialFileProviderTests() + : base("app.manifest") { - public AppManifestSpecialFileProviderTests() - : base("app.manifest") - { - } + } - internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - { - var properties = CreateProperties("NoManifest"); + internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + { + var properties = CreateProperties("NoManifest"); - return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree, properties); - } + return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree, properties); + } - private ProjectProperties CreateProperties(string appManifestPropertyValue) - { - return ProjectPropertiesFactory.Create(UnconfiguredProjectFactory.Create(), - new PropertyPageData( - ConfigurationGeneralBrowseObject.SchemaName, - ConfigurationGeneralBrowseObject.ApplicationManifestProperty, - appManifestPropertyValue)); - } + private ProjectProperties CreateProperties(string appManifestPropertyValue) + { + return ProjectPropertiesFactory.Create(UnconfiguredProjectFactory.Create(), + new PropertyPageData( + ConfigurationGeneralBrowseObject.SchemaName, + ConfigurationGeneralBrowseObject.ApplicationManifestProperty, + appManifestPropertyValue)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProviderTests.cs index 3db67f4b8a..f88c9e0b5f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AppSettingsSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public class AppSettingsSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public class AppSettingsSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + public AppSettingsSpecialFileProviderTests() + : base("Settings.settings") { - public AppSettingsSpecialFileProviderTests() - : base("Settings.settings") - { - } + } - internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); - } + internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProviderTests.cs index bfa5fdd6db..5902d78fc8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/AssemblyResourcesSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public class AssemblyResourcesSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public class AssemblyResourcesSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + public AssemblyResourcesSpecialFileProviderTests() + : base("Resources.resx") { - public AssemblyResourcesSpecialFileProviderTests() - : base("Resources.resx") - { - } + } - internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); - } + internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProviderTests.cs index b94e1dc0d5..7289e80d3c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppConfigSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp; + +public class CSharpAppConfigSpecialFileProviderTests : AbstractFindByNameSpecialFileProviderTestBase { - public class CSharpAppConfigSpecialFileProviderTests : AbstractFindByNameSpecialFileProviderTestBase + public CSharpAppConfigSpecialFileProviderTests() + : base("App.config") { - public CSharpAppConfigSpecialFileProviderTests() - : base("App.config") - { - } + } - internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(projectTree, (ICreateFileFromTemplateService)null!); - } + internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(projectTree, (ICreateFileFromTemplateService)null!); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProviderTests.cs index 1d6f997bbd..8b12fd1784 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAppXamlSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp; + +public class CSharpAppXamlSpecialFileProviderTests : AbstractAppXamlSpecialFileProviderTestBase { - public class CSharpAppXamlSpecialFileProviderTests : AbstractAppXamlSpecialFileProviderTestBase + public CSharpAppXamlSpecialFileProviderTests() + : base("App.xaml") { - public CSharpAppXamlSpecialFileProviderTests() - : base("App.xaml") - { - } + } - internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(projectTree); - } + internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProviderTests.cs index 841e3b3a8e..d52c844e22 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/CSharp/CSharpAssemblyInfoSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.CSharp; + +public class CSharpAssemblyInfoSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public class CSharpAssemblyInfoSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + public CSharpAssemblyInfoSpecialFileProviderTests() + : base("AssemblyInfo.cs") { - public CSharpAssemblyInfoSpecialFileProviderTests() - : base("AssemblyInfo.cs") - { - } + } - internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); - } + internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProviderTests.cs index 6bd262c806..18092c976d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/LicensesSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders; + +public class LicensesSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public class LicensesSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + public LicensesSpecialFileProviderTests() + : base("licenses.licx") { - public LicensesSpecialFileProviderTests() - : base("licenses.licx") - { - } + } - internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - { - return new LicensesSpecialFileProvider(specialFilesManager, projectTree); - } + internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + { + return new LicensesSpecialFileProvider(specialFilesManager, projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProviderTests.cs index 771d443d15..8e64406995 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppConfigSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic; + +public class VisualBasicAppConfigSpecialFileProviderTests : AbstractFindByNameSpecialFileProviderTestBase { - public class VisualBasicAppConfigSpecialFileProviderTests : AbstractFindByNameSpecialFileProviderTestBase + public VisualBasicAppConfigSpecialFileProviderTests() + : base("App.config") { - public VisualBasicAppConfigSpecialFileProviderTests() - : base("App.config") - { - } + } - internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(projectTree, (ICreateFileFromTemplateService)null!); - } + internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(projectTree, (ICreateFileFromTemplateService)null!); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProviderTests.cs index 61dea92911..aa42f96f35 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAppXamlSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic; + +public class VisualBasicAppXamlSpecialFileProviderTests : AbstractAppXamlSpecialFileProviderTestBase { - public class VisualBasicAppXamlSpecialFileProviderTests : AbstractAppXamlSpecialFileProviderTestBase + public VisualBasicAppXamlSpecialFileProviderTests() + : base("Application.xaml") { - public VisualBasicAppXamlSpecialFileProviderTests() - : base("Application.xaml") - { - } + } - internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(projectTree, (ICreateFileFromTemplateService)null!); - } + internal override AbstractFindByNameSpecialFileProvider CreateInstance(IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(projectTree, (ICreateFileFromTemplateService)null!); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProviderTests.cs index 324ba85000..db71d158c1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/SpecialFileProviders/VisualBasic/VisualBasicAssemblyInfoSpecialFileProviderTests.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.SpecialFileProviders.VisualBasic; + +public class VisualBasicAssemblyInfoSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase { - public class VisualBasicAssemblyInfoSpecialFileProviderTests : AbstractFindByNameUnderAppDesignerSpecialFileProviderTestBase + public VisualBasicAssemblyInfoSpecialFileProviderTests() + : base("AssemblyInfo.vb") { - public VisualBasicAssemblyInfoSpecialFileProviderTests() - : base("AssemblyInfo.vb") - { - } + } - internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) - { - return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); - } + internal override AbstractFindByNameUnderAppDesignerSpecialFileProvider CreateInstance(ISpecialFilesManager specialFilesManager, IPhysicalProjectTree projectTree) + { + return CreateInstanceWithOverrideCreateFileAsync(specialFilesManager, projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProviderTests.cs index adfac0db33..8dbeaaba96 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/AppDesignerFolderProjectTreePropertiesProviderTests.cs @@ -3,727 +3,726 @@ using Microsoft.VisualStudio.ProjectSystem.Imaging; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.Tree +namespace Microsoft.VisualStudio.ProjectSystem.Tree; + +public class AppDesignerFolderProjectTreePropertiesProviderTests { - public class AppDesignerFolderProjectTreePropertiesProviderTests + [Fact] + public void ProjectPropertiesRules_ReturnsAppDesigner() { - [Fact] - public void ProjectPropertiesRules_ReturnsAppDesigner() - { - var propertiesProvider = CreateInstance(); + var propertiesProvider = CreateInstance(); - Assert.Equal(propertiesProvider.ProjectPropertiesRules, new string[] { "AppDesigner" }); - } + Assert.Equal(propertiesProvider.ProjectPropertiesRules, new string[] { "AppDesigner" }); + } + + [Fact] + public void UpdateProjectTreeSettings_NullAsRuleSnapshots_ThrowsArgumentNull() + { + var propertiesProvider = CreateInstance(); + IImmutableDictionary projectTreeSettings = ImmutableStringDictionary.EmptyOrdinal; - [Fact] - public void UpdateProjectTreeSettings_NullAsRuleSnapshots_ThrowsArgumentNull() + Assert.Throws("ruleSnapshots", () => { - var propertiesProvider = CreateInstance(); - IImmutableDictionary projectTreeSettings = ImmutableStringDictionary.EmptyOrdinal; + propertiesProvider.UpdateProjectTreeSettings(null!, ref projectTreeSettings); + }); + } - Assert.Throws("ruleSnapshots", () => - { - propertiesProvider.UpdateProjectTreeSettings(null!, ref projectTreeSettings); - }); - } + [Fact] + public void UpdateProjectTreeSettings_NullAsProjectTreeSettings_ThrowsArgumentNull() + { + var ruleSnapshots = IProjectRuleSnapshotsFactory.Create(); + var propertiesProvider = CreateInstance(); + IImmutableDictionary? projectTreeSettings = null; - [Fact] - public void UpdateProjectTreeSettings_NullAsProjectTreeSettings_ThrowsArgumentNull() + Assert.Throws("projectTreeSettings", () => { - var ruleSnapshots = IProjectRuleSnapshotsFactory.Create(); - var propertiesProvider = CreateInstance(); - IImmutableDictionary? projectTreeSettings = null; - - Assert.Throws("projectTreeSettings", () => - { - propertiesProvider.UpdateProjectTreeSettings(ruleSnapshots, ref projectTreeSettings!); - }); - } - - [Fact] - public void CalculatePropertyValues_NullAsPropertyContext_ThrowsArgumentNull() - { - var propertyValues = IProjectTreeCustomizablePropertyValuesFactory.Create(); - var propertiesProvider = CreateInstance(); + propertiesProvider.UpdateProjectTreeSettings(ruleSnapshots, ref projectTreeSettings!); + }); + } - Assert.Throws("propertyContext", () => - { - propertiesProvider.CalculatePropertyValues(null!, propertyValues); - }); - } + [Fact] + public void CalculatePropertyValues_NullAsPropertyContext_ThrowsArgumentNull() + { + var propertyValues = IProjectTreeCustomizablePropertyValuesFactory.Create(); + var propertiesProvider = CreateInstance(); - [Fact] - public void CalculatePropertyValues_NullAsPropertyValues_ThrowsArgumentNull() + Assert.Throws("propertyContext", () => { - var propertyContext = IProjectTreeCustomizablePropertyContextFactory.Create(); - var propertiesProvider = CreateInstance(); + propertiesProvider.CalculatePropertyValues(null!, propertyValues); + }); + } - Assert.Throws("propertyValues", () => - { - propertiesProvider.CalculatePropertyValues(propertyContext, null!); - }); - } + [Fact] + public void CalculatePropertyValues_NullAsPropertyValues_ThrowsArgumentNull() + { + var propertyContext = IProjectTreeCustomizablePropertyContextFactory.Create(); + var propertiesProvider = CreateInstance(); - [Fact] - public void ChangePropertyValues_TreeWithAppDesignerFolderButSupportsProjectDesignerFalse_ReturnsUnmodifiedTree() + Assert.Throws("propertyValues", () => { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => false); // Don't support AppDesigner - var propertiesProvider = CreateInstance(designerService); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - """); + propertiesProvider.CalculatePropertyValues(propertyContext, null!); + }); + } - Verify(propertiesProvider, tree, tree); - } + [Fact] + public void ChangePropertyValues_TreeWithAppDesignerFolderButSupportsProjectDesignerFalse_ReturnsUnmodifiedTree() + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => false); // Don't support AppDesigner + var propertiesProvider = CreateInstance(designerService); - [Theory] - [InlineData( + var tree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - """)] - public void ChangePropertyValues_TreeWithMyProjectFolder_ReturnsUnmodifiedTree(string input) - { // "Properties" is the default, so we shouldn't find "My Project" - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + Properties (flags: {Folder}) + """); - var tree = ProjectTreeParser.Parse(input); + Verify(propertiesProvider, tree, tree); + } - Verify(propertiesProvider, tree, tree); - } + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + """)] + public void ChangePropertyValues_TreeWithMyProjectFolder_ReturnsUnmodifiedTree(string input) + { // "Properties" is the default, so we shouldn't find "My Project" + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder}) - AssemblyInfo.cs (flags: {}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder}) - AssemblyInfo.cs (flags: {}) - NotProperties (flags: {Folder}) - """)] - public void ChangePropertyValues_TreeWithoutAppDesignerFolder_ReturnsUnmodifiedTree(string input) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => false); - var propertiesProvider = CreateInstance(designerService); + var tree = ProjectTreeParser.Parse(input); - var tree = ProjectTreeParser.Parse(input); + Verify(propertiesProvider, tree, tree); + } - Verify(propertiesProvider, tree, tree); - } + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + AssemblyInfo.cs (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + AssemblyInfo.cs (flags: {}) + NotProperties (flags: {Folder}) + """)] + public void ChangePropertyValues_TreeWithoutAppDesignerFolder_ReturnsUnmodifiedTree(string input) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => false); + var propertiesProvider = CreateInstance(designerService); - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {NotFolder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Unrecognized NotAFolder}) - """)] - public void ChangePropertyValues_TreeWithFileCalledProperties_ReturnsUnmodifiedTree(string input) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + var tree = ProjectTreeParser.Parse(input); - var tree = ProjectTreeParser.Parse(input); + Verify(propertiesProvider, tree, tree); + } - Verify(propertiesProvider, tree, tree); - } + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {NotFolder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Unrecognized NotAFolder}) + """)] + public void ChangePropertyValues_TreeWithFileCalledProperties_ReturnsUnmodifiedTree(string input) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder IncludeInProjectCandidate}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {IncludeInProjectCandidate Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {IncludeInProjectCandidate}) - """)] - public void ChangePropertyValues_TreeWithExcludedAppDesignerFolder_ReturnsUnmodifiedTree(string input) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + var tree = ProjectTreeParser.Parse(input); + + Verify(propertiesProvider, tree, tree); + } - var tree = ProjectTreeParser.Parse(input); + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder IncludeInProjectCandidate}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {IncludeInProjectCandidate Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {IncludeInProjectCandidate}) + """)] + public void ChangePropertyValues_TreeWithExcludedAppDesignerFolder_ReturnsUnmodifiedTree(string input) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - Verify(propertiesProvider, tree, tree); - } + var tree = ProjectTreeParser.Parse(input); - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) + Verify(propertiesProvider, tree, tree); + } + + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + Properties (flags: {Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) Folder (flags: {Folder}) Properties (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder1 (flags: {Folder}) + Folder2 (flags: {Folder}) + Properties (flags: {Folder}) + """)] + public void ChangePropertyValues_TreeWithNestedAppDesignerFolder_ReturnsUnmodifiedTree(string input) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); + + var tree = ProjectTreeParser.Parse(input); + + Verify(propertiesProvider, tree, tree); + } + + [Theory] + [InlineData( + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """, + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """, + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder BubbleUp}) + """, + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder Unrecognized AppDesignerFolder}) + """, + """ + Root(flags: {ProjectRoot}) + Properties (flags: {Folder Unrecognized AppDesignerFolder BubbleUp}) + """)] + public void ChangePropertyValues_TreeWithAppDesignerFolderAlreadyMarkedAsAppDesignerOrBubbleup_AddsRemainingFlags(string input, string expected) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); + + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); + + Verify(propertiesProvider, expectedTree, inputTree); + } + + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder BubbleUp}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + properties (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + properties (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + PROPERTIES (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + PROPERTIES (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder UnrecognizedCapability}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder UnrecognizedCapability AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}) + AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}) + AssemblyInfo.cs (flags: {}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + AssemblyInfo.cs (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}) Folder (flags: {Folder}) - Folder (flags: {Folder}) - Properties (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder1 (flags: {Folder}) - Folder2 (flags: {Folder}) - Properties (flags: {Folder}) - """)] - public void ChangePropertyValues_TreeWithNestedAppDesignerFolder_ReturnsUnmodifiedTree(string input) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + Folder (flags: {Folder}) + """)] + public void ChangePropertyValues_TreeWithAppDesignerFolder_ReturnsCandidateMarkedWithAppDesignerFolderAndBubbleUp(string input, string expected) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - var tree = ProjectTreeParser.Parse(input); + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); - Verify(propertiesProvider, tree, tree); - } + Verify(propertiesProvider, expectedTree, inputTree); + } - [Theory] - [InlineData( - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """, - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """, - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder BubbleUp}) - """, - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder Unrecognized AppDesignerFolder}) - """, - """ - Root(flags: {ProjectRoot}) - Properties (flags: {Folder Unrecognized AppDesignerFolder BubbleUp}) - """)] - public void ChangePropertyValues_TreeWithAppDesignerFolderAlreadyMarkedAsAppDesignerOrBubbleup_AddsRemainingFlags(string input, string expected) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {FileSystemEntity Folder}) + Folder (flags: {FileSystemEntity Folder}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {FileSystemEntity Folder AppDesignerFolder BubbleUp}), Icon: {259567C1-AA6B-46BF-811C-C145DD9F3B48 28} + Folder (flags: {FileSystemEntity Folder}) + """)] + public void ChangePropertyValues_TreeWithAppDesignerFolder_SetsIconToAppDesignerFolder(string input, string expected) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.AppDesignerFolder, new ProjectImageMoniker(new Guid("259567C1-AA6B-46BF-811C-C145DD9F3B48"), 28)); + var propertiesProvider = CreateInstance(imageProvider, designerService); - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); - Verify(propertiesProvider, expectedTree, inputTree); - } + Verify(propertiesProvider, expectedTree, inputTree); + } - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder BubbleUp}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - properties (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - properties (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - PROPERTIES (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - PROPERTIES (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder UnrecognizedCapability}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder UnrecognizedCapability AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - AssemblyInfo.cs (flags: {}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - AssemblyInfo.cs (flags: {}) - """)] - [InlineData( + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {FileSystemEntity Folder}) + Folder (flags: {FileSystemEntity Folder}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {FileSystemEntity Folder AppDesignerFolder BubbleUp}), ExpandedIcon: {259567C1-AA6B-46BF-811C-C145DD9F3B48 29} + Folder (flags: {FileSystemEntity Folder}) + """)] + public void ChangePropertyValues_TreeWithAppDesignerFolder_SetsExpandedIconToExpandedAppDesignerFolder(string input, string expected) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.ExpandedAppDesignerFolder, new ProjectImageMoniker(new Guid("259567C1-AA6B-46BF-811C-C145DD9F3B48"), 29)); + var propertiesProvider = CreateInstance(imageProvider, designerService); + + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); + + Verify(propertiesProvider, expectedTree, inputTree); + } + + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}), Icon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 1}, ExpandedIcon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 2} + Folder (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}), Icon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 1}, ExpandedIcon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 2} + Folder (flags: {Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}), Icon: {}, ExpandedIcon: {} + Folder (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder BubbleUp}), Icon: {}, ExpandedIcon: {} + Folder (flags: {Folder}) + """)] + public void ChangePropertyValues_TreeWithAppDesignerFolderWhenImageProviderReturnsNull_DoesNotSetIconAndExpandedIcon(string input, string expected) + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.AppDesignerFolder, null); + var propertiesProvider = CreateInstance(imageProvider, designerService); + + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); + + Verify(propertiesProvider, expectedTree, inputTree); + } + + [Fact] + public void ChangePropertyValues_ProjectWithNoAppDesignerFolderProperty_DefaultsToProperties() + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); + + var inputTree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) Properties (flags: {Folder}) - Folder (flags: {Folder}) - """, + """); + var expectedTree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) Properties (flags: {Folder AppDesignerFolder BubbleUp}) - Folder (flags: {Folder}) - """)] - public void ChangePropertyValues_TreeWithAppDesignerFolder_ReturnsCandidateMarkedWithAppDesignerFolderAndBubbleUp(string input, string expected) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + """); - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); + Verify(propertiesProvider, expectedTree, inputTree, folderName: null); + } - Verify(propertiesProvider, expectedTree, inputTree); - } + [Fact] + public void ChangePropertyValues_ProjectWithEmptyAppDesignerFolderProperty_DefaultsToProperties() + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - [Theory] - [InlineData( + var inputTree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) - Properties (flags: {FileSystemEntity Folder}) - Folder (flags: {FileSystemEntity Folder}) - """, + Properties (flags: {Folder}) + """); + var expectedTree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) - Properties (flags: {FileSystemEntity Folder AppDesignerFolder BubbleUp}), Icon: {259567C1-AA6B-46BF-811C-C145DD9F3B48 28} - Folder (flags: {FileSystemEntity Folder}) - """)] - public void ChangePropertyValues_TreeWithAppDesignerFolder_SetsIconToAppDesignerFolder(string input, string expected) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.AppDesignerFolder, new ProjectImageMoniker(new Guid("259567C1-AA6B-46BF-811C-C145DD9F3B48"), 28)); - var propertiesProvider = CreateInstance(imageProvider, designerService); + Properties (flags: {Folder AppDesignerFolder BubbleUp}) + """); - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); + Verify(propertiesProvider, expectedTree, inputTree, folderName: ""); + } - Verify(propertiesProvider, expectedTree, inputTree); - } + [Fact] + public void ChangePropertyValues_ProjectWithNonDefaultAppDesignerFolderProperty_ReturnsCandidateMarkedWithAppDesignerFolderAndBubbleUp() + { + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - [Theory] - [InlineData( + var inputTree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) - Properties (flags: {FileSystemEntity Folder}) - Folder (flags: {FileSystemEntity Folder}) - """, + FooBar (flags: {Folder}) + """); + var expectedTree = ProjectTreeParser.Parse( """ Root (flags: {ProjectRoot}) - Properties (flags: {FileSystemEntity Folder AppDesignerFolder BubbleUp}), ExpandedIcon: {259567C1-AA6B-46BF-811C-C145DD9F3B48 29} - Folder (flags: {FileSystemEntity Folder}) - """)] - public void ChangePropertyValues_TreeWithAppDesignerFolder_SetsExpandedIconToExpandedAppDesignerFolder(string input, string expected) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.ExpandedAppDesignerFolder, new ProjectImageMoniker(new Guid("259567C1-AA6B-46BF-811C-C145DD9F3B48"), 29)); - var propertiesProvider = CreateInstance(imageProvider, designerService); + FooBar (flags: {Folder AppDesignerFolder BubbleUp}) + """); - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); - - Verify(propertiesProvider, expectedTree, inputTree); - } + Verify(propertiesProvider, expectedTree, inputTree, folderName: "FooBar"); + } - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}), Icon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 1}, ExpandedIcon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 2} + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder BubbleUp}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + my project (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + my project (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + MY PROJECT (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + MY PROJECT (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder UnrecognizedCapability}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder UnrecognizedCapability AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + My Project (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + My Project (flags: {Folder VisibleOnlyInShowAllFiles}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + Folder (flags: {IncludeInProjectCandidate}) + Item.cs (flags: {IncludeInProjectCandidate}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + Folder (flags: {IncludeInProjectCandidate}) + Item.cs (flags: {IncludeInProjectCandidate}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + Folder1 (flags: {IncludeInProjectCandidate}) + Item.cs (flags: {IncludeInProjectCandidate}) + Folder2 (flags: {Folder}) + Item.cs (flags: {}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + Folder1 (flags: {IncludeInProjectCandidate}) + Item.cs (flags: {IncludeInProjectCandidate}) + Folder2 (flags: {Folder VisibleOnlyInShowAllFiles}) + Item.cs (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + Resources.resx (flags: {}) + Resources.Designer.cs (flags: {}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + Resources.resx (flags: {VisibleOnlyInShowAllFiles}) + Resources.Designer.cs (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + AssemblyInfo.cs (flags: {}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + AssemblyInfo.cs (flags: {VisibleOnlyInShowAllFiles}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + Folder (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + Folder (flags: {Folder VisibleOnlyInShowAllFiles}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + Folder (flags: {Folder}) Folder (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}), Icon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 1}, ExpandedIcon: {AE27A6B0-E345-4288-96DF-5EAF394EE369 2} + File (flags: {}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + Folder (flags: {Folder VisibleOnlyInShowAllFiles}) Folder (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}), Icon: {}, ExpandedIcon: {} + File (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder}) + Folder1 (flags: {Folder}) Folder (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}), Icon: {}, ExpandedIcon: {} + File (flags: {}) + Folder2 (flags: {Folder}) Folder (flags: {Folder}) - """)] - public void ChangePropertyValues_TreeWithAppDesignerFolderWhenImageProviderReturnsNull_DoesNotSetIconAndExpandedIcon(string input, string expected) - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.AppDesignerFolder, null); - var propertiesProvider = CreateInstance(imageProvider, designerService); - - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); + File (flags: {}) + """, + """ + Root (flags: {ProjectRoot}) + My Project (flags: {Folder AppDesignerFolder BubbleUp}) + Folder1 (flags: {Folder VisibleOnlyInShowAllFiles}) + Folder (flags: {Folder}) + File (flags: {}) + Folder2 (flags: {Folder VisibleOnlyInShowAllFiles}) + Folder (flags: {Folder}) + File (flags: {}) + """)] + public void ChangePropertyValues_TreeWithMyProjectCandidateAndContentVisibleOnlyInShowAllFiles_ReturnsCandidateMarkedWithAppDesignerFolderAndBubbleUp(string input, string expected) + { // Mimic's Visual Basic projects + var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); + var propertiesProvider = CreateInstance(designerService); - Verify(propertiesProvider, expectedTree, inputTree); - } + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); - [Fact] - public void ChangePropertyValues_ProjectWithNoAppDesignerFolderProperty_DefaultsToProperties() - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); - - var inputTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - """); - var expectedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """); - - Verify(propertiesProvider, expectedTree, inputTree, folderName: null); - } - - [Fact] - public void ChangePropertyValues_ProjectWithEmptyAppDesignerFolderProperty_DefaultsToProperties() - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); + Verify(propertiesProvider, expectedTree, inputTree, folderName: "My Project", contentOnlyVisibleInShowAllFiles: true); + } - var inputTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - """); - var expectedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder BubbleUp}) - """); - - Verify(propertiesProvider, expectedTree, inputTree, folderName: ""); - } - - [Fact] - public void ChangePropertyValues_ProjectWithNonDefaultAppDesignerFolderProperty_ReturnsCandidateMarkedWithAppDesignerFolderAndBubbleUp() - { - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); - - var inputTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - FooBar (flags: {Folder}) - """); - var expectedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - FooBar (flags: {Folder AppDesignerFolder BubbleUp}) - """); - - Verify(propertiesProvider, expectedTree, inputTree, folderName: "FooBar"); - } - - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder BubbleUp}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - my project (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - my project (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - MY PROJECT (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - MY PROJECT (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder UnrecognizedCapability}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder UnrecognizedCapability AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - My Project (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - My Project (flags: {Folder VisibleOnlyInShowAllFiles}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - AssemblyInfo.cs (flags: {IncludeInProjectCandidate}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - Folder (flags: {IncludeInProjectCandidate}) - Item.cs (flags: {IncludeInProjectCandidate}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - Folder (flags: {IncludeInProjectCandidate}) - Item.cs (flags: {IncludeInProjectCandidate}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - Folder1 (flags: {IncludeInProjectCandidate}) - Item.cs (flags: {IncludeInProjectCandidate}) - Folder2 (flags: {Folder}) - Item.cs (flags: {}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - Folder1 (flags: {IncludeInProjectCandidate}) - Item.cs (flags: {IncludeInProjectCandidate}) - Folder2 (flags: {Folder VisibleOnlyInShowAllFiles}) - Item.cs (flags: {}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - Resources.resx (flags: {}) - Resources.Designer.cs (flags: {}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - Resources.resx (flags: {VisibleOnlyInShowAllFiles}) - Resources.Designer.cs (flags: {}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - AssemblyInfo.cs (flags: {}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - AssemblyInfo.cs (flags: {VisibleOnlyInShowAllFiles}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - Folder (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - Folder (flags: {Folder VisibleOnlyInShowAllFiles}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - Folder (flags: {Folder}) - Folder (flags: {Folder}) - File (flags: {}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - Folder (flags: {Folder VisibleOnlyInShowAllFiles}) - Folder (flags: {Folder}) - File (flags: {}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder}) - Folder1 (flags: {Folder}) - Folder (flags: {Folder}) - File (flags: {}) - Folder2 (flags: {Folder}) - Folder (flags: {Folder}) - File (flags: {}) - """, - """ - Root (flags: {ProjectRoot}) - My Project (flags: {Folder AppDesignerFolder BubbleUp}) - Folder1 (flags: {Folder VisibleOnlyInShowAllFiles}) - Folder (flags: {Folder}) - File (flags: {}) - Folder2 (flags: {Folder VisibleOnlyInShowAllFiles}) - Folder (flags: {Folder}) - File (flags: {}) - """)] - public void ChangePropertyValues_TreeWithMyProjectCandidateAndContentVisibleOnlyInShowAllFiles_ReturnsCandidateMarkedWithAppDesignerFolderAndBubbleUp(string input, string expected) - { // Mimic's Visual Basic projects - var designerService = IProjectDesignerServiceFactory.ImplementSupportsProjectDesigner(() => true); - var propertiesProvider = CreateInstance(designerService); - - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); - - Verify(propertiesProvider, expectedTree, inputTree, folderName: "My Project", contentOnlyVisibleInShowAllFiles: true); - } - - internal static void Verify(AppDesignerFolderProjectTreePropertiesProvider provider, IProjectTree expected, IProjectTree input, string? folderName = null, bool? contentOnlyVisibleInShowAllFiles = null) - { - IImmutableDictionary projectTreeSettings = ImmutableStringDictionary.EmptyOrdinal; - IImmutableDictionary ruleSnapshots = IProjectRuleSnapshotsFactory.Create(); + internal static void Verify(AppDesignerFolderProjectTreePropertiesProvider provider, IProjectTree expected, IProjectTree input, string? folderName = null, bool? contentOnlyVisibleInShowAllFiles = null) + { + IImmutableDictionary projectTreeSettings = ImmutableStringDictionary.EmptyOrdinal; + IImmutableDictionary ruleSnapshots = IProjectRuleSnapshotsFactory.Create(); - if (folderName is not null) - ruleSnapshots = ruleSnapshots.Add(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, folderName); + if (folderName is not null) + ruleSnapshots = ruleSnapshots.Add(AppDesigner.SchemaName, AppDesigner.FolderNameProperty, folderName); - if (contentOnlyVisibleInShowAllFiles != null) - ruleSnapshots = ruleSnapshots.Add(AppDesigner.SchemaName, AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty, contentOnlyVisibleInShowAllFiles.Value.ToString()); + if (contentOnlyVisibleInShowAllFiles != null) + ruleSnapshots = ruleSnapshots.Add(AppDesigner.SchemaName, AppDesigner.ContentsVisibleOnlyInShowAllFilesProperty, contentOnlyVisibleInShowAllFiles.Value.ToString()); - provider.UpdateProjectTreeSettings(ruleSnapshots, ref projectTreeSettings); + provider.UpdateProjectTreeSettings(ruleSnapshots, ref projectTreeSettings); - IProjectTree result = provider.ChangePropertyValuesForEntireTree(input, projectTreeSettings); + IProjectTree result = provider.ChangePropertyValuesForEntireTree(input, projectTreeSettings); - AssertAreEquivalent(expected, result); - } + AssertAreEquivalent(expected, result); + } - private static void AssertAreEquivalent(IProjectTree expected, IProjectTree actual) - { - Assert.NotSame(expected, actual); + private static void AssertAreEquivalent(IProjectTree expected, IProjectTree actual) + { + Assert.NotSame(expected, actual); - string expectedAsString = ProjectTreeWriter.WriteToString(expected); - string actualAsString = ProjectTreeWriter.WriteToString(actual); + string expectedAsString = ProjectTreeWriter.WriteToString(expected); + string actualAsString = ProjectTreeWriter.WriteToString(actual); - Assert.Equal(expectedAsString, actualAsString); - } + Assert.Equal(expectedAsString, actualAsString); + } - private static AppDesignerFolderProjectTreePropertiesProvider CreateInstance() - { - return CreateInstance(null, null); - } + private static AppDesignerFolderProjectTreePropertiesProvider CreateInstance() + { + return CreateInstance(null, null); + } - private static AppDesignerFolderProjectTreePropertiesProvider CreateInstance(IProjectDesignerService designerService) - { - return CreateInstance(null, designerService); - } + private static AppDesignerFolderProjectTreePropertiesProvider CreateInstance(IProjectDesignerService designerService) + { + return CreateInstance(null, designerService); + } - private static AppDesignerFolderProjectTreePropertiesProvider CreateInstance(IProjectImageProvider? imageProvider, IProjectDesignerService? designerService) - { - return new AppDesignerFolderProjectTreePropertiesProvider( - imageProvider ?? IProjectImageProviderFactory.Create(), - designerService ?? IProjectDesignerServiceFactory.Create()); - } + private static AppDesignerFolderProjectTreePropertiesProvider CreateInstance(IProjectImageProvider? imageProvider, IProjectDesignerService? designerService) + { + return new AppDesignerFolderProjectTreePropertiesProvider( + imageProvider ?? IProjectImageProviderFactory.Create(), + designerService ?? IProjectDesignerServiceFactory.Create()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Order/TreeItemOrderPropertyProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Order/TreeItemOrderPropertyProviderTests.cs index cdc36d120b..dc04aa5b9b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Order/TreeItemOrderPropertyProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Order/TreeItemOrderPropertyProviderTests.cs @@ -2,267 +2,266 @@ using Microsoft.VisualStudio.ProjectSystem.References; -namespace Microsoft.VisualStudio.ProjectSystem.Tree.Order +namespace Microsoft.VisualStudio.ProjectSystem.Tree.Order; + +public class TreeItemOrderPropertyProviderTests { - public class TreeItemOrderPropertyProviderTests + private readonly List<(string type, string include)> _simpleOrderFile = new() { - private readonly List<(string type, string include)> _simpleOrderFile = new() - { - ("Compile", "Order.fs"), - ("Compile", "Customer.fs"), - ("Compile", "Program.fs") - }; - - [Theory] - [InlineData("Customer.fs", "Compile", false, "X:\\Project\\Customer.fs", 2)] - [InlineData("order.fs", "Compile", false, "X:\\Project\\order.fs", 1)] // case insensitive - [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", 3)] - [InlineData("Misc.txt", "Content", false, "X:\\Project\\Misc.txt", int.MaxValue)] // unknown type - [InlineData("ordered.fsproj", null, false, "X:\\Project\\ordered.fsproj", int.MaxValue)] // hidden file - [InlineData("Debug", null, true, null, 0)] // unknown folder - public void VerifySimpleOrderedUnderProjectRoot(string itemName, string? itemType, bool isFolder, string? rootedPath, int expectedOrder) - { - var orderedItems = _simpleOrderFile - .Select(p => new ProjectItemIdentity(p.type, p.include)) - .ToList(); + ("Compile", "Order.fs"), + ("Compile", "Customer.fs"), + ("Compile", "Program.fs") + }; + + [Theory] + [InlineData("Customer.fs", "Compile", false, "X:\\Project\\Customer.fs", 2)] + [InlineData("order.fs", "Compile", false, "X:\\Project\\order.fs", 1)] // case insensitive + [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", 3)] + [InlineData("Misc.txt", "Content", false, "X:\\Project\\Misc.txt", int.MaxValue)] // unknown type + [InlineData("ordered.fsproj", null, false, "X:\\Project\\ordered.fsproj", int.MaxValue)] // hidden file + [InlineData("Debug", null, true, null, 0)] // unknown folder + public void VerifySimpleOrderedUnderProjectRoot(string itemName, string? itemType, bool isFolder, string? rootedPath, int expectedOrder) + { + var orderedItems = _simpleOrderFile + .Select(p => new ProjectItemIdentity(p.type, p.include)) + .ToList(); - var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); + var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); - var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); + var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); - var context = GetContext(itemName, itemType, isFolder, ProjectTreeFlags.ProjectRoot, metadata); - var values = GetInitialValues(); + var context = GetContext(itemName, itemType, isFolder, ProjectTreeFlags.ProjectRoot, metadata); + var values = GetInitialValues(); - provider.CalculatePropertyValues(context, values); + provider.CalculatePropertyValues(context, values); - Assert.Equal(expectedOrder, values.DisplayOrder); - } + Assert.Equal(expectedOrder, values.DisplayOrder); + } - private readonly List<(string type, string include)> _simpleOrderFileDuplicate = new() - { - ("Compile", "Order.fs"), - ("Compile", "Customer.fs"), - ("Compile", "Program.fs"), - ("Compile", "Program.fs") - }; - - [Theory] - [InlineData("Customer.fs", "Compile", false, "X:\\Project\\Customer.fs", 2)] - [InlineData("order.fs", "Compile", false, "X:\\Project\\order.fs", 1)] // case insensitive - [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", 3)] - [InlineData("Misc.txt", "Content", false, "X:\\Project\\Misc.txt", int.MaxValue)] // unknown type - [InlineData("ordered.fsproj", null, false, "X:\\Project\\ordered.fsproj", int.MaxValue)] // hidden file - [InlineData("Debug", null, true, null, 0)] // unknown folder - public void VerifySimpleOrderedUnderProjectRootDuplicate(string itemName, string? itemType, bool isFolder, string? rootedPath, int expectedOrder) - { - var orderedItems = _simpleOrderFileDuplicate - .Select(p => new ProjectItemIdentity(p.type, p.include)) - .ToList(); + private readonly List<(string type, string include)> _simpleOrderFileDuplicate = new() + { + ("Compile", "Order.fs"), + ("Compile", "Customer.fs"), + ("Compile", "Program.fs"), + ("Compile", "Program.fs") + }; + + [Theory] + [InlineData("Customer.fs", "Compile", false, "X:\\Project\\Customer.fs", 2)] + [InlineData("order.fs", "Compile", false, "X:\\Project\\order.fs", 1)] // case insensitive + [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", 3)] + [InlineData("Misc.txt", "Content", false, "X:\\Project\\Misc.txt", int.MaxValue)] // unknown type + [InlineData("ordered.fsproj", null, false, "X:\\Project\\ordered.fsproj", int.MaxValue)] // hidden file + [InlineData("Debug", null, true, null, 0)] // unknown folder + public void VerifySimpleOrderedUnderProjectRootDuplicate(string itemName, string? itemType, bool isFolder, string? rootedPath, int expectedOrder) + { + var orderedItems = _simpleOrderFileDuplicate + .Select(p => new ProjectItemIdentity(p.type, p.include)) + .ToList(); - var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); + var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); - var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); + var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); - var context = GetContext(itemName, itemType, isFolder, ProjectTreeFlags.ProjectRoot, metadata); - var values = GetInitialValues(); + var context = GetContext(itemName, itemType, isFolder, ProjectTreeFlags.ProjectRoot, metadata); + var values = GetInitialValues(); - provider.CalculatePropertyValues(context, values); + provider.CalculatePropertyValues(context, values); - Assert.Equal(expectedOrder, values.DisplayOrder); - } + Assert.Equal(expectedOrder, values.DisplayOrder); + } - [Theory] - [MemberData(nameof(TestTreeItems))] - public void VerifyOrderIncreasesMonotonically( - List<(string type, string include)> orderedFileInput, - List<(string itemName, string? itemType, bool isFolder, bool isUnderProjectRoot, string? rootedPath)> solutionTree) - { - var orderedItems = orderedFileInput - .Select(p => new ProjectItemIdentity(p.type, p.include)) - .ToList(); + [Theory] + [MemberData(nameof(TestTreeItems))] + public void VerifyOrderIncreasesMonotonically( + List<(string type, string include)> orderedFileInput, + List<(string itemName, string? itemType, bool isFolder, bool isUnderProjectRoot, string? rootedPath)> solutionTree) + { + var orderedItems = orderedFileInput + .Select(p => new ProjectItemIdentity(p.type, p.include)) + .ToList(); - var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); + var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); - var lastOrder = 0; - solutionTree.ForEach(item => - { - var rootedPath = item.rootedPath; - var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); + var lastOrder = 0; + solutionTree.ForEach(item => + { + var rootedPath = item.rootedPath; + var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); - var context = GetContext(item.itemName, item.itemType, item.isFolder, - item.isUnderProjectRoot ? ProjectTreeFlags.ProjectRoot : ProjectTreeFlags.Empty, metadata); - var values = GetInitialValues(); + var context = GetContext(item.itemName, item.itemType, item.isFolder, + item.isUnderProjectRoot ? ProjectTreeFlags.ProjectRoot : ProjectTreeFlags.Empty, metadata); + var values = GetInitialValues(); - provider.CalculatePropertyValues(context, values); + provider.CalculatePropertyValues(context, values); - Assert.True(values.DisplayOrder >= lastOrder); - lastOrder = values.DisplayOrder; - }); + Assert.True(values.DisplayOrder >= lastOrder); + lastOrder = values.DisplayOrder; + }); - Assert.True(lastOrder >= orderedFileInput.Count); - } + Assert.True(lastOrder >= orderedFileInput.Count); + } - public static IEnumerable TestTreeItems + public static IEnumerable TestTreeItems + { + get { - get + return new[] { - return new[] + // 1. simple ordering with no folders in evaluated include + new object[] { - // 1. simple ordering with no folders in evaluated include - new object[] + new List<(string type, string include)> { - new List<(string type, string include)> - { - ("Compile", "Order.fs"), - ("Compile", "Customer.fs"), - ("Compile", "Program.fs") - }, - new List<(string itemName, string? itemType, bool isFolder, bool isUnderProjectRoot, string? rootedPath)> - { - // unknown folders and their nested items - ("Debug", null, true, true, "X:\\Project\\Debug"), - ("bin", null, true, false, "X:\\Project\\bin"), - - // included items - ("Order.fs", "Compile", false, true, "X:\\Project\\Order.fs"), - ("Customer.fs", "Compile", false, true, "X:\\Project\\Customer.fs"), - ("Program.fs", "Compile", false, true, "X:\\Project\\Program.fs"), - - // hidden or other items under project root - ("profile.png", "Content", false, true, "X:\\Project\\profile.png"), - ("app.fsproj", null, false, true, "X:\\Project\\app.fsproj"), - ("app.sln", null, false, true, "X:\\Project\\app.sln") - } + ("Compile", "Order.fs"), + ("Compile", "Customer.fs"), + ("Compile", "Program.fs") }, + new List<(string itemName, string? itemType, bool isFolder, bool isUnderProjectRoot, string? rootedPath)> + { + // unknown folders and their nested items + ("Debug", null, true, true, "X:\\Project\\Debug"), + ("bin", null, true, false, "X:\\Project\\bin"), + + // included items + ("Order.fs", "Compile", false, true, "X:\\Project\\Order.fs"), + ("Customer.fs", "Compile", false, true, "X:\\Project\\Customer.fs"), + ("Program.fs", "Compile", false, true, "X:\\Project\\Program.fs"), + + // hidden or other items under project root + ("profile.png", "Content", false, true, "X:\\Project\\profile.png"), + ("app.fsproj", null, false, true, "X:\\Project\\app.fsproj"), + ("app.sln", null, false, true, "X:\\Project\\app.sln") + } + }, - // 2. nested ordering with folders that should also be ordered - new object[] + // 2. nested ordering with folders that should also be ordered + new object[] + { + new List<(string type, string include)> + { + ("Compile", "Order/Order.fs"), + ("Compile", "Customer/Postcode.fs"), + ("Compile", "Customer\\Customer.fs"), + ("Compile", "Customer/Telemetry/Data.fs"), + ("Compile", "Customer/Address.fs"), + ("Content", "Bio.png"), + ("Compile", "Program.fs"), + }, + new List<(string itemName, string? itemType, bool isFolder, bool isUnderProjectRoot, string? rootedPath)> { - new List<(string type, string include)> - { - ("Compile", "Order/Order.fs"), - ("Compile", "Customer/Postcode.fs"), - ("Compile", "Customer\\Customer.fs"), - ("Compile", "Customer/Telemetry/Data.fs"), - ("Compile", "Customer/Address.fs"), - ("Content", "Bio.png"), - ("Compile", "Program.fs"), - }, - new List<(string itemName, string? itemType, bool isFolder, bool isUnderProjectRoot, string? rootedPath)> - { - // rootedPath is set to null for folders as we never get any metadata for folders in reality. - - // unknown folders - (".vs", "Folder", true, false, null), - ("bin", "Folder", true, true, null), - ("netcoreapp2.0", "Folder", true, false, null), - ("obj", "Folder", true, true, null), - - // included items - ("Order", "Folder", true, true, null), - ("Order.fs", "Compile", false, false, "X:\\Project\\Order\\Order.fs"), - ("Customer", "Folder", true, true, null), - ("Postcode.fs", "Compile", false, false, "X:\\Project\\Customer\\Postcode.fs"), - ("Customer.fs", "Compile", false, false, "X:\\Project\\Customer\\Customer.fs"), - ("Telemetry", "Folder", true, false, null), - ("Data.fs", "Compile", false, false, "X:\\Project\\Customer\\Telemetry\\Data.fs"), - ("Address.fs", "Compile", false, false, "X:\\Project\\Customer\\Address.fs"), - ("Bio.png", "Content", false, true, "X:\\Project\\Bio.png"), - ("Program.fs", "Compile", false, true, "X:\\Project\\Program.fs"), - - // hidden or other items under project root - ("app.fsproj", null, false, true, "X:\\Project\\app.fsproj"), - ("app.sln", null, false, true, "X:\\Project\\app.sln") - } + // rootedPath is set to null for folders as we never get any metadata for folders in reality. + + // unknown folders + (".vs", "Folder", true, false, null), + ("bin", "Folder", true, true, null), + ("netcoreapp2.0", "Folder", true, false, null), + ("obj", "Folder", true, true, null), + + // included items + ("Order", "Folder", true, true, null), + ("Order.fs", "Compile", false, false, "X:\\Project\\Order\\Order.fs"), + ("Customer", "Folder", true, true, null), + ("Postcode.fs", "Compile", false, false, "X:\\Project\\Customer\\Postcode.fs"), + ("Customer.fs", "Compile", false, false, "X:\\Project\\Customer\\Customer.fs"), + ("Telemetry", "Folder", true, false, null), + ("Data.fs", "Compile", false, false, "X:\\Project\\Customer\\Telemetry\\Data.fs"), + ("Address.fs", "Compile", false, false, "X:\\Project\\Customer\\Address.fs"), + ("Bio.png", "Content", false, true, "X:\\Project\\Bio.png"), + ("Program.fs", "Compile", false, true, "X:\\Project\\Program.fs"), + + // hidden or other items under project root + ("app.fsproj", null, false, true, "X:\\Project\\app.fsproj"), + ("app.sln", null, false, true, "X:\\Project\\app.sln") } - }; - } + } + }; } + } - private readonly List<(string type, string include)> _orderedWithDups = new() - { - ("Compile", "Common.fs"), - ("Compile", "Tables\\Order.fs"), - ("Compile", "Tables\\Common.fs"), - ("Compile", "Program.fs") - }; - - [Theory] - [InlineData("Common.fs", "Compile", false, "X:\\Project\\Common.fs", 1)] - [InlineData("Tables", "Folder", true, null, 2)] - [InlineData("Order.fs", "Compile", false, "X:\\Project\\Tables\\Order.fs", 3)] - [InlineData("Common.fs", "Compile", false, "X:\\Project\\Tables\\Common.fs", 4)] // duplicate and out of alphabetical order - [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", 5)] - public void VerifyOrderingWithDuplicateFiles(string itemName, string itemType, bool isFolder, string? rootedPath, int expectedOrder) - { - var orderedItems = _orderedWithDups - .Select(p => new ProjectItemIdentity(p.type, p.include)) - .ToList(); - - var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); + private readonly List<(string type, string include)> _orderedWithDups = new() + { + ("Compile", "Common.fs"), + ("Compile", "Tables\\Order.fs"), + ("Compile", "Tables\\Common.fs"), + ("Compile", "Program.fs") + }; + + [Theory] + [InlineData("Common.fs", "Compile", false, "X:\\Project\\Common.fs", 1)] + [InlineData("Tables", "Folder", true, null, 2)] + [InlineData("Order.fs", "Compile", false, "X:\\Project\\Tables\\Order.fs", 3)] + [InlineData("Common.fs", "Compile", false, "X:\\Project\\Tables\\Common.fs", 4)] // duplicate and out of alphabetical order + [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", 5)] + public void VerifyOrderingWithDuplicateFiles(string itemName, string itemType, bool isFolder, string? rootedPath, int expectedOrder) + { + var orderedItems = _orderedWithDups + .Select(p => new ProjectItemIdentity(p.type, p.include)) + .ToList(); - bool isUnderProjectRoot = rootedPath?.Contains("Tables") != true; - var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); + var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); - var context = GetContext(itemName, itemType, isFolder, - isUnderProjectRoot ? ProjectTreeFlags.ProjectRoot : ProjectTreeFlags.Empty, - metadata); - var values = GetInitialValues(); + bool isUnderProjectRoot = rootedPath?.Contains("Tables") != true; + var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath } }.ToImmutableDictionary(); - provider.CalculatePropertyValues(context, values); + var context = GetContext(itemName, itemType, isFolder, + isUnderProjectRoot ? ProjectTreeFlags.ProjectRoot : ProjectTreeFlags.Empty, + metadata); + var values = GetInitialValues(); - Assert.Equal(expectedOrder, values.DisplayOrder); - } + provider.CalculatePropertyValues(context, values); - private readonly List<(string type, string include, string? linkPath)> _orderedWithLinkPaths = new() - { - ("Compile", "Common.fs", "Tables/Test.fs"), - ("Compile", "Tables\\Order.fs", null), - ("Compile", "Tables\\Common.fs", null), - ("Compile", "Program.fs", null) - }; - - [Theory] - [InlineData("Common.fs", "Compile", false, "X:\\Project\\Common.fs", "Tables/Test.fs", 2)] // Our link path - [InlineData("Tables", "Folder", true, null, null, 1)] - [InlineData("Order.fs", "Compile", false, "X:\\Project\\Tables\\Order.fs", null, 3)] - [InlineData("Common.fs", "Compile", false, "X:\\Project\\Tables\\Common.fs", null, 4)] // duplicate and out of alphabetical order - [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", null, 5)] - public void VerifyOrderingWithLinkPaths(string itemName, string itemType, bool isFolder, string? rootedPath, string? linkPath, int expectedOrder) - { - var orderedItems = _orderedWithLinkPaths - .Select(p => new ProjectItemIdentity(p.type, p.include, p.linkPath)) - .ToList(); + Assert.Equal(expectedOrder, values.DisplayOrder); + } - var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); + private readonly List<(string type, string include, string? linkPath)> _orderedWithLinkPaths = new() + { + ("Compile", "Common.fs", "Tables/Test.fs"), + ("Compile", "Tables\\Order.fs", null), + ("Compile", "Tables\\Common.fs", null), + ("Compile", "Program.fs", null) + }; + + [Theory] + [InlineData("Common.fs", "Compile", false, "X:\\Project\\Common.fs", "Tables/Test.fs", 2)] // Our link path + [InlineData("Tables", "Folder", true, null, null, 1)] + [InlineData("Order.fs", "Compile", false, "X:\\Project\\Tables\\Order.fs", null, 3)] + [InlineData("Common.fs", "Compile", false, "X:\\Project\\Tables\\Common.fs", null, 4)] // duplicate and out of alphabetical order + [InlineData("Program.fs", "Compile", false, "X:\\Project\\Program.fs", null, 5)] + public void VerifyOrderingWithLinkPaths(string itemName, string itemType, bool isFolder, string? rootedPath, string? linkPath, int expectedOrder) + { + var orderedItems = _orderedWithLinkPaths + .Select(p => new ProjectItemIdentity(p.type, p.include, p.linkPath)) + .ToList(); - bool isUnderProjectRoot = rootedPath?.Contains("Tables") != true; - var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath }, { "Link", linkPath ?? "" } }.ToImmutableDictionary(); + var provider = new TreeItemOrderPropertyProvider(orderedItems, UnconfiguredProjectFactory.Create(fullPath: "X:\\Project\\")); - var context = GetContext(itemName, itemType, isFolder, - isUnderProjectRoot ? ProjectTreeFlags.ProjectRoot : ProjectTreeFlags.Empty, - metadata); - var values = GetInitialValues(); + bool isUnderProjectRoot = rootedPath?.Contains("Tables") != true; + var metadata = rootedPath is null ? null : new Dictionary { { "FullPath", rootedPath }, { "Link", linkPath ?? "" } }.ToImmutableDictionary(); - provider.CalculatePropertyValues(context, values); + var context = GetContext(itemName, itemType, isFolder, + isUnderProjectRoot ? ProjectTreeFlags.ProjectRoot : ProjectTreeFlags.Empty, + metadata); + var values = GetInitialValues(); - Assert.Equal(expectedOrder, values.DisplayOrder); - } + provider.CalculatePropertyValues(context, values); - private static IProjectTreeCustomizablePropertyContext GetContext( - string? itemName = null, - string? itemType = null, - bool isFolder = false, - ProjectTreeFlags flags = default, - IImmutableDictionary? metadata = null) - => IProjectTreeCustomizablePropertyContextFactory.Implement( - itemName: itemName, - itemType: itemType, - isFolder: isFolder, - flags: flags, - metadata: metadata); - - private static ReferencesProjectTreeCustomizablePropertyValues GetInitialValues() - => new(); + Assert.Equal(expectedOrder, values.DisplayOrder); } + + private static IProjectTreeCustomizablePropertyContext GetContext( + string? itemName = null, + string? itemType = null, + bool isFolder = false, + ProjectTreeFlags flags = default, + IImmutableDictionary? metadata = null) + => IProjectTreeCustomizablePropertyContextFactory.Implement( + itemName: itemName, + itemType: itemType, + isFolder: isFolder, + flags: flags, + metadata: metadata); + + private static ReferencesProjectTreeCustomizablePropertyValues GetInitialValues() + => new(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/ProjectRootImageProjectTreeModifierTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/ProjectRootImageProjectTreeModifierTests.cs index 691b21f531..86e254b53f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/ProjectRootImageProjectTreeModifierTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/ProjectRootImageProjectTreeModifierTests.cs @@ -2,230 +2,229 @@ using Microsoft.VisualStudio.ProjectSystem.Imaging; -namespace Microsoft.VisualStudio.ProjectSystem.Tree +namespace Microsoft.VisualStudio.ProjectSystem.Tree; + +public class ProjectRootImageProjectTreeModifierTests { - public class ProjectRootImageProjectTreeModifierTests + [Fact] + public void CalculatePropertyValues_NullAsPropertyContext_ThrowsArgumentNull() { - [Fact] - public void CalculatePropertyValues_NullAsPropertyContext_ThrowsArgumentNull() + var propertyValues = IProjectTreeCustomizablePropertyValuesFactory.Create(); + var propertiesProvider = CreateInstance(); + + Assert.Throws("propertyContext", () => { - var propertyValues = IProjectTreeCustomizablePropertyValuesFactory.Create(); - var propertiesProvider = CreateInstance(); + propertiesProvider.CalculatePropertyValues(null!, propertyValues); + }); + } - Assert.Throws("propertyContext", () => - { - propertiesProvider.CalculatePropertyValues(null!, propertyValues); - }); - } + [Fact] + public void CalculatePropertyValues_NullAsPropertyValues_ThrowsArgumentNull() + { + var propertyContext = IProjectTreeCustomizablePropertyContextFactory.Create(); + var propertiesProvider = CreateInstance(); - [Fact] - public void CalculatePropertyValues_NullAsPropertyValues_ThrowsArgumentNull() - { - var propertyContext = IProjectTreeCustomizablePropertyContextFactory.Create(); - var propertiesProvider = CreateInstance(); - - Assert.Throws("propertyValues", () => - { - propertiesProvider.CalculatePropertyValues(propertyContext, null!); - }); - } - - [Theory] - [InlineData( - """ - Root (flags: {Unrecognized ProjectRoot}) - """, - """ - Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - """)] - [InlineData( - """ - Root (flags: {Unrecognized ProjectRoot}) - Folder (flags: {Folder}) - """, - """ - Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - Folder (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - Folder (flags: {Folder}) - """)] - public void CalculatePropertyValues_ProjectRootAsTree_SetsIconToProjectRoot(string input, string expected) - { - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.ProjectRoot, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); - - var propertiesProvider = CreateInstance(imageProvider); - - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); - var result = propertiesProvider.ChangePropertyValuesForEntireTree(inputTree); - - AssertAreEquivalent(expectedTree, result); - } - - [Theory] - [InlineData( - """ - Root (flags: {Unrecognized ProjectRoot}) - """, - """ - Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - """)] - [InlineData( - """ - Root (flags: {Unrecognized ProjectRoot}) - Folder (flags: {Folder}) - """, - """ - Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - Folder (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder}) - """, - """ - Root (flags: {ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - Folder (flags: {Folder}) - """)] - public void CalculatePropertyValues_WhenSharedProjectRootAsTree_SetsIconToSharedProjectRoot(string input, string expected) + Assert.Throws("propertyValues", () => { - var capabilities = IProjectCapabilitiesServiceFactory.ImplementsContains(capability => - { - return capability == ProjectCapabilities.SharedAssetsProject; - }); - - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.SharedProjectRoot, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); - - var propertiesProvider = CreateInstance(capabilities, imageProvider); - - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); - var result = propertiesProvider.ChangePropertyValuesForEntireTree(inputTree); - - AssertAreEquivalent(expectedTree, result); - } - - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Shared.items (flags: {SharedItemsImportFile}) - """, - """ - Root (flags: {ProjectRoot}) - Shared.items (flags: {SharedItemsImportFile}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Shared.items (flags: {SharedItemsImportFile Unrecognized}) - """, - """ - Root (flags: {ProjectRoot}) - Shared.items (flags: {SharedItemsImportFile Unrecognized}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} - """)] - public void CalculatePropertyValues_WhenSharedItemsImportFileAsTree_SetsIconToSharedItemsImportFile(string input, string expected) - { - var capabilities = IProjectCapabilitiesServiceFactory.ImplementsContains(capability => - { - return capability == ProjectCapabilities.SharedAssetsProject; - }); - - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.SharedItemsImportFile, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); - - var propertiesProvider = CreateInstance(capabilities, imageProvider); - - var inputTree = ProjectTreeParser.Parse(input); - var expectedTree = ProjectTreeParser.Parse(expected); - var result = propertiesProvider.ChangePropertyValuesForEntireTree(inputTree); - - AssertAreEquivalent(expectedTree, result); - } - - [Theory] - [InlineData( - """ - Root (flags: {ProjectRoot}) - File (flags: {}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - File (flags: {IncludeInProjectCandidate}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder}) - """)] - [InlineData( - """ - Root (flags: {ProjectRoot}) - Folder (flags: {Folder IncludeInProjectCandidate}) - """)] - public void CalculatePropertyValues_NonProjectRootAsTree_DoesNotSetIcon(string input) + propertiesProvider.CalculatePropertyValues(propertyContext, null!); + }); + } + + [Theory] + [InlineData( + """ + Root (flags: {Unrecognized ProjectRoot}) + """, + """ + Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + """)] + [InlineData( + """ + Root (flags: {Unrecognized ProjectRoot}) + Folder (flags: {Folder}) + """, + """ + Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + Folder (flags: {Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + Folder (flags: {Folder}) + """)] + public void CalculatePropertyValues_ProjectRootAsTree_SetsIconToProjectRoot(string input, string expected) + { + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.ProjectRoot, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); + + var propertiesProvider = CreateInstance(imageProvider); + + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); + var result = propertiesProvider.ChangePropertyValuesForEntireTree(inputTree); + + AssertAreEquivalent(expectedTree, result); + } + + [Theory] + [InlineData( + """ + Root (flags: {Unrecognized ProjectRoot}) + """, + """ + Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + """)] + [InlineData( + """ + Root (flags: {Unrecognized ProjectRoot}) + Folder (flags: {Folder}) + """, + """ + Root (flags: {Unrecognized ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + Folder (flags: {Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + """, + """ + Root (flags: {ProjectRoot}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + Folder (flags: {Folder}) + """)] + public void CalculatePropertyValues_WhenSharedProjectRootAsTree_SetsIconToSharedProjectRoot(string input, string expected) + { + var capabilities = IProjectCapabilitiesServiceFactory.ImplementsContains(capability => { - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.ProjectRoot, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); + return capability == ProjectCapabilities.SharedAssetsProject; + }); - var propertiesProvider = CreateInstance(imageProvider); + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.SharedProjectRoot, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); - var tree = ProjectTreeParser.Parse(input); - var result = propertiesProvider.ChangePropertyValuesForEntireTree(tree.Children[0]); + var propertiesProvider = CreateInstance(capabilities, imageProvider); - Assert.Null(result.Icon); - } + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); + var result = propertiesProvider.ChangePropertyValuesForEntireTree(inputTree); - [Fact] - public void CalculatePropertyValues_ProjectRootAsTreeWhenImageProviderReturnsNull_DoesNotSetIcon() + AssertAreEquivalent(expectedTree, result); + } + + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Shared.items (flags: {SharedItemsImportFile}) + """, + """ + Root (flags: {ProjectRoot}) + Shared.items (flags: {SharedItemsImportFile}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Shared.items (flags: {SharedItemsImportFile Unrecognized}) + """, + """ + Root (flags: {ProjectRoot}) + Shared.items (flags: {SharedItemsImportFile Unrecognized}), Icon: {A140CD9F-FF94-483C-87B1-9EF5BE9F469A 1}, ExpandedIcon: {} + """)] + public void CalculatePropertyValues_WhenSharedItemsImportFileAsTree_SetsIconToSharedItemsImportFile(string input, string expected) + { + var capabilities = IProjectCapabilitiesServiceFactory.ImplementsContains(capability => { - var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage((string key) => null); + return capability == ProjectCapabilities.SharedAssetsProject; + }); - var propertiesProvider = CreateInstance(imageProvider); + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.SharedItemsImportFile, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); - var icon = new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1); - var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); + var propertiesProvider = CreateInstance(capabilities, imageProvider); - tree = tree.SetIcon(icon); + var inputTree = ProjectTreeParser.Parse(input); + var expectedTree = ProjectTreeParser.Parse(expected); + var result = propertiesProvider.ChangePropertyValuesForEntireTree(inputTree); - propertiesProvider.ChangePropertyValuesForEntireTree(tree); + AssertAreEquivalent(expectedTree, result); + } - Assert.Same(icon, tree.Icon); - } + [Theory] + [InlineData( + """ + Root (flags: {ProjectRoot}) + File (flags: {}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + File (flags: {IncludeInProjectCandidate}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder}) + """)] + [InlineData( + """ + Root (flags: {ProjectRoot}) + Folder (flags: {Folder IncludeInProjectCandidate}) + """)] + public void CalculatePropertyValues_NonProjectRootAsTree_DoesNotSetIcon(string input) + { + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage(ProjectImageKey.ProjectRoot, new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1)); - private static void AssertAreEquivalent(IProjectTree expected, IProjectTree actual) - { - Assert.NotSame(expected, actual); + var propertiesProvider = CreateInstance(imageProvider); - string expectedAsString = ProjectTreeWriter.WriteToString(expected); - string actualAsString = ProjectTreeWriter.WriteToString(actual); + var tree = ProjectTreeParser.Parse(input); + var result = propertiesProvider.ChangePropertyValuesForEntireTree(tree.Children[0]); - Assert.Equal(expectedAsString, actualAsString); - } + Assert.Null(result.Icon); + } - private static ProjectRootImageProjectTreePropertiesProvider CreateInstance() - { - return CreateInstance(null!); - } + [Fact] + public void CalculatePropertyValues_ProjectRootAsTreeWhenImageProviderReturnsNull_DoesNotSetIcon() + { + var imageProvider = IProjectImageProviderFactory.ImplementGetProjectImage((string key) => null); - private static ProjectRootImageProjectTreePropertiesProvider CreateInstance(IProjectImageProvider imageProvider) - { - return CreateInstance(null!, imageProvider); - } + var propertiesProvider = CreateInstance(imageProvider); - private static ProjectRootImageProjectTreePropertiesProvider CreateInstance(IProjectCapabilitiesService? capabilities, IProjectImageProvider? imageProvider) - { - capabilities ??= IProjectCapabilitiesServiceFactory.Create(); - imageProvider ??= IProjectImageProviderFactory.Create(); + var icon = new ProjectImageMoniker(new Guid("{A140CD9F-FF94-483C-87B1-9EF5BE9F469A}"), 1); + var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); + + tree = tree.SetIcon(icon); + + propertiesProvider.ChangePropertyValuesForEntireTree(tree); + + Assert.Same(icon, tree.Icon); + } + + private static void AssertAreEquivalent(IProjectTree expected, IProjectTree actual) + { + Assert.NotSame(expected, actual); + + string expectedAsString = ProjectTreeWriter.WriteToString(expected); + string actualAsString = ProjectTreeWriter.WriteToString(actual); + + Assert.Equal(expectedAsString, actualAsString); + } + + private static ProjectRootImageProjectTreePropertiesProvider CreateInstance() + { + return CreateInstance(null!); + } + + private static ProjectRootImageProjectTreePropertiesProvider CreateInstance(IProjectImageProvider imageProvider) + { + return CreateInstance(null!, imageProvider); + } + + private static ProjectRootImageProjectTreePropertiesProvider CreateInstance(IProjectCapabilitiesService? capabilities, IProjectImageProvider? imageProvider) + { + capabilities ??= IProjectCapabilitiesServiceFactory.Create(); + imageProvider ??= IProjectImageProviderFactory.Create(); - return new ProjectRootImageProjectTreePropertiesProvider(capabilities, imageProvider); - } + return new ProjectRootImageProjectTreePropertiesProvider(capabilities, imageProvider); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectCommonServicesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectCommonServicesTests.cs index 912bd1edc6..cc8a58937b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectCommonServicesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectCommonServicesTests.cs @@ -1,82 +1,81 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class UnconfiguredProjectCommonServicesTests { - public class UnconfiguredProjectCommonServicesTests + [Fact] + public void Constructor_ValueAsProject_SetsProjectProperty() + { + var project = UnconfiguredProjectFactory.Create(); + var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); + var projectProperties = ProjectPropertiesFactory.Create(project); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); + var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); + + var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); + + Assert.Same(project, services.Project); + } + + [Fact] + public void Constructor_ValueAsThreadingService_SetsThreadingServiceProperty() { - [Fact] - public void Constructor_ValueAsProject_SetsProjectProperty() - { - var project = UnconfiguredProjectFactory.Create(); - var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); - var projectProperties = ProjectPropertiesFactory.Create(project); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); - var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); - - var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); - - Assert.Same(project, services.Project); - } - - [Fact] - public void Constructor_ValueAsThreadingService_SetsThreadingServiceProperty() - { - var project = UnconfiguredProjectFactory.Create(); - var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); - var projectProperties = ProjectPropertiesFactory.Create(project); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); - var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); - - var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); - - Assert.Same(threadingService.Value, services.ThreadingService); - } - - [Fact] - public void Constructor_ValueAsActiveConfiguredProject_SetsActiveConfiguredProjectProperty() - { - var project = UnconfiguredProjectFactory.Create(); - var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); - var projectProperties = ProjectPropertiesFactory.Create(project); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); - var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); - - var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); - - Assert.Same(projectProperties.ConfiguredProject, services.ActiveConfiguredProject); - } - - [Fact] - public void Constructor_ValueAsActiveConfiguredProjectProperties_SetsActiveConfiguredProjectPropertiesProperty() - { - var project = UnconfiguredProjectFactory.Create(); - var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); - var projectProperties = ProjectPropertiesFactory.Create(project); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); - var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); - - var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); - - Assert.Same(projectProperties, services.ActiveConfiguredProjectProperties); - } - - [Fact] - public void Constructor_ValueAsProjectAccessor_SetsProjectAccessorProperty() - { - var project = UnconfiguredProjectFactory.Create(); - var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); - var projectProperties = ProjectPropertiesFactory.Create(project); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); - var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); - - var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); - - Assert.Same(projectAccessor.Value, services.ProjectAccessor); - } + var project = UnconfiguredProjectFactory.Create(); + var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); + var projectProperties = ProjectPropertiesFactory.Create(project); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); + var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); + + var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); + + Assert.Same(threadingService.Value, services.ThreadingService); + } + + [Fact] + public void Constructor_ValueAsActiveConfiguredProject_SetsActiveConfiguredProjectProperty() + { + var project = UnconfiguredProjectFactory.Create(); + var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); + var projectProperties = ProjectPropertiesFactory.Create(project); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); + var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); + + var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); + + Assert.Same(projectProperties.ConfiguredProject, services.ActiveConfiguredProject); + } + + [Fact] + public void Constructor_ValueAsActiveConfiguredProjectProperties_SetsActiveConfiguredProjectPropertiesProperty() + { + var project = UnconfiguredProjectFactory.Create(); + var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); + var projectProperties = ProjectPropertiesFactory.Create(project); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); + var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); + + var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); + + Assert.Same(projectProperties, services.ActiveConfiguredProjectProperties); + } + + [Fact] + public void Constructor_ValueAsProjectAccessor_SetsProjectAccessorProperty() + { + var project = UnconfiguredProjectFactory.Create(); + var threadingService = new Lazy(() => IProjectThreadingServiceFactory.Create()); + var projectProperties = ProjectPropertiesFactory.Create(project); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties.ConfiguredProject); + var activeConfiguredProjectProperties = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var projectAccessor = new Lazy(() => IProjectAccessorFactory.Create()); + + var services = new UnconfiguredProjectCommonServices(project, threadingService, activeConfiguredProject, activeConfiguredProjectProperties, projectAccessor); + + Assert.Same(projectAccessor.Value, services.ProjectAccessor); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectTasksServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectTasksServiceTests.cs index 8eab377010..9c8d79c1d6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectTasksServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/UnconfiguredProjectTasksServiceTests.cs @@ -1,166 +1,165 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public class UnconfiguredProjectTasksServiceTests { - public class UnconfiguredProjectTasksServiceTests + [Fact] + public void ProjectLoadedInHost_WhenNotProjectLoadedInHost_ReturnsUncompletedTask() { - [Fact] - public void ProjectLoadedInHost_WhenNotProjectLoadedInHost_ReturnsUncompletedTask() - { - var service = CreateInstance(); + var service = CreateInstance(); - var result = service.ProjectLoadedInHost; + var result = service.ProjectLoadedInHost; - Assert.False(result.IsCompleted); - } + Assert.False(result.IsCompleted); + } - [Fact] - public void SolutionLoadedInHost_SolutionServiceReturnsUncompletedTask_ReturnsUncompletedTask() - { - var solutionService = ISolutionServiceFactory.ImplementSolutionLoadedInHost(() => new Task(() => { })); + [Fact] + public void SolutionLoadedInHost_SolutionServiceReturnsUncompletedTask_ReturnsUncompletedTask() + { + var solutionService = ISolutionServiceFactory.ImplementSolutionLoadedInHost(() => new Task(() => { })); - var service = CreateInstance(solutionService: solutionService); + var service = CreateInstance(solutionService: solutionService); - var result = service.SolutionLoadedInHost; + var result = service.SolutionLoadedInHost; - Assert.False(result.IsCompleted); - } + Assert.False(result.IsCompleted); + } - [Fact] - public void PrioritizedProjectLoadedInHost_WhenNotPrioritizedProjectLoadedInHost_ReturnsUncompletedTask() - { - var service = CreateInstance(); + [Fact] + public void PrioritizedProjectLoadedInHost_WhenNotPrioritizedProjectLoadedInHost_ReturnsUncompletedTask() + { + var service = CreateInstance(); - var result = service.PrioritizedProjectLoadedInHost; + var result = service.PrioritizedProjectLoadedInHost; - Assert.False(result.IsCompleted); - } + Assert.False(result.IsCompleted); + } - [Fact] - public void ProjectLoadedInHost_WhenProjectUnloaded_ReturnsCancelledTask() - { - var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(new CancellationToken(true)); - var service = CreateInstance(tasksService); + [Fact] + public void ProjectLoadedInHost_WhenProjectUnloaded_ReturnsCancelledTask() + { + var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(new CancellationToken(true)); + var service = CreateInstance(tasksService); - var result = service.ProjectLoadedInHost; + var result = service.ProjectLoadedInHost; - Assert.True(result.IsCanceled); - } + Assert.True(result.IsCanceled); + } - [Fact] - public void PrioritizedProjectLoadedInHost_WhenProjectUnloaded_ReturnsCancelledTask() - { - var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(new CancellationToken(true)); - var service = CreateInstance(tasksService); + [Fact] + public void PrioritizedProjectLoadedInHost_WhenProjectUnloaded_ReturnsCancelledTask() + { + var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(new CancellationToken(true)); + var service = CreateInstance(tasksService); - var result = service.PrioritizedProjectLoadedInHost; + var result = service.PrioritizedProjectLoadedInHost; - Assert.True(result.IsCanceled); - } + Assert.True(result.IsCanceled); + } - [Fact] - public void SolutionLoadedInHost_WhenProjectUnloaded_ReturnsCancelledTask() - { - var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(new CancellationToken(true)); - var service = CreateInstance(tasksService); + [Fact] + public void SolutionLoadedInHost_WhenProjectUnloaded_ReturnsCancelledTask() + { + var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(new CancellationToken(true)); + var service = CreateInstance(tasksService); - var result = service.SolutionLoadedInHost; + var result = service.SolutionLoadedInHost; - Assert.True(result.IsCanceled); - } + Assert.True(result.IsCanceled); + } - [Fact] - public void ProjectLoadedInHost_WhenPrioritizedProjectLoadedInHost_ReturnsUncompletedTask() - { - var service = CreateInstance(); + [Fact] + public void ProjectLoadedInHost_WhenPrioritizedProjectLoadedInHost_ReturnsUncompletedTask() + { + var service = CreateInstance(); - var result = service.ProjectLoadedInHost; + var result = service.ProjectLoadedInHost; - service.OnPrioritizedProjectLoadedInHost(); + service.OnPrioritizedProjectLoadedInHost(); - Assert.False(result.IsCompleted); - } + Assert.False(result.IsCompleted); + } - [Fact] - public void PrioritizedProjectLoadedInHost_WhenProjectLoadedInHost_ReturnsUncompletedTask() - { - var service = CreateInstance(); + [Fact] + public void PrioritizedProjectLoadedInHost_WhenProjectLoadedInHost_ReturnsUncompletedTask() + { + var service = CreateInstance(); - var result = service.PrioritizedProjectLoadedInHost; + var result = service.PrioritizedProjectLoadedInHost; - service.OnProjectLoadedInHost(); + service.OnProjectLoadedInHost(); - Assert.False(result.IsCompleted); - } + Assert.False(result.IsCompleted); + } - [Fact] - public void ProjectLoadedInHost_WhenProjectLoadedInHost_ReturnsCompletedTask() - { - var service = CreateInstance(); + [Fact] + public void ProjectLoadedInHost_WhenProjectLoadedInHost_ReturnsCompletedTask() + { + var service = CreateInstance(); - var result = service.ProjectLoadedInHost; - service.OnProjectLoadedInHost(); + var result = service.ProjectLoadedInHost; + service.OnProjectLoadedInHost(); - Assert.True(result.Status == TaskStatus.RanToCompletion); - } + Assert.True(result.Status == TaskStatus.RanToCompletion); + } - [Fact] - public void PrioritizedProjectLoadedInHost_WhenPrioritizedProjectLoadedInHost_ReturnsCompletedTask() - { - var service = CreateInstance(); + [Fact] + public void PrioritizedProjectLoadedInHost_WhenPrioritizedProjectLoadedInHost_ReturnsCompletedTask() + { + var service = CreateInstance(); - var result = service.PrioritizedProjectLoadedInHost; - service.OnPrioritizedProjectLoadedInHost(); + var result = service.PrioritizedProjectLoadedInHost; + service.OnPrioritizedProjectLoadedInHost(); - Assert.True(result.Status == TaskStatus.RanToCompletion); - } + Assert.True(result.Status == TaskStatus.RanToCompletion); + } - [Fact] - public void SolutionLoadedInHost_SolutionServiceSolutionReturnsCompletedTask_ReturnsCompletedTask() - { - var solutionService = ISolutionServiceFactory.ImplementSolutionLoadedInHost(() => Task.CompletedTask); + [Fact] + public void SolutionLoadedInHost_SolutionServiceSolutionReturnsCompletedTask_ReturnsCompletedTask() + { + var solutionService = ISolutionServiceFactory.ImplementSolutionLoadedInHost(() => Task.CompletedTask); - var service = CreateInstance(solutionService: solutionService); + var service = CreateInstance(solutionService: solutionService); - var result = service.SolutionLoadedInHost; + var result = service.SolutionLoadedInHost; - Assert.True(result.Status == TaskStatus.RanToCompletion); - } + Assert.True(result.Status == TaskStatus.RanToCompletion); + } - [Fact] - public void UnloadCancellationToken_WhenUnderlyingUnloadCancellationTokenCancelled_IsCancelled() - { - var source = new CancellationTokenSource(); - var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(source.Token); - var service = CreateInstance(tasksService); + [Fact] + public void UnloadCancellationToken_WhenUnderlyingUnloadCancellationTokenCancelled_IsCancelled() + { + var source = new CancellationTokenSource(); + var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(source.Token); + var service = CreateInstance(tasksService); - Assert.False(service.UnloadCancellationToken.IsCancellationRequested); + Assert.False(service.UnloadCancellationToken.IsCancellationRequested); - source.Cancel(); + source.Cancel(); - Assert.True(service.UnloadCancellationToken.IsCancellationRequested); - } + Assert.True(service.UnloadCancellationToken.IsCancellationRequested); + } - [Fact] - public void OnProjectFactoryCompleted_StartsLoadedInHostListening() - { - int callCount = 0; - var loadedInHostListener = ILoadedInHostListenerFactory.ImplementStartListeningAsync(() => { callCount++; }); + [Fact] + public void OnProjectFactoryCompleted_StartsLoadedInHostListening() + { + int callCount = 0; + var loadedInHostListener = ILoadedInHostListenerFactory.ImplementStartListeningAsync(() => { callCount++; }); - var service = CreateInstance(loadedInHostListener: loadedInHostListener); + var service = CreateInstance(loadedInHostListener: loadedInHostListener); - service.OnProjectFactoryCompleted(); + service.OnProjectFactoryCompleted(); - Assert.Equal(1, callCount); - } + Assert.Equal(1, callCount); + } - private static UnconfiguredProjectTasksService CreateInstance(IProjectAsynchronousTasksService? tasksService = null, ILoadedInHostListener? loadedInHostListener = null, ISolutionService? solutionService = null) - { - tasksService ??= IProjectAsynchronousTasksServiceFactory.Create(); - loadedInHostListener ??= ILoadedInHostListenerFactory.Create(); - solutionService ??= ISolutionServiceFactory.Create(); + private static UnconfiguredProjectTasksService CreateInstance(IProjectAsynchronousTasksService? tasksService = null, ILoadedInHostListener? loadedInHostListener = null, ISolutionService? solutionService = null) + { + tasksService ??= IProjectAsynchronousTasksServiceFactory.Create(); + loadedInHostListener ??= ILoadedInHostListenerFactory.Create(); + solutionService ??= ISolutionServiceFactory.Create(); - return new UnconfiguredProjectTasksService(tasksService, IProjectThreadingServiceFactory.Create(), loadedInHostListener, solutionService); - } + return new UnconfiguredProjectTasksService(tasksService, IProjectThreadingServiceFactory.Create(), loadedInHostListener, solutionService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/ResourceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/ResourceTests.cs index d8a275c4a7..02368b3de0 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/ResourceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/ResourceTests.cs @@ -3,47 +3,46 @@ using Microsoft.VisualStudio.Utilities; using Xunit.Sdk; -namespace Microsoft.VisualStudio.Resources +namespace Microsoft.VisualStudio.Resources; + +public sealed class ResourceTests { - public sealed class ResourceTests + [Theory] + [InlineData(@"src\Microsoft.VisualStudio.AppDesigner\Resources\Designer.Designer.vb", "Microsoft.VisualStudio.AppDesigner.Designer")] + [InlineData(@"src\Microsoft.VisualStudio.Editors\AddImportsDialogs\AddImports.Designer.vb", "AddImports")] + [InlineData(@"src\Microsoft.VisualStudio.Editors\OptionPages\GeneralOptionPageResources.Designer.vb", "GeneralOptionPageResources")] + [InlineData(@"src\Microsoft.VisualStudio.Editors\PropPages\Strings.Designer.vb", "Strings")] + [InlineData(@"src\Microsoft.VisualStudio.Editors\Resources\Microsoft.VisualStudio.Editors.Designer.Designer.vb", "Microsoft.VisualStudio.Editors.Designer")] + [InlineData(@"src\Microsoft.VisualStudio.Editors\Resources\MyExtensibilityRes.Designer.vb", "MyExtensibilityRes")] + [InlineData(@"src\Microsoft.VisualStudio.ProjectSystem.Managed\Resources.Designer.cs", "Microsoft.VisualStudio.Resources")] + [InlineData(@"src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PropertyPages\PropertyPageResources.Designer.cs", "Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages.PropertyPageResources")] + [InlineData(@"src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\VSResources.Designer.cs", "Microsoft.VisualStudio.VSResources")] + public void ResourceCodeGenHasCorrectBaseName(string sourcePath, string baseName) { - [Theory] - [InlineData(@"src\Microsoft.VisualStudio.AppDesigner\Resources\Designer.Designer.vb", "Microsoft.VisualStudio.AppDesigner.Designer")] - [InlineData(@"src\Microsoft.VisualStudio.Editors\AddImportsDialogs\AddImports.Designer.vb", "AddImports")] - [InlineData(@"src\Microsoft.VisualStudio.Editors\OptionPages\GeneralOptionPageResources.Designer.vb", "GeneralOptionPageResources")] - [InlineData(@"src\Microsoft.VisualStudio.Editors\PropPages\Strings.Designer.vb", "Strings")] - [InlineData(@"src\Microsoft.VisualStudio.Editors\Resources\Microsoft.VisualStudio.Editors.Designer.Designer.vb", "Microsoft.VisualStudio.Editors.Designer")] - [InlineData(@"src\Microsoft.VisualStudio.Editors\Resources\MyExtensibilityRes.Designer.vb", "MyExtensibilityRes")] - [InlineData(@"src\Microsoft.VisualStudio.ProjectSystem.Managed\Resources.Designer.cs", "Microsoft.VisualStudio.Resources")] - [InlineData(@"src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\ProjectSystem\VS\PropertyPages\PropertyPageResources.Designer.cs", "Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages.PropertyPageResources")] - [InlineData(@"src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\VSResources.Designer.cs", "Microsoft.VisualStudio.VSResources")] - public void ResourceCodeGenHasCorrectBaseName(string sourcePath, string baseName) - { - // Resx code generation does not respect metadata on . - // This can result in an incorrect base name being used in generated code. - // - // https://github.com/dotnet/project-system/issues/1058 - // - // To prevent this from happening unwittingly (eg. https://github.com/dotnet/project-system/issues/6180) - // this test ensures the expected name is used. This will catch cases when code gen - // produces invalid code before merging/insertion. - - string pattern = sourcePath.Substring(sourcePath.Length - 2, 2) switch - { - "cs" => $"global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"{baseName}\"", - "vb" => $"Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager(\"{baseName}\"", - string format => throw new Exception("Unexpected source file format: " + format) - }; + // Resx code generation does not respect metadata on . + // This can result in an incorrect base name being used in generated code. + // + // https://github.com/dotnet/project-system/issues/1058 + // + // To prevent this from happening unwittingly (eg. https://github.com/dotnet/project-system/issues/6180) + // this test ensures the expected name is used. This will catch cases when code gen + // produces invalid code before merging/insertion. - string path = Path.Combine(RepoUtil.FindRepoRootPath(), sourcePath); + string pattern = sourcePath.Substring(sourcePath.Length - 2, 2) switch + { + "cs" => $"global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"{baseName}\"", + "vb" => $"Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager(\"{baseName}\"", + string format => throw new Exception("Unexpected source file format: " + format) + }; - foreach (string line in File.ReadLines(path)) - { - if (line.Contains(pattern)) - return; - } + string path = Path.Combine(RepoUtil.FindRepoRootPath(), sourcePath); - throw new XunitException($"Expected base name \"{baseName}\" not found in generated file: {sourcePath}"); + foreach (string line in File.ReadLines(path)) + { + if (line.Contains(pattern)) + return; } + + throw new XunitException($"Expected base name \"{baseName}\" not found in generated file: {sourcePath}"); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/XliffTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/XliffTests.cs index d2754b2c02..10ccb69d6e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/XliffTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Resources/XliffTests.cs @@ -6,46 +6,45 @@ using System.Xml.XPath; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.Resources +namespace Microsoft.VisualStudio.Resources; + +public sealed class XliffTests { - public sealed class XliffTests - { - private const string XliffNamespace = "urn:oasis:names:tc:xliff:document:1.2"; + private const string XliffNamespace = "urn:oasis:names:tc:xliff:document:1.2"; - private static readonly Regex s_acceleratorPattern = new(@"&\w"); + private static readonly Regex s_acceleratorPattern = new(@"&\w"); - [Fact] - public void ResourceStringsDoNotContainMultipleAcceleratorMnemonics() + [Fact] + public void ResourceStringsDoNotContainMultipleAcceleratorMnemonics() + { + foreach (string path in GetXlfFiles()) { - foreach (string path in GetXlfFiles()) - { - var settings = new XmlReaderSettings { XmlResolver = null }; - using var fileStream = File.OpenRead(path); - using var reader = XmlReader.Create(fileStream, settings); - var root = XDocument.Load(reader).Root; + var settings = new XmlReaderSettings { XmlResolver = null }; + using var fileStream = File.OpenRead(path); + using var reader = XmlReader.Create(fileStream, settings); + var root = XDocument.Load(reader).Root; - var namespaceManager = new XmlNamespaceManager(reader.NameTable); - namespaceManager.AddNamespace("x", XliffNamespace); + var namespaceManager = new XmlNamespaceManager(reader.NameTable); + namespaceManager.AddNamespace("x", XliffNamespace); - var targets = root.XPathSelectElements(@"/x:xliff/x:file/x:body/x:trans-unit/x:target", namespaceManager); + var targets = root.XPathSelectElements(@"/x:xliff/x:file/x:body/x:trans-unit/x:target", namespaceManager); - foreach (var target in targets) - { - var matches = s_acceleratorPattern.Matches(target.Value); + foreach (var target in targets) + { + var matches = s_acceleratorPattern.Matches(target.Value); - if (matches.Count > 1) - { - throw new Xunit.Sdk.XunitException($"Translated string in {Path.GetFileName(path)} contains multiple accelerator mnemonics: {target.Value}"); - } + if (matches.Count > 1) + { + throw new Xunit.Sdk.XunitException($"Translated string in {Path.GetFileName(path)} contains multiple accelerator mnemonics: {target.Value}"); } } + } - static IEnumerable GetXlfFiles() - { - var root = RepoUtil.FindRepoRootPath(); + static IEnumerable GetXlfFiles() + { + var root = RepoUtil.FindRepoRootPath(); - return Directory.EnumerateFiles(root, "*.xlf", SearchOption.AllDirectories); - } + return Directory.EnumerateFiles(root, "*.xlf", SearchOption.AllDirectories); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Setup/PackageContentTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Setup/PackageContentTests.cs index 114fe9ab9d..67d563fcfc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Setup/PackageContentTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Setup/PackageContentTests.cs @@ -6,42 +6,41 @@ using Microsoft.VisualStudio.Utilities; using VerifyXunit; -namespace Microsoft.VisualStudio.Setup +namespace Microsoft.VisualStudio.Setup; + +[UsesVerify] +public sealed class PackageContentTests { - [UsesVerify] - public sealed class PackageContentTests + [Fact] + public Task NpmPackage() { - [Fact] - public Task NpmPackage() - { - IEnumerable files = GetNpmPackageContents(); - return Verifier.Verify(files); - } + IEnumerable files = GetNpmPackageContents(); + return Verifier.Verify(files); + } - private static IEnumerable GetNpmPackageContents() - { - var rootPath = RepoUtil.FindRepoRootPath(); + private static IEnumerable GetNpmPackageContents() + { + var rootPath = RepoUtil.FindRepoRootPath(); #if DEBUG - var config = "Debug"; + var config = "Debug"; #elif RELEASE - var config = "Release"; + var config = "Release"; #else #error Unexpected configuration #endif - var packagesDirectory = Path.Combine( - rootPath, - "artifacts", - config, - "obj", - "Microsoft.VisualStudio.ProjectSystem.Managed", - "net8.0", - "npmsrc"); - - return Directory.EnumerateFiles(packagesDirectory, "*", SearchOption.AllDirectories) - .Select(pullPath => Path.GetRelativePath(packagesDirectory, pullPath)); - } + var packagesDirectory = Path.Combine( + rootPath, + "artifacts", + config, + "obj", + "Microsoft.VisualStudio.ProjectSystem.Managed", + "net8.0", + "npmsrc"); + + return Directory.EnumerateFiles(packagesDirectory, "*", SearchOption.AllDirectories) + .Select(pullPath => Path.GetRelativePath(packagesDirectory, pullPath)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Telemetry/ManagedTelemetryServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Telemetry/ManagedTelemetryServiceTests.cs index 181f782464..1417dbda83 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Telemetry/ManagedTelemetryServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Telemetry/ManagedTelemetryServiceTests.cs @@ -2,224 +2,223 @@ using Moq.Protected; -namespace Microsoft.VisualStudio.Telemetry +namespace Microsoft.VisualStudio.Telemetry; + +public class ManagedTelemetryServiceTests { - public class ManagedTelemetryServiceTests + [Fact] + public void PostEvent_NullAsEventName_ThrowsArgumentNull() { - [Fact] - public void PostEvent_NullAsEventName_ThrowsArgumentNull() - { - var service = CreateInstance(); - - Assert.Throws("eventName", () => - { - service.PostEvent(null!); - }); - } + var service = CreateInstance(); - [Fact] - public void PostEvent_EmptyAsEventName_ThrowsArgument() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + service.PostEvent(null!); + }); + } - Assert.Throws("eventName", () => - { - service.PostEvent(string.Empty); - }); - } + [Fact] + public void PostEvent_EmptyAsEventName_ThrowsArgument() + { + var service = CreateInstance(); - [Fact] - public void PostProperty_NullAsEventName_ThrowArgumentNull() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + service.PostEvent(string.Empty); + }); + } - Assert.Throws("eventName", () => - { - service.PostProperty(null!, "propName", "value"); - }); - } + [Fact] + public void PostProperty_NullAsEventName_ThrowArgumentNull() + { + var service = CreateInstance(); - [Fact] - public void PostProperty_EmptyAsEventName_ThrowArgument() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + service.PostProperty(null!, "propName", "value"); + }); + } - Assert.Throws("eventName", () => - { - service.PostProperty(string.Empty, "propName", "value"); - }); - } + [Fact] + public void PostProperty_EmptyAsEventName_ThrowArgument() + { + var service = CreateInstance(); - [Fact] - public void PostProperty_NullAsPropertyName_ThrowArgumentNull() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + service.PostProperty(string.Empty, "propName", "value"); + }); + } - Assert.Throws("propertyName", () => - { - service.PostProperty("event1", null!, "value"); - }); - } + [Fact] + public void PostProperty_NullAsPropertyName_ThrowArgumentNull() + { + var service = CreateInstance(); - [Fact] - public void PostProperty_EmptyAsPropertyName_ThrowArgument() + Assert.Throws("propertyName", () => { - var service = CreateInstance(); + service.PostProperty("event1", null!, "value"); + }); + } - Assert.Throws("propertyName", () => - { - service.PostProperty("event1", string.Empty, "value"); - }); - } + [Fact] + public void PostProperty_EmptyAsPropertyName_ThrowArgument() + { + var service = CreateInstance(); - [Fact] - public void PostProperty_NullAsPropertyValue() + Assert.Throws("propertyName", () => { - var service = CreateInstance(); + service.PostProperty("event1", string.Empty, "value"); + }); + } - service.PostProperty("vs/projectsystem/managed/test", "vs.projectsystem.managed.test", null); - } + [Fact] + public void PostProperty_NullAsPropertyValue() + { + var service = CreateInstance(); - [Fact] - public void PostProperties_NullAsEventName_ThrowArgumentNull() - { - var service = CreateInstance(); + service.PostProperty("vs/projectsystem/managed/test", "vs.projectsystem.managed.test", null); + } - Assert.Throws("eventName", () => - { - service.PostProperties(null!, [("propertyName", "propertyValue")]); - }); - } + [Fact] + public void PostProperties_NullAsEventName_ThrowArgumentNull() + { + var service = CreateInstance(); - [Fact] - public void PostProperties_EmptyAsEventName_ThrowArgument() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + service.PostProperties(null!, [("propertyName", "propertyValue")]); + }); + } - Assert.Throws("eventName", () => - { - service.PostProperties(string.Empty, [("propertyName", "propertyValue")]); - }); - } + [Fact] + public void PostProperties_EmptyAsEventName_ThrowArgument() + { + var service = CreateInstance(); - [Fact] - public void PostProperties_NullAsPropertyName_ThrowArgumentNull() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + service.PostProperties(string.Empty, [("propertyName", "propertyValue")]); + }); + } - Assert.Throws("properties", () => - { - service.PostProperties("event1", null!); - }); - } + [Fact] + public void PostProperties_NullAsPropertyName_ThrowArgumentNull() + { + var service = CreateInstance(); - [Fact] - public void PostProperties_EmptyProperties_ThrowArgument() + Assert.Throws("properties", () => { - var service = CreateInstance(); + service.PostProperties("event1", null!); + }); + } - Assert.Throws("properties", () => - { - service.PostProperties("event1", []); - }); - } + [Fact] + public void PostProperties_EmptyProperties_ThrowArgument() + { + var service = CreateInstance(); - [Fact] - public void PostEvent_SendsTelemetryEvent() + Assert.Throws("properties", () => { - TelemetryEvent? result = null; - var service = CreateInstance((e) => { result = e; }); + service.PostProperties("event1", []); + }); + } - service.PostEvent(TelemetryEventName.UpToDateCheckSuccess); + [Fact] + public void PostEvent_SendsTelemetryEvent() + { + TelemetryEvent? result = null; + var service = CreateInstance((e) => { result = e; }); - Assert.NotNull(result); - Assert.Equal(TelemetryEventName.UpToDateCheckSuccess, result.Name); - } + service.PostEvent(TelemetryEventName.UpToDateCheckSuccess); - [Fact] - public void PostProperty_SendsTelemetryEventWithProperty() - { - TelemetryEvent? result = null; - var service = CreateInstance((e) => { result = e; }); + Assert.NotNull(result); + Assert.Equal(TelemetryEventName.UpToDateCheckSuccess, result.Name); + } - service.PostProperty(TelemetryEventName.UpToDateCheckFail, TelemetryPropertyName.UpToDateCheck.FailReason, "Reason"); + [Fact] + public void PostProperty_SendsTelemetryEventWithProperty() + { + TelemetryEvent? result = null; + var service = CreateInstance((e) => { result = e; }); - Assert.NotNull(result); - Assert.Equal(TelemetryEventName.UpToDateCheckFail, result.Name); - Assert.Contains(new KeyValuePair(TelemetryPropertyName.UpToDateCheck.FailReason, "Reason"), result.Properties); - } + service.PostProperty(TelemetryEventName.UpToDateCheckFail, TelemetryPropertyName.UpToDateCheck.FailReason, "Reason"); - [Fact] - public void PostProperties_SendsTelemetryEventWithProperties() - { - TelemetryEvent? result = null; - var service = CreateInstance((e) => { result = e; }); - - service.PostProperties(TelemetryEventName.DesignTimeBuildComplete, - [ - (TelemetryPropertyName.DesignTimeBuildComplete.Succeeded, true), - (TelemetryPropertyName.DesignTimeBuildComplete.Targets, "Compile") - ]); - - Assert.NotNull(result); - Assert.Equal(TelemetryEventName.DesignTimeBuildComplete, result.Name); - Assert.Contains(new KeyValuePair(TelemetryPropertyName.DesignTimeBuildComplete.Succeeded, true), result.Properties); - Assert.Contains(new KeyValuePair(TelemetryPropertyName.DesignTimeBuildComplete.Targets, "Compile"), result.Properties); - } - - [Fact] - public void BeginOperation_NullAsEventName_ThrowsArgumentNull() - { - var service = CreateInstance(); + Assert.NotNull(result); + Assert.Equal(TelemetryEventName.UpToDateCheckFail, result.Name); + Assert.Contains(new KeyValuePair(TelemetryPropertyName.UpToDateCheck.FailReason, "Reason"), result.Properties); + } + + [Fact] + public void PostProperties_SendsTelemetryEventWithProperties() + { + TelemetryEvent? result = null; + var service = CreateInstance((e) => { result = e; }); + + service.PostProperties(TelemetryEventName.DesignTimeBuildComplete, + [ + (TelemetryPropertyName.DesignTimeBuildComplete.Succeeded, true), + (TelemetryPropertyName.DesignTimeBuildComplete.Targets, "Compile") + ]); + + Assert.NotNull(result); + Assert.Equal(TelemetryEventName.DesignTimeBuildComplete, result.Name); + Assert.Contains(new KeyValuePair(TelemetryPropertyName.DesignTimeBuildComplete.Succeeded, true), result.Properties); + Assert.Contains(new KeyValuePair(TelemetryPropertyName.DesignTimeBuildComplete.Targets, "Compile"), result.Properties); + } - Assert.Throws("eventName", () => - { - _ = service.BeginOperation(null!); - }); - } + [Fact] + public void BeginOperation_NullAsEventName_ThrowsArgumentNull() + { + var service = CreateInstance(); - [Fact] - public void BeginOperation_EmptyAsEventName_ThrowsArgument() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + _ = service.BeginOperation(null!); + }); + } - Assert.Throws("eventName", () => - { - _ = service.BeginOperation(string.Empty); - }); - } + [Fact] + public void BeginOperation_EmptyAsEventName_ThrowsArgument() + { + var service = CreateInstance(); - [Fact] - public void HashValue() + Assert.Throws("eventName", () => { - var service = CreateInstance(); + _ = service.BeginOperation(string.Empty); + }); + } - service.IsUserMicrosoftInternal = true; + [Fact] + public void HashValue() + { + var service = CreateInstance(); - Assert.Equal("Hello", service.HashValue("Hello")); - Assert.Equal("World", service.HashValue("World")); - Assert.Equal("", service.HashValue("")); - Assert.Equal(" ", service.HashValue(" ")); + service.IsUserMicrosoftInternal = true; - service.IsUserMicrosoftInternal = false; + Assert.Equal("Hello", service.HashValue("Hello")); + Assert.Equal("World", service.HashValue("World")); + Assert.Equal("", service.HashValue("")); + Assert.Equal(" ", service.HashValue(" ")); - Assert.Equal("185f8db32271fe25", service.HashValue("Hello")); - Assert.Equal("78ae647dc5544d22", service.HashValue("World")); - Assert.Equal("e3b0c44298fc1c14", service.HashValue("")); - Assert.Equal("36a9e7f1c95b82ff", service.HashValue(" ")); - } + service.IsUserMicrosoftInternal = false; - private static ManagedTelemetryService CreateInstance(Action? action = null) - { - if (action is null) - return new ManagedTelemetryService(); + Assert.Equal("185f8db32271fe25", service.HashValue("Hello")); + Assert.Equal("78ae647dc5544d22", service.HashValue("World")); + Assert.Equal("e3b0c44298fc1c14", service.HashValue("")); + Assert.Equal("36a9e7f1c95b82ff", service.HashValue(" ")); + } + + private static ManagedTelemetryService CreateInstance(Action? action = null) + { + if (action is null) + return new ManagedTelemetryService(); - // Override PostEventToSession to avoid actually sending to telemetry - var mock = new Mock(); - mock.Protected().Setup("PostEventToSession", ItExpr.IsAny()) - .Callback(action); + // Override PostEventToSession to avoid actually sending to telemetry + var mock = new Mock(); + mock.Protected().Setup("PostEventToSession", ItExpr.IsAny()) + .Callback(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Text/LazyStringSplitTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Text/LazyStringSplitTests.cs index 2163e51a51..92ba592c53 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Text/LazyStringSplitTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Text/LazyStringSplitTests.cs @@ -1,53 +1,52 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Text +namespace Microsoft.VisualStudio.Text; + +public sealed class LazyStringSplitTests { - public sealed class LazyStringSplitTests + [Theory] + [InlineData("a;b;c", ';', new[] { "a", "b", "c" })] + [InlineData("a_b_c", '_', new[] { "a", "b", "c" })] + [InlineData("aa;bb;cc", ';', new[] { "aa", "bb", "cc" })] + [InlineData("aaa;bbb;ccc", ';', new[] { "aaa", "bbb", "ccc" })] + [InlineData(";a;b;c", ';', new[] { "a", "b", "c" })] + [InlineData("a;b;c;", ';', new[] { "a", "b", "c" })] + [InlineData(";a;b;c;", ';', new[] { "a", "b", "c" })] + [InlineData(";;a;;b;;c;;", ';', new[] { "a", "b", "c" })] + [InlineData("", ';', new string[0])] + [InlineData(";", ';', new string[0])] + [InlineData(";;", ';', new string[0])] + [InlineData(";;;", ';', new string[0])] + [InlineData(";;;a", ';', new[] { "a" })] + [InlineData("a;;;", ';', new[] { "a" })] + [InlineData(";a;;", ';', new[] { "a" })] + [InlineData(";;a;", ';', new[] { "a" })] + [InlineData("a", ';', new[] { "a" })] + [InlineData("aa", ';', new[] { "aa" })] + public void ProducesCorrectEnumeration(string input, char delimiter, string[] expected) { - [Theory] - [InlineData("a;b;c", ';', new[] { "a", "b", "c" })] - [InlineData("a_b_c", '_', new[] { "a", "b", "c" })] - [InlineData("aa;bb;cc", ';', new[] { "aa", "bb", "cc" })] - [InlineData("aaa;bbb;ccc", ';', new[] { "aaa", "bbb", "ccc" })] - [InlineData(";a;b;c", ';', new[] { "a", "b", "c" })] - [InlineData("a;b;c;", ';', new[] { "a", "b", "c" })] - [InlineData(";a;b;c;", ';', new[] { "a", "b", "c" })] - [InlineData(";;a;;b;;c;;", ';', new[] { "a", "b", "c" })] - [InlineData("", ';', new string[0])] - [InlineData(";", ';', new string[0])] - [InlineData(";;", ';', new string[0])] - [InlineData(";;;", ';', new string[0])] - [InlineData(";;;a", ';', new[] { "a" })] - [InlineData("a;;;", ';', new[] { "a" })] - [InlineData(";a;;", ';', new[] { "a" })] - [InlineData(";;a;", ';', new[] { "a" })] - [InlineData("a", ';', new[] { "a" })] - [InlineData("aa", ';', new[] { "aa" })] - public void ProducesCorrectEnumeration(string input, char delimiter, string[] expected) - { - // This boxes - IEnumerable actual = new LazyStringSplit(input, delimiter); + // This boxes + IEnumerable actual = new LazyStringSplit(input, delimiter); - Assert.Equal(expected, actual); + Assert.Equal(expected, actual); - // Non boxing foreach - var list = new List(); + // Non boxing foreach + var list = new List(); - foreach (var s in new LazyStringSplit(input, delimiter)) - { - list.Add(s); - } + foreach (var s in new LazyStringSplit(input, delimiter)) + { + list.Add(s); + } - Assert.Equal(expected, list); + Assert.Equal(expected, list); - // Equivalence with string.Split - Assert.Equal(expected, input.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries)); - } + // Equivalence with string.Split + Assert.Equal(expected, input.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries)); + } - [Fact] - public void Constructor_WithNullInput_Throws() - { - Assert.Throws(() => new LazyStringSplit(null!, ' ')); - } + [Fact] + public void Constructor_WithNullInput_Throws() + { + Assert.Throws(() => new LazyStringSplit(null!, ' ')); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/CancellationSeriesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/CancellationSeriesTests.cs index a91f927480..0ced65c45c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/CancellationSeriesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/CancellationSeriesTests.cs @@ -1,118 +1,117 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +public sealed class CancellationSeriesTests { - public sealed class CancellationSeriesTests + [Fact] + public void CreateNext_ReturnsNonCancelledToken() { - [Fact] - public void CreateNext_ReturnsNonCancelledToken() - { - using var series = new CancellationSeries(); - var token = series.CreateNext(); + using var series = new CancellationSeries(); + var token = series.CreateNext(); - Assert.False(token.IsCancellationRequested); - Assert.True(token.CanBeCanceled); - } + Assert.False(token.IsCancellationRequested); + Assert.True(token.CanBeCanceled); + } - [Fact] - public void CreateNext_CancelsPreviousToken() - { - using var series = new CancellationSeries(); - var token1 = series.CreateNext(); + [Fact] + public void CreateNext_CancelsPreviousToken() + { + using var series = new CancellationSeries(); + var token1 = series.CreateNext(); - Assert.False(token1.IsCancellationRequested); + Assert.False(token1.IsCancellationRequested); - var token2 = series.CreateNext(); + var token2 = series.CreateNext(); - Assert.True(token1.IsCancellationRequested); - Assert.False(token2.IsCancellationRequested); + Assert.True(token1.IsCancellationRequested); + Assert.False(token2.IsCancellationRequested); - var token3 = series.CreateNext(); + var token3 = series.CreateNext(); - Assert.True(token2.IsCancellationRequested); - Assert.False(token3.IsCancellationRequested); - } + Assert.True(token2.IsCancellationRequested); + Assert.False(token3.IsCancellationRequested); + } - [Fact] - public void CreateNext_ThrowsIfDisposed() - { - var series = new CancellationSeries(); + [Fact] + public void CreateNext_ThrowsIfDisposed() + { + var series = new CancellationSeries(); - series.Dispose(); + series.Dispose(); - Assert.Throws(() => series.CreateNext()); - } + Assert.Throws(() => series.CreateNext()); + } - [Fact] - public void CreateNext_ReturnsCancelledTokenIfSuperTokenAlreadyCancelled() - { - var cts = new CancellationTokenSource(); + [Fact] + public void CreateNext_ReturnsCancelledTokenIfSuperTokenAlreadyCancelled() + { + var cts = new CancellationTokenSource(); - using var series = new CancellationSeries(cts.Token); - cts.Cancel(); + using var series = new CancellationSeries(cts.Token); + cts.Cancel(); - var token = series.CreateNext(); + var token = series.CreateNext(); - Assert.True(token.IsCancellationRequested); - } + Assert.True(token.IsCancellationRequested); + } - [Fact] - public void CreateNext_ReturnsCancelledTokenIfInputTokenAlreadyCancelled() - { - var cts = new CancellationTokenSource(); + [Fact] + public void CreateNext_ReturnsCancelledTokenIfInputTokenAlreadyCancelled() + { + var cts = new CancellationTokenSource(); - using var series = new CancellationSeries(); - cts.Cancel(); + using var series = new CancellationSeries(); + cts.Cancel(); - var token = series.CreateNext(cts.Token); + var token = series.CreateNext(cts.Token); - Assert.True(token.IsCancellationRequested); - } + Assert.True(token.IsCancellationRequested); + } - [Fact] - public void CancellingSuperTokenCancelsIssuedToken() - { - var cts = new CancellationTokenSource(); + [Fact] + public void CancellingSuperTokenCancelsIssuedToken() + { + var cts = new CancellationTokenSource(); - using var series = new CancellationSeries(cts.Token); - var token = series.CreateNext(); + using var series = new CancellationSeries(cts.Token); + var token = series.CreateNext(); - Assert.False(token.IsCancellationRequested); + Assert.False(token.IsCancellationRequested); - cts.Cancel(); + cts.Cancel(); - Assert.True(token.IsCancellationRequested); - } + Assert.True(token.IsCancellationRequested); + } - [Fact] - public void CancellingInputTokenCancelsIssuedToken() - { - var cts = new CancellationTokenSource(); + [Fact] + public void CancellingInputTokenCancelsIssuedToken() + { + var cts = new CancellationTokenSource(); - using var series = new CancellationSeries(); - var token = series.CreateNext(cts.Token); + using var series = new CancellationSeries(); + var token = series.CreateNext(cts.Token); - Assert.False(token.IsCancellationRequested); + Assert.False(token.IsCancellationRequested); - cts.Cancel(); + cts.Cancel(); - Assert.True(token.IsCancellationRequested); - } + Assert.True(token.IsCancellationRequested); + } - [Fact] - public void CreateNext_HandlesExceptionsFromPreviousTokenRegistration() - { - using var series = new CancellationSeries(); - var token1 = series.CreateNext(); + [Fact] + public void CreateNext_HandlesExceptionsFromPreviousTokenRegistration() + { + using var series = new CancellationSeries(); + var token1 = series.CreateNext(); - var exception = new Exception(); + var exception = new Exception(); - token1.Register(() => throw exception); + token1.Register(() => throw exception); - var aggregateException = Assert.Throws(() => series.CreateNext()); + var aggregateException = Assert.Throws(() => series.CreateNext()); - Assert.Same(exception, aggregateException.InnerExceptions.Single()); - Assert.True(token1.IsCancellationRequested); - } + Assert.Same(exception, aggregateException.InnerExceptions.Single()); + Assert.True(token1.IsCancellationRequested); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/SequentialTaskExecutorTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/SequentialTaskExecutorTests.cs index ee43f2f8ef..d50951ae57 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/SequentialTaskExecutorTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/SequentialTaskExecutorTests.cs @@ -2,166 +2,165 @@ using Task = System.Threading.Tasks.Task; -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +public sealed class SequentialTaskExecutorTests : IDisposable { - public sealed class SequentialTaskExecutorTests : IDisposable - { - private readonly JoinableTaskContext _joinableTaskContext; + private readonly JoinableTaskContext _joinableTaskContext; - public SequentialTaskExecutorTests() - { + public SequentialTaskExecutorTests() + { #pragma warning disable VSSDK005 - _joinableTaskContext = new JoinableTaskContext(); + _joinableTaskContext = new JoinableTaskContext(); #pragma warning restore VSSDK005 - } + } + + public void Dispose() + { + _joinableTaskContext.Dispose(); + } + + [Fact] + public async Task EnsureTasksAreRunInOrder() + { + const int NumberOfTasks = 25; + var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); - public void Dispose() + var tasks = new List(); + var sequences = new List(); + for (int i = 0; i < NumberOfTasks; i++) { - _joinableTaskContext.Dispose(); + int num = i; + tasks.Add(sequencer.ExecuteTask(async () => + { + async Task func() + { + await Task.Delay(1); + sequences.Add(num); + } + await func(); + })); } - [Fact] - public async Task EnsureTasksAreRunInOrder() + await Task.WhenAll(tasks); + for (int i = 0; i < NumberOfTasks; i++) { - const int NumberOfTasks = 25; - var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); + Assert.Equal(i, sequences[i]); + } + } - var tasks = new List(); - var sequences = new List(); - for (int i = 0; i < NumberOfTasks; i++) - { - int num = i; - tasks.Add(sequencer.ExecuteTask(async () => - { - async Task func() - { - await Task.Delay(1); - sequences.Add(num); - } - await func(); - })); - } + [Fact] + public async Task EnsureTasksAreRunInOrderWithReturnValues() + { + const int NumberOfTasks = 25; + var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); - await Task.WhenAll(tasks); - for (int i = 0; i < NumberOfTasks; i++) + var tasks = new List>(); + for (int i = 0; i < NumberOfTasks; i++) + { + int num = i; + tasks.Add(sequencer.ExecuteTask(async () => { - Assert.Equal(i, sequences[i]); - } + async Task func() + { + await Task.Delay(1); + return num; + } + return await func(); + })); } - [Fact] - public async Task EnsureTasksAreRunInOrderWithReturnValues() + await Task.WhenAll(tasks); + for (int i = 0; i < NumberOfTasks; i++) { - const int NumberOfTasks = 25; - var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); + Assert.Equal(i, tasks[i].Result); + } + } - var tasks = new List>(); - for (int i = 0; i < NumberOfTasks; i++) + [Fact] + public async Task EnsureNestedCallsAreExecutedDirectly() + { + const int NumberOfTasks = 10; + var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); + + var tasks = new List(); + var sequences = new List(); + for (int i = 0; i < NumberOfTasks; i++) + { + int num = i; + tasks.Add(sequencer.ExecuteTask(async () => { - int num = i; - tasks.Add(sequencer.ExecuteTask(async () => + async Task func() { - async Task func() + await sequencer.ExecuteTask(async () => { await Task.Delay(1); - return num; - } - return await func(); - })); - } - - await Task.WhenAll(tasks); - for (int i = 0; i < NumberOfTasks; i++) - { - Assert.Equal(i, tasks[i].Result); - } + sequences.Add(num); + }); + } + await func(); + })); } - [Fact] - public async Task EnsureNestedCallsAreExecutedDirectly() + await Task.WhenAll(tasks); + for (int i = 0; i < NumberOfTasks; i++) { - const int NumberOfTasks = 10; - var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); + Assert.Equal(i, sequences[i]); + } + } - var tasks = new List(); - var sequences = new List(); - for (int i = 0; i < NumberOfTasks; i++) - { - int num = i; - tasks.Add(sequencer.ExecuteTask(async () => - { - async Task func() - { - await sequencer.ExecuteTask(async () => - { - await Task.Delay(1); - sequences.Add(num); - }); - } - await func(); - })); - } + [Fact] + public void CallToDisposedObjectShouldThrow() + { + var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); + sequencer.Dispose(); + Assert.ThrowsAsync(() => sequencer.ExecuteTask(() => Task.CompletedTask)); + } - await Task.WhenAll(tasks); - for (int i = 0; i < NumberOfTasks; i++) + [Fact] + public async Task EnsureTasksCancelledWhenDisposed() + { + const int NumberOfTasks = 10; + var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); + + var tasks = new List(); + for (int i = 0; i < NumberOfTasks; i++) + { + tasks.Add(sequencer.ExecuteTask(async () => { - Assert.Equal(i, sequences[i]); - } + static async Task func() + { + await Task.Delay(100); + } + await func(); + })); } + sequencer.Dispose(); - [Fact] - public void CallToDisposedObjectShouldThrow() + bool mustBeCancelled = false; + + try { - var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); - sequencer.Dispose(); - Assert.ThrowsAsync(() => sequencer.ExecuteTask(() => Task.CompletedTask)); + await Task.WhenAll(tasks); } - - [Fact] - public async Task EnsureTasksCancelledWhenDisposed() + catch (OperationCanceledException) { - const int NumberOfTasks = 10; - var sequencer = new SequentialTaskExecutor(new(_joinableTaskContext), "UnitTests"); - - var tasks = new List(); for (int i = 0; i < NumberOfTasks; i++) { - tasks.Add(sequencer.ExecuteTask(async () => + // The first task or two may already be running. So we skip completed tasks until we find + // one that is is cancelled + if (mustBeCancelled) { - static async Task func() - { - await Task.Delay(100); - } - await func(); - })); - } - sequencer.Dispose(); - - bool mustBeCancelled = false; - - try - { - await Task.WhenAll(tasks); - } - catch (OperationCanceledException) - { - for (int i = 0; i < NumberOfTasks; i++) + Assert.True(tasks[i].IsCanceled); + } + else { - // The first task or two may already be running. So we skip completed tasks until we find - // one that is is cancelled - if (mustBeCancelled) - { - Assert.True(tasks[i].IsCanceled); - } - else - { - // All remaining tasks should be cancelled - mustBeCancelled = tasks[i].IsCanceled; - } + // All remaining tasks should be cancelled + mustBeCancelled = tasks[i].IsCanceled; } } - - Assert.True(mustBeCancelled); } + + Assert.True(mustBeCancelled); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskDelaySchedulerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskDelaySchedulerTests.cs index 9042761a86..a03605a401 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskDelaySchedulerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskDelaySchedulerTests.cs @@ -2,111 +2,110 @@ using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +public class TaskDelaySchedulerTests { - public class TaskDelaySchedulerTests + [Fact] + public async Task ScheduleAsyncTask_RunsAsyncMethod() { - [Fact] - public async Task ScheduleAsyncTask_RunsAsyncMethod() + using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(10), IProjectThreadingServiceFactory.Create(), CancellationToken.None); + bool taskRan = false; + var task = scheduler.ScheduleAsyncTask(ct => { - using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(10), IProjectThreadingServiceFactory.Create(), CancellationToken.None); - bool taskRan = false; - var task = scheduler.ScheduleAsyncTask(ct => - { - taskRan = true; - return Task.CompletedTask; - }); + taskRan = true; + return Task.CompletedTask; + }); - await task; + await task; - Assert.True(taskRan); - } + Assert.True(taskRan); + } - [Fact] - public async Task ScheduleAsyncTask_SkipsPendingTasks() + [Fact] + public async Task ScheduleAsyncTask_SkipsPendingTasks() + { + using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), CancellationToken.None); + var tasksRun = new bool[3]; + var task1 = scheduler.ScheduleAsyncTask(ct => { - using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), CancellationToken.None); - var tasksRun = new bool[3]; - var task1 = scheduler.ScheduleAsyncTask(ct => - { - tasksRun[0] = true; - return Task.CompletedTask; - }); + tasksRun[0] = true; + return Task.CompletedTask; + }); - var task2 = scheduler.ScheduleAsyncTask(ct => - { - tasksRun[1] = true; - return Task.CompletedTask; - }); + var task2 = scheduler.ScheduleAsyncTask(ct => + { + tasksRun[1] = true; + return Task.CompletedTask; + }); - var task3 = scheduler.ScheduleAsyncTask(ct => - { - tasksRun[2] = true; - return Task.CompletedTask; - }); + var task3 = scheduler.ScheduleAsyncTask(ct => + { + tasksRun[2] = true; + return Task.CompletedTask; + }); - await task1; - await task2; - await task3; + await task1; + await task2; + await task3; - Assert.False(tasksRun[0]); - Assert.False(tasksRun[1]); - Assert.True(tasksRun[2]); - } + Assert.False(tasksRun[0]); + Assert.False(tasksRun[1]); + Assert.True(tasksRun[2]); + } - [Fact] - public async Task Dispose_SkipsPendingTasks() + [Fact] + public async Task Dispose_SkipsPendingTasks() + { + using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), CancellationToken.None); + bool taskRan = false; + var task = scheduler.ScheduleAsyncTask(ct => { - using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), CancellationToken.None); - bool taskRan = false; - var task = scheduler.ScheduleAsyncTask(ct => - { - taskRan = true; - return Task.CompletedTask; - }); + taskRan = true; + return Task.CompletedTask; + }); + + scheduler.Dispose(); - scheduler.Dispose(); + await task; + Assert.False(taskRan); + } - await task; - Assert.False(taskRan); - } + [Fact] + public async Task ScheduleAsyncTask_Noop_OriginalSourceTokenCancelled() + { + var cts = new CancellationTokenSource(); + using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), cts.Token); + cts.Cancel(); - [Fact] - public async Task ScheduleAsyncTask_Noop_OriginalSourceTokenCancelled() + bool taskRan = false; + var task = scheduler.ScheduleAsyncTask(ct => { - var cts = new CancellationTokenSource(); - using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), cts.Token); - cts.Cancel(); + taskRan = true; + return Task.CompletedTask; + }); + + await task; + Assert.False(taskRan); + } - bool taskRan = false; - var task = scheduler.ScheduleAsyncTask(ct => + [Fact] + public async Task ScheduleAsyncTask_Noop_RequestTokenCancelled() + { + using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), CancellationToken.None); + var cts = new CancellationTokenSource(); + cts.Cancel(); + + bool taskRan = false; + var task = scheduler.ScheduleAsyncTask( + ct => { taskRan = true; return Task.CompletedTask; - }); - - await task; - Assert.False(taskRan); - } + }, + cts.Token); - [Fact] - public async Task ScheduleAsyncTask_Noop_RequestTokenCancelled() - { - using var scheduler = new TaskDelayScheduler(TimeSpan.FromMilliseconds(250), IProjectThreadingServiceFactory.Create(), CancellationToken.None); - var cts = new CancellationTokenSource(); - cts.Cancel(); - - bool taskRan = false; - var task = scheduler.ScheduleAsyncTask( - ct => - { - taskRan = true; - return Task.CompletedTask; - }, - cts.Token); - - await task; - Assert.False(taskRan); - } + await task; + Assert.False(taskRan); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskExtensionsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskExtensionsTests.cs index 0d1c9bc7db..c7875e9b74 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskExtensionsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskExtensionsTests.cs @@ -1,29 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +public class TaskExtensionsTests { - public class TaskExtensionsTests + [Fact] + public async Task TaskExtensions_TryWaitForCompleteOrTimeoutAsyncTests() { - [Fact] - public async Task TaskExtensions_TryWaitForCompleteOrTimeoutAsyncTests() - { - var t1 = TaskResult.True; - Assert.True(await t1.TryWaitForCompleteOrTimeoutAsync(1000)); + var t1 = TaskResult.True; + Assert.True(await t1.TryWaitForCompleteOrTimeoutAsync(1000)); - var t2 = Task.Delay(10000); - Assert.False(await t2.TryWaitForCompleteOrTimeoutAsync(20)); + var t2 = Task.Delay(10000); + Assert.False(await t2.TryWaitForCompleteOrTimeoutAsync(20)); - var t3 = Task.Delay(20); - Assert.True(await t3.TryWaitForCompleteOrTimeoutAsync(Timeout.Infinite)); + var t3 = Task.Delay(20); + Assert.True(await t3.TryWaitForCompleteOrTimeoutAsync(Timeout.Infinite)); - var t4 = Task.FromCanceled(new CancellationToken(canceled: true)); - await Assert.ThrowsAsync(() => t4.TryWaitForCompleteOrTimeoutAsync(1000)); - await Assert.ThrowsAsync(() => t4.TryWaitForCompleteOrTimeoutAsync(Timeout.Infinite)); + var t4 = Task.FromCanceled(new CancellationToken(canceled: true)); + await Assert.ThrowsAsync(() => t4.TryWaitForCompleteOrTimeoutAsync(1000)); + await Assert.ThrowsAsync(() => t4.TryWaitForCompleteOrTimeoutAsync(Timeout.Infinite)); - var ex = new Exception(); - var t5 = Task.FromException(ex); - Assert.Same(ex, await Assert.ThrowsAsync(() => t5.TryWaitForCompleteOrTimeoutAsync(1000))); - Assert.Same(ex, await Assert.ThrowsAsync(() => t5.TryWaitForCompleteOrTimeoutAsync(Timeout.Infinite))); - } + var ex = new Exception(); + var t5 = Task.FromException(ex); + Assert.Same(ex, await Assert.ThrowsAsync(() => t5.TryWaitForCompleteOrTimeoutAsync(1000))); + Assert.Same(ex, await Assert.ThrowsAsync(() => t5.TryWaitForCompleteOrTimeoutAsync(Timeout.Infinite))); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskResultTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskResultTests.cs index 4f4988f455..8523a2075a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskResultTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Threading/Tasks/TaskResultTests.cs @@ -1,32 +1,31 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Threading.Tasks +namespace Microsoft.VisualStudio.Threading.Tasks; + +public sealed class TaskResultTests { - public sealed class TaskResultTests + [Fact] + public async Task True() { - [Fact] - public async Task True() - { - Assert.Same(TaskResult.True, TaskResult.True); + Assert.Same(TaskResult.True, TaskResult.True); - Assert.True(await TaskResult.True); - } + Assert.True(await TaskResult.True); + } - [Fact] - public async Task False() - { - Assert.Same(TaskResult.False, TaskResult.False); + [Fact] + public async Task False() + { + Assert.Same(TaskResult.False, TaskResult.False); - Assert.False(await TaskResult.False); - } + Assert.False(await TaskResult.False); + } - [Fact] - public async Task Null() - { - Assert.Same(TaskResult.Null(), TaskResult.Null()); - Assert.NotSame(TaskResult.Null(), TaskResult.Null()); + [Fact] + public async Task Null() + { + Assert.Same(TaskResult.Null(), TaskResult.Null()); + Assert.NotSame(TaskResult.Null(), TaskResult.Null()); - Assert.Null(await TaskResult.Null()); - } + Assert.Null(await TaskResult.Null()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/RepoUtil.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/RepoUtil.cs index 07b9658cc7..548f30af51 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/RepoUtil.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/RepoUtil.cs @@ -1,35 +1,34 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Utilities +namespace Microsoft.VisualStudio.Utilities; + +internal static class RepoUtil { - internal static class RepoUtil - { - private static string? _root; + private static string? _root; - /// - /// Gets the absolute path to the checked out location of this repo. - /// - /// - /// Intended for unit tests that need to inspect files in the repo itself. - /// - public static string FindRepoRootPath() + /// + /// Gets the absolute path to the checked out location of this repo. + /// + /// + /// Intended for unit tests that need to inspect files in the repo itself. + /// + public static string FindRepoRootPath() + { + if (_root is null) { - if (_root is null) - { - // Start with this DLL's location - string path = typeof(RepoUtil).Assembly.Location; + // Start with this DLL's location + string path = typeof(RepoUtil).Assembly.Location; - // Walk up the tree until we find the 'artifacts' folder - while (!Path.GetFileName(path).Equals("artifacts", StringComparisons.Paths)) - { - path = Path.GetDirectoryName(path); - } - - // Go up one more level - _root = Path.GetDirectoryName(path); + // Walk up the tree until we find the 'artifacts' folder + while (!Path.GetFileName(path).Equals("artifacts", StringComparisons.Paths)) + { + path = Path.GetDirectoryName(path); } - return _root; + // Go up one more level + _root = Path.GetDirectoryName(path); } + + return _root; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/SetDiffTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/SetDiffTests.cs index 6abb98980e..0527bfa353 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/SetDiffTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Utilities/SetDiffTests.cs @@ -1,33 +1,32 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public sealed class SetDiffTests { - public sealed class SetDiffTests - { - // TODO explore when before/after inputs have duplicate items + // TODO explore when before/after inputs have duplicate items - [Theory] - [InlineData(new int[] {}, new int[] {1, 2, 3}, new int[] {1, 2, 3}, new int[] {})] - [InlineData(new int[] {1, 2, 3}, new int[] {}, new int[] {}, new int[] {1, 2, 3})] - [InlineData(new int[] {1, 2, 3}, new int[] {1, 2, 3}, new int[] {}, new int[] {})] - [InlineData(new int[] {3, 2, 1}, new int[] {1, 2, 3}, new int[] {}, new int[] {})] - [InlineData(new int[] {1, 2}, new int[] {2, 3}, new int[] {3}, new int[] {1})] - public void ProducesCorrectDiff(int[] before, int[] after, int[] added, int[] removed) - { - var diff = new SetDiff(before, after); + [Theory] + [InlineData(new int[] {}, new int[] {1, 2, 3}, new int[] {1, 2, 3}, new int[] {})] + [InlineData(new int[] {1, 2, 3}, new int[] {}, new int[] {}, new int[] {1, 2, 3})] + [InlineData(new int[] {1, 2, 3}, new int[] {1, 2, 3}, new int[] {}, new int[] {})] + [InlineData(new int[] {3, 2, 1}, new int[] {1, 2, 3}, new int[] {}, new int[] {})] + [InlineData(new int[] {1, 2}, new int[] {2, 3}, new int[] {3}, new int[] {1})] + public void ProducesCorrectDiff(int[] before, int[] after, int[] added, int[] removed) + { + var diff = new SetDiff(before, after); - var actualAdded = new HashSet(diff.Added); - var actualRemoved = new HashSet(diff.Removed); + var actualAdded = new HashSet(diff.Added); + var actualRemoved = new HashSet(diff.Removed); - Assert.True(actualAdded.SetEquals(added)); - Assert.True(actualRemoved.SetEquals(removed)); - } + Assert.True(actualAdded.SetEquals(added)); + Assert.True(actualRemoved.SetEquals(removed)); + } - [Fact] - public void Constructor_WithNullValues_Throws() - { - Assert.Throws(() => new SetDiff(null!, new[] {1, 2, 3})); - Assert.Throws(() => new SetDiff(new[] {1, 2, 3}, null!)); - } + [Fact] + public void Constructor_WithNullValues_Throws() + { + Assert.Throws(() => new SetDiff(null!, new[] {1, 2, 3})); + Assert.Throws(() => new SetDiff(new[] {1, 2, 3}, null!)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/DteFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/DteFactory.cs index 752c9ba6f7..1fd75c39bb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/DteFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/DteFactory.cs @@ -2,26 +2,25 @@ using EnvDTE; -namespace EnvDTE80 +namespace EnvDTE80; + +internal static class DTEFactory { - internal static class DTEFactory + public static DTE2 Create() { - public static DTE2 Create() - { - var mock = new Mock(); + var mock = new Mock(); - return mock.As().Object; - } + return mock.As().Object; + } - public static DTE2 ImplementSolution(Func action) - { - var mock = new Mock(); - mock.As(); + public static DTE2 ImplementSolution(Func action) + { + var mock = new Mock(); + mock.As(); - mock.SetupGet(m => m.Solution) - .Returns(action); + mock.SetupGet(m => m.Solution) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IActiveConfiguredProjectSubscriptionServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IActiveConfiguredProjectSubscriptionServiceFactory.cs index 4f196bb251..f6afbc424b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IActiveConfiguredProjectSubscriptionServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IActiveConfiguredProjectSubscriptionServiceFactory.cs @@ -1,23 +1,22 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public static class IActiveConfiguredProjectSubscriptionServiceFactory { - public static class IActiveConfiguredProjectSubscriptionServiceFactory + public static IActiveConfiguredProjectSubscriptionService Create(IProjectValueDataSource? sourceItemsRuleSource = null) { - public static IActiveConfiguredProjectSubscriptionService Create(IProjectValueDataSource? sourceItemsRuleSource = null) - { - var mock = new Mock(); - - mock.SetupGet(s => s.ProjectRuleSource) - .Returns(IProjectValueDataSourceFactory.CreateInstance); + var mock = new Mock(); - if (sourceItemsRuleSource is not null) - { - mock.SetupGet(s => s.SourceItemsRuleSource) - .Returns(() => sourceItemsRuleSource); - } + mock.SetupGet(s => s.ProjectRuleSource) + .Returns(IProjectValueDataSourceFactory.CreateInstance); - return mock.Object; + if (sourceItemsRuleSource is not null) + { + mock.SetupGet(s => s.SourceItemsRuleSource) + .Returns(() => sourceItemsRuleSource); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAddItemDialogServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAddItemDialogServiceFactory.cs index ec2cf28dff..14d1df0e33 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAddItemDialogServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAddItemDialogServiceFactory.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.UI +namespace Microsoft.VisualStudio.ProjectSystem.VS.UI; + +internal static class IAddItemDialogServiceFactory { - internal static class IAddItemDialogServiceFactory + public static IAddItemDialogService Create() { - public static IAddItemDialogService Create() - { - var mock = new Mock(); - mock.Setup(s => s.CanAddNewOrExistingItemTo(It.IsAny())) - .Returns(true); + var mock = new Mock(); + mock.Setup(s => s.CanAddNewOrExistingItemTo(It.IsAny())) + .Returns(true); - return mock.Object; - } + return mock.Object; + } - public static IAddItemDialogService ImplementShowAddNewItemDialogAsync(Func action) - { - var mock = new Mock(); - mock.Setup(s => s.CanAddNewOrExistingItemTo(It.IsAny())) - .Returns(true); + public static IAddItemDialogService ImplementShowAddNewItemDialogAsync(Func action) + { + var mock = new Mock(); + mock.Setup(s => s.CanAddNewOrExistingItemTo(It.IsAny())) + .Returns(true); - mock.Setup(s => s.ShowAddNewItemDialogAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(action); + mock.Setup(s => s.ShowAddNewItemDialogAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAsyncServiceProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAsyncServiceProviderFactory.cs index b4b0ce0e4a..d4ed597a36 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAsyncServiceProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IAsyncServiceProviderFactory.cs @@ -2,23 +2,22 @@ using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IAsyncServiceProviderFactory { - internal static class IAsyncServiceProviderFactory + public static IAsyncServiceProvider Create() { - public static IAsyncServiceProvider Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IAsyncServiceProvider ImplementGetServiceAsync(Func action) - { - var mock = new Mock(); + public static IAsyncServiceProvider ImplementGetServiceAsync(Func action) + { + var mock = new Mock(); - mock.Setup(s => s.GetServiceAsync(It.IsAny())) - .ReturnsAsync(action); + mock.Setup(s => s.GetServiceAsync(It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IDebugLaunchProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IDebugLaunchProviderFactory.cs index 196fe4679f..012613b89f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IDebugLaunchProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IDebugLaunchProviderFactory.cs @@ -2,19 +2,18 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +public static class IDebugLaunchProviderFactory { - public static class IDebugLaunchProviderFactory + public static IDebugLaunchProvider ImplementIsProjectDebuggableAsync(Func action) { - public static IDebugLaunchProvider ImplementIsProjectDebuggableAsync(Func action) - { - var mock = new Mock(); + var mock = new Mock(); - mock.As() - .Setup(d => d.CanBeStartupProjectAsync(It.IsAny())) - .ReturnsAsync(action); + mock.As() + .Setup(d => d.CanBeStartupProjectAsync(It.IsAny())) + .ReturnsAsync(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityRuntimeModelFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityRuntimeModelFactory.cs index 68762a96ce..2d42dd5b0d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityRuntimeModelFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityRuntimeModelFactory.cs @@ -2,14 +2,13 @@ using Microsoft.VisualStudio.ProjectSystem.Query; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IEntityRuntimeModelFactory { - internal static class IEntityRuntimeModelFactory + public static IEntityRuntimeModel Create() { - public static IEntityRuntimeModel Create() - { - var mock = new Mock(); - return mock.Object; - } + var mock = new Mock(); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityWithIdFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityWithIdFactory.cs index 46b6ea6ddf..a7e1c33fda 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityWithIdFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEntityWithIdFactory.cs @@ -2,20 +2,19 @@ using Microsoft.VisualStudio.ProjectSystem.Query; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IEntityWithIdFactory { - internal static class IEntityWithIdFactory + public static IEntityValue Create(string key, string value) { - public static IEntityValue Create(string key, string value) - { - var mock = new Mock(); + var mock = new Mock(); - mock.SetupGet(m => m.Id).Returns(new EntityIdentity(key, value)); + mock.SetupGet(m => m.Id).Returns(new EntityIdentity(key, value)); - var mockWithValue = mock.As(); - mockWithValue.SetupGet(m => m.EntityRuntime).Returns(IEntityRuntimeModelFactory.Create()); + var mockWithValue = mock.As(); + mockWithValue.SetupGet(m => m.EntityRuntime).Returns(IEntityRuntimeModelFactory.Create()); - return mockWithValue.Object; - } + return mockWithValue.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEnvironmentOptionsFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEnvironmentOptionsFactory.cs index c1dbb7971a..70a23feba3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEnvironmentOptionsFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IEnvironmentOptionsFactory.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IEnvironmentOptionsFactory { - internal static class IEnvironmentOptionsFactory + public static IEnvironmentOptions Create() { - public static IEnvironmentOptions Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IEnvironmentOptions Implement(Func environmentOptionsValue) - { - var mock = new Mock(); + public static IEnvironmentOptions Implement(Func environmentOptionsValue) + { + var mock = new Mock(); - mock.Setup(h => h.GetOption(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(environmentOptionsValue); + mock.Setup(h => h.GetOption(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(environmentOptionsValue); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadDiagnosticOutputServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadDiagnosticOutputServiceFactory.cs index 204d55825b..39601aa02f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadDiagnosticOutputServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadDiagnosticOutputServiceFactory.cs @@ -3,21 +3,20 @@ using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Microsoft.VisualStudio.ProjectSystem.VS.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IHotReloadDiagnosticOutputServiceFactory { - internal static class IHotReloadDiagnosticOutputServiceFactory + public static IHotReloadDiagnosticOutputService Create(Action? writeLineCallback = null) { - public static IHotReloadDiagnosticOutputService Create(Action? writeLineCallback = null) - { - var mock = new Mock(); - - if (writeLineCallback is not null) - { - mock.Setup(service => service.WriteLine(It.IsAny(), CancellationToken.None)) - .Callback(writeLineCallback); - } + var mock = new Mock(); - return mock.Object; + if (writeLineCallback is not null) + { + mock.Setup(service => service.WriteLine(It.IsAny(), CancellationToken.None)) + .Callback(writeLineCallback); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadOptionServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadOptionServiceFactory.cs index af8b87aa8e..669ab84ee1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadOptionServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IHotReloadOptionServiceFactory.cs @@ -1,17 +1,16 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Debugger.UI.Interfaces.HotReload +namespace Microsoft.VisualStudio.Debugger.UI.Interfaces.HotReload; + +internal static class IHotReloadOptionServiceFactory { - internal static class IHotReloadOptionServiceFactory + internal static IHotReloadOptionService Create(bool enabledWhenDebugging = true, bool enabledWhenNotDebugging = true) { - internal static IHotReloadOptionService Create(bool enabledWhenDebugging = true, bool enabledWhenNotDebugging = true) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(options => options.IsHotReloadEnabledAsync(It.IsAny(), It.IsAny())) - .Returns((debugging, ct) => new ValueTask(debugging ? enabledWhenDebugging : enabledWhenNotDebugging)); + mock.Setup(options => options.IsHotReloadEnabledAsync(It.IsAny(), It.IsAny())) + .Returns((debugging, ct) => new ValueTask(debugging ? enabledWhenDebugging : enabledWhenNotDebugging)); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IOleAsyncServiceProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IOleAsyncServiceProviderFactory.cs index f44123e663..ac0fec177b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IOleAsyncServiceProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IOleAsyncServiceProviderFactory.cs @@ -2,18 +2,17 @@ using IOleAsyncServiceProvider = Microsoft.VisualStudio.Shell.Interop.COMAsyncServiceProvider.IAsyncServiceProvider; -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IOleAsyncServiceProviderFactory { - internal static class IOleAsyncServiceProviderFactory + public static IOleAsyncServiceProvider ImplementQueryServiceAsync(object? service, Guid clsid) { - public static IOleAsyncServiceProvider ImplementQueryServiceAsync(object? service, Guid clsid) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(p => p.QueryServiceAsync(ref clsid)) - .Returns(IVsTaskFactory.FromResult(service)); + mock.Setup(p => p.QueryServiceAsync(ref clsid)) + .Returns(IVsTaskFactory.FromResult(service)); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectCapabilitiesScopeFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectCapabilitiesScopeFactory.cs index 8a7c62de9e..227a49b38b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectCapabilitiesScopeFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectCapabilitiesScopeFactory.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectCapabilitiesScopeFactory { - internal static class IProjectCapabilitiesScopeFactory + public static IProjectCapabilitiesScope Create(IEnumerable? capabilities = null) { - public static IProjectCapabilitiesScope Create(IEnumerable? capabilities = null) - { - capabilities ??= Enumerable.Empty(); - var snapshot = new Mock(); - snapshot.Setup(s => s.IsProjectCapabilityPresent(It.IsAny())).Returns((string capability) => capabilities.Contains(capability)); + capabilities ??= Enumerable.Empty(); + var snapshot = new Mock(); + snapshot.Setup(s => s.IsProjectCapabilityPresent(It.IsAny())).Returns((string capability) => capabilities.Contains(capability)); - var versionedValue = new Mock>(); - versionedValue.Setup(v => v.Value).Returns(snapshot.Object); + var versionedValue = new Mock>(); + versionedValue.Setup(v => v.Value).Returns(snapshot.Object); - var scope = new Mock(); - scope.Setup(s => s.Current).Returns(versionedValue.Object); + var scope = new Mock(); + scope.Setup(s => s.Current).Returns(versionedValue.Object); - return scope.Object; - } + return scope.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectEvaluationHandlerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectEvaluationHandlerFactory.cs index d877eccdae..ebf4078394 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectEvaluationHandlerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectEvaluationHandlerFactory.cs @@ -2,29 +2,28 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal static class IProjectEvaluationHandlerFactory { - internal static class IProjectEvaluationHandlerFactory + public static IProjectEvaluationHandler ImplementHandle( + Action action, + string? projectEvaluationRule = null) { - public static IProjectEvaluationHandler ImplementHandle( - Action action, - string? projectEvaluationRule = null) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup( - h => h.Handle( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Callback(action); + mock.Setup( + h => h.Handle( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Callback(action); - mock.SetupGet(o => o.ProjectEvaluationRule).Returns(projectEvaluationRule ?? "MyEvaluationRule"); + mock.SetupGet(o => o.ProjectEvaluationRule).Returns(projectEvaluationRule ?? "MyEvaluationRule"); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidService2Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidService2Factory.cs index d23a1fc5c6..f7fda8f3cf 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidService2Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidService2Factory.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectGuidService2Factory { - internal static class IProjectGuidService2Factory + public static IProjectGuidService ImplementGetProjectGuidAsync(Guid result) { - public static IProjectGuidService ImplementGetProjectGuidAsync(Guid result) - { - return ImplementGetProjectGuidAsync(() => result); - } + return ImplementGetProjectGuidAsync(() => result); + } - public static IProjectGuidService ImplementGetProjectGuidAsync(Func action) - { - var mock = new Mock(); - mock.Setup(s => s.GetProjectGuidAsync()) - .ReturnsAsync(action); + public static IProjectGuidService ImplementGetProjectGuidAsync(Func action) + { + var mock = new Mock(); + mock.Setup(s => s.GetProjectGuidAsync()) + .ReturnsAsync(action); - // All IProjectGuidService2 have to be IProjectGuidService instances - return mock.As().Object; - } + // All IProjectGuidService2 have to be IProjectGuidService instances + return mock.As().Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidServiceFactory.cs index b69ff6aad6..747f808181 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectGuidServiceFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectGuidServiceFactory { - internal static class IProjectGuidServiceFactory + public static IProjectGuidService ImplementProjectGuid(Guid result) { - public static IProjectGuidService ImplementProjectGuid(Guid result) - { - var mock = new Mock(); - mock.Setup(s => s.ProjectGuid) - .Returns(result); + var mock = new Mock(); + mock.Setup(s => s.ProjectGuid) + .Returns(result); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadAgentFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadAgentFactory.cs index 3d5012a46e..5887b8b08d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadAgentFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadAgentFactory.cs @@ -2,23 +2,22 @@ using Microsoft.VisualStudio.ProjectSystem.VS.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectHotReloadAgentFactory { - internal static class IProjectHotReloadAgentFactory + public static IProjectHotReloadAgent Create(IProjectHotReloadSession? session = null) { - public static IProjectHotReloadAgent Create(IProjectHotReloadSession? session = null) - { - var mock = new Mock(); + var mock = new Mock(); - if (session is null) - { - session = IProjectHotReloadSessionFactory.Create(); - } + if (session is null) + { + session = IProjectHotReloadSessionFactory.Create(); + } - mock.Setup(agent => agent.CreateHotReloadSession(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(session); + mock.Setup(agent => agent.CreateHotReloadSession(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(session); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadNotificationServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadNotificationServiceFactory.cs index caa2446045..94988687d3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadNotificationServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadNotificationServiceFactory.cs @@ -2,15 +2,14 @@ using Microsoft.VisualStudio.ProjectSystem.VS.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectHotReloadNotificationServiceFactory { - internal static class IProjectHotReloadNotificationServiceFactory + public static IProjectHotReloadNotificationService Create() { - public static IProjectHotReloadNotificationService Create() - { - var mock = new Mock(); + var mock = new Mock(); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionFactory.cs index f0d3d4677f..2768af6848 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionFactory.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.ProjectSystem.VS.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectHotReloadSessionFactory { - internal static class IProjectHotReloadSessionFactory + public static IProjectHotReloadSession Create() { - public static IProjectHotReloadSession Create() - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(session => session.ApplyLaunchVariablesAsync(It.IsAny>(), It.IsAny())) - .ReturnsAsync(true); + mock.Setup(session => session.ApplyLaunchVariablesAsync(It.IsAny>(), It.IsAny())) + .ReturnsAsync(true); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionManagerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionManagerFactory.cs index b21f1e6512..370c6624cd 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionManagerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectHotReloadSessionManagerFactory.cs @@ -2,21 +2,20 @@ using Microsoft.VisualStudio.ProjectSystem.VS.HotReload; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IProjectHotReloadSessionManagerFactory { - internal static class IProjectHotReloadSessionManagerFactory + public static IProjectHotReloadSessionManager Create() { - public static IProjectHotReloadSessionManager Create() - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(manager => manager.TryCreatePendingSessionAsync(It.IsAny>())) - .ReturnsAsync(true); + mock.Setup(manager => manager.TryCreatePendingSessionAsync(It.IsAny>())) + .ReturnsAsync(true); - mock.Setup(manager => manager.ActivateSessionAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.CompletedTask); + mock.Setup(manager => manager.ActivateSessionAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectSpecificEditorProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectSpecificEditorProviderFactory.cs index e7fef8d940..abeaf16d37 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectSpecificEditorProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectSpecificEditorProviderFactory.cs @@ -1,25 +1,24 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class IProjectSpecificEditorProviderFactory { - internal static class IProjectSpecificEditorProviderFactory + public static IProjectSpecificEditorProvider ImplementGetSpecificEditorAsync(Guid editorFactory = default) { - public static IProjectSpecificEditorProvider ImplementGetSpecificEditorAsync(Guid editorFactory = default) - { - var mock = new Mock(); - mock.Setup(i => i.EditorFactory) - .Returns(editorFactory); + var mock = new Mock(); + mock.Setup(i => i.EditorFactory) + .Returns(editorFactory); - return ImplementGetSpecificEditorAsync(mock.Object); - } + return ImplementGetSpecificEditorAsync(mock.Object); + } - public static IProjectSpecificEditorProvider ImplementGetSpecificEditorAsync(IProjectSpecificEditorInfo projectSpecificEditorInfo) - { - var mock = new Mock(); - mock.Setup(p => p.GetSpecificEditorAsync(It.IsAny())) - .ReturnsAsync(projectSpecificEditorInfo); + public static IProjectSpecificEditorProvider ImplementGetSpecificEditorAsync(IProjectSpecificEditorInfo projectSpecificEditorInfo) + { + var mock = new Mock(); + mock.Setup(p => p.GetSpecificEditorAsync(It.IsAny())) + .ReturnsAsync(projectSpecificEditorInfo); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectStateFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectStateFactory.cs index e09a7605fc..aaf4d27eac 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectStateFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectStateFactory.cs @@ -3,35 +3,34 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.VS.Query; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public static class IProjectStateFactory { - public static class IProjectStateFactory + internal static IProjectState Create( + IImmutableSet? projectConfigurations = null, + ProjectConfiguration? defaultConfiguration = null, + Func? bindToRule = null) { - internal static IProjectState Create( - IImmutableSet? projectConfigurations = null, - ProjectConfiguration? defaultConfiguration = null, - Func? bindToRule = null) - { - var mock = new Mock(); - - if (projectConfigurations is not null) - { - mock.Setup(cache => cache.GetKnownConfigurationsAsync()).ReturnsAsync(projectConfigurations); - } + var mock = new Mock(); - if (defaultConfiguration is not null) - { - mock.Setup(cache => cache.GetSuggestedConfigurationAsync()).ReturnsAsync(defaultConfiguration); - } + if (projectConfigurations is not null) + { + mock.Setup(cache => cache.GetKnownConfigurationsAsync()).ReturnsAsync(projectConfigurations); + } - if (bindToRule is not null) - { - mock.Setup(cache => cache - .BindToRuleAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((ProjectConfiguration config, string schema, QueryProjectPropertiesContext context) => Task.FromResult(bindToRule(config, schema, context))); - } + if (defaultConfiguration is not null) + { + mock.Setup(cache => cache.GetSuggestedConfigurationAsync()).ReturnsAsync(defaultConfiguration); + } - return mock.Object; + if (bindToRule is not null) + { + mock.Setup(cache => cache + .BindToRuleAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((ProjectConfiguration config, string schema, QueryProjectPropertiesContext context) => Task.FromResult(bindToRule(config, schema, context))); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectValueDataSourceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectValueDataSourceFactory.cs index fd4fba28b3..27aecd8c1d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectValueDataSourceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IProjectValueDataSourceFactory.cs @@ -2,15 +2,14 @@ using System.Threading.Tasks.Dataflow; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +public static class IProjectValueDataSourceFactory { - public static class IProjectValueDataSourceFactory + public static IProjectValueDataSource CreateInstance() { - public static IProjectValueDataSource CreateInstance() - { - var mock = new Mock>(); - mock.SetupGet(m => m.SourceBlock).Returns(Mock.Of>>()); - return mock.Object; - } + var mock = new Mock>(); + mock.SetupGet(m => m.SourceBlock).Returns(Mock.Of>>()); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IQueryExecutionContextFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IQueryExecutionContextFactory.cs index 6d2fc94343..943c6e3977 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IQueryExecutionContextFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IQueryExecutionContextFactory.cs @@ -4,22 +4,21 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Execution; -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class IQueryExecutionContextFactory { - internal static class IQueryExecutionContextFactory + public static IQueryExecutionContext Create(IEntityRuntimeModel? runtimeModel = null) { - public static IQueryExecutionContext Create(IEntityRuntimeModel? runtimeModel = null) - { - var mock = new Mock(); + var mock = new Mock(); - if (runtimeModel is null) - { - runtimeModel = IEntityRuntimeModelFactory.Create(); - } + if (runtimeModel is null) + { + runtimeModel = IEntityRuntimeModelFactory.Create(); + } - mock.SetupGet(m => m.EntityRuntime).Returns(runtimeModel); + mock.SetupGet(m => m.EntityRuntime).Returns(runtimeModel); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRemoteDebuggerAuthenticationServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRemoteDebuggerAuthenticationServiceFactory.cs index e3155778c5..f736ac2807 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRemoteDebuggerAuthenticationServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRemoteDebuggerAuthenticationServiceFactory.cs @@ -2,17 +2,16 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.Mocks +namespace Microsoft.VisualStudio.Mocks; + +public static class IRemoteDebuggerAuthenticationServiceFactory { - public static class IRemoteDebuggerAuthenticationServiceFactory + internal static IRemoteDebuggerAuthenticationService Create(params IRemoteAuthenticationProvider[] providers) { - internal static IRemoteDebuggerAuthenticationService Create(params IRemoteAuthenticationProvider[] providers) - { - var service = new Mock(); + var service = new Mock(); - service.Setup(s => s.GetRemoteAuthenticationModes()).Returns(providers); + service.Setup(s => s.GetRemoteAuthenticationModes()).Returns(providers); - return service.Object; - } + return service.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRoslynServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRoslynServicesFactory.cs index 425dbb4968..5b87969e20 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRoslynServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IRoslynServicesFactory.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.ProjectSystem.LanguageServices; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IRoslynServicesFactory { - internal static class IRoslynServicesFactory + public static IRoslynServices Implement(ISyntaxFactsService syntaxFactsService) { - public static IRoslynServices Implement(ISyntaxFactsService syntaxFactsService) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(h => h.IsValidIdentifier(It.IsAny())) - .Returns(syntaxFactsService.IsValidIdentifier); + mock.Setup(h => h.IsValidIdentifier(It.IsAny())) + .Returns(syntaxFactsService.IsValidIdentifier); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IServiceProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IServiceProviderFactory.cs index 88d34aaea4..554d58566b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IServiceProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IServiceProviderFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio +namespace Microsoft.VisualStudio; + +internal static class IServiceProviderFactory { - internal static class IServiceProviderFactory + public static IServiceProvider ImplementGetService(Func func) { - public static IServiceProvider ImplementGetService(Func func) - { - var mock = new Mock(); - mock.Setup(sp => sp.GetService(It.IsAny())) - .Returns(func); + var mock = new Mock(); + mock.Setup(sp => sp.GetService(It.IsAny())) + .Returns(func); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ISolutionBuildManagerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ISolutionBuildManagerFactory.cs index e4736a4f92..5ac1b9c159 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ISolutionBuildManagerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ISolutionBuildManagerFactory.cs @@ -2,59 +2,58 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +internal static class ISolutionBuildManagerFactory { - internal static class ISolutionBuildManagerFactory + public static ISolutionBuildManager ImplementBusy(VSSOLNBUILDUPDATEFLAGS buildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_NONE) { - public static ISolutionBuildManager ImplementBusy(VSSOLNBUILDUPDATEFLAGS buildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_NONE) - { - var buildManager = new Mock(); + var buildManager = new Mock(); - buildManager.Setup(b => b.QueryBuildManagerBusy()) - .Returns((int)buildFlags); + buildManager.Setup(b => b.QueryBuildManagerBusy()) + .Returns((int)buildFlags); - buildManager.Setup(b => b.QueryBuildManagerBusyEx()) - .Returns((uint)buildFlags); + buildManager.Setup(b => b.QueryBuildManagerBusyEx()) + .Returns((uint)buildFlags); - return buildManager.Object; - } + return buildManager.Object; + } - public static ISolutionBuildManager Create( - IVsUpdateSolutionEvents? solutionEventsListener = null, - IVsHierarchy? hierarchyToBuild = null, - bool isBuilding = false, - bool cancelBuild = false) - { - var buildManager = new Mock(); + public static ISolutionBuildManager Create( + IVsUpdateSolutionEvents? solutionEventsListener = null, + IVsHierarchy? hierarchyToBuild = null, + bool isBuilding = false, + bool cancelBuild = false) + { + var buildManager = new Mock(); - solutionEventsListener ??= IVsUpdateSolutionEventsFactory.Create(); - hierarchyToBuild ??= IVsHierarchyFactory.Create(); + solutionEventsListener ??= IVsUpdateSolutionEventsFactory.Create(); + hierarchyToBuild ??= IVsHierarchyFactory.Create(); - int isBusy = isBuilding ? 1 : 0; - buildManager.Setup(b => b.QueryBuildManagerBusy()) - .Returns(isBusy); + int isBusy = isBuilding ? 1 : 0; + buildManager.Setup(b => b.QueryBuildManagerBusy()) + .Returns(isBusy); - if (hierarchyToBuild is not null) + if (hierarchyToBuild is not null) + { + void onBuildStartedWithReturn(IVsHierarchy[] _, uint[] __, uint ___) { - void onBuildStartedWithReturn(IVsHierarchy[] _, uint[] __, uint ___) + solutionEventsListener!.UpdateSolution_Begin(It.IsAny()); + + if (cancelBuild) { - solutionEventsListener!.UpdateSolution_Begin(It.IsAny()); - - if (cancelBuild) - { - solutionEventsListener.UpdateSolution_Cancel(); - } - else - { - solutionEventsListener.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny()); - } + solutionEventsListener.UpdateSolution_Cancel(); + } + else + { + solutionEventsListener.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny()); } - - buildManager.Setup(b => b.StartUpdateSpecificProjectConfigurations(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((Action)onBuildStartedWithReturn); } - return buildManager.Object; + buildManager.Setup(b => b.StartUpdateSpecificProjectConfigurations(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((Action)onBuildStartedWithReturn); } + + return buildManager.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IStartupProjectHelperFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IStartupProjectHelperFactory.cs index 2edd6b9ac4..dbd4fd586d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IStartupProjectHelperFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IStartupProjectHelperFactory.cs @@ -2,21 +2,20 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IStartupProjectHelperFactory { - internal static class IStartupProjectHelperFactory + internal static IStartupProjectHelper Create(ImmutableArray? startProjectFullPaths = null) { - internal static IStartupProjectHelper Create(ImmutableArray? startProjectFullPaths = null) - { - var mock = new Mock(); - - if (startProjectFullPaths.HasValue) - { - mock.Setup(t => t.GetFullPathsOfStartupProjects()) - .Returns(startProjectFullPaths.Value); - } + var mock = new Mock(); - return mock.Object; + if (startProjectFullPaths.HasValue) + { + mock.Setup(t => t.GetFullPathsOfStartupProjects()) + .Returns(startProjectFullPaths.Value); } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ITokenReplacerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ITokenReplacerFactory.cs index 0fa9c04d14..1faa8f02f5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ITokenReplacerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ITokenReplacerFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Debug +namespace Microsoft.VisualStudio.ProjectSystem.Debug; + +internal static class IDebugTokenReplacerFactory { - internal static class IDebugTokenReplacerFactory + public static IDebugTokenReplacer Create() { - public static IDebugTokenReplacer Create() - { - var mock = new Mock(); - mock.Setup(s => s.ReplaceTokensInProfileAsync(It.IsAny())) - .Returns(p => Task.FromResult(p)); + var mock = new Mock(); + mock.Setup(s => s.ReplaceTokensInProfileAsync(It.IsAny())) + .Returns(p => Task.FromResult(p)); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesFactory.cs index 72c9696ee5..ef5c14392e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesFactory.cs @@ -2,57 +2,56 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IUnconfiguredProjectVsServicesFactory { - internal static class IUnconfiguredProjectVsServicesFactory + public static IUnconfiguredProjectVsServices Create() + { + return Mock.Of(); + } + + public static IUnconfiguredProjectVsServices Implement(Func? hierarchyCreator = null, + Func? projectCreator = null, + Func? threadingServiceCreator = null, + Func? projectProperties = null, + Func? configuredProjectCreator = null, + Func? unconfiguredProjectCreator = null) { - public static IUnconfiguredProjectVsServices Create() + var mock = new Mock(); + if (hierarchyCreator is not null) + { + mock.SetupGet(h => h.VsHierarchy) + .Returns(hierarchyCreator); + } + + var threadingService = threadingServiceCreator is null ? IProjectThreadingServiceFactory.Create() : threadingServiceCreator(); + + mock.SetupGet(h => h.ThreadingService) + .Returns(threadingService); + + if (projectCreator is not null) + { + mock.SetupGet(h => h.VsProject) + .Returns(projectCreator()); + } + + if (configuredProjectCreator is not null) { - return Mock.Of(); + mock.SetupGet(h => h.ActiveConfiguredProject) + .Returns(configuredProjectCreator); } - public static IUnconfiguredProjectVsServices Implement(Func? hierarchyCreator = null, - Func? projectCreator = null, - Func? threadingServiceCreator = null, - Func? projectProperties = null, - Func? configuredProjectCreator = null, - Func? unconfiguredProjectCreator = null) + if (projectProperties is not null) { - var mock = new Mock(); - if (hierarchyCreator is not null) - { - mock.SetupGet(h => h.VsHierarchy) - .Returns(hierarchyCreator); - } - - var threadingService = threadingServiceCreator is null ? IProjectThreadingServiceFactory.Create() : threadingServiceCreator(); - - mock.SetupGet(h => h.ThreadingService) - .Returns(threadingService); - - if (projectCreator is not null) - { - mock.SetupGet(h => h.VsProject) - .Returns(projectCreator()); - } - - if (configuredProjectCreator is not null) - { - mock.SetupGet(h => h.ActiveConfiguredProject) - .Returns(configuredProjectCreator); - } - - if (projectProperties is not null) - { - mock.SetupGet(h => h.ActiveConfiguredProjectProperties).Returns(projectProperties()); - } - - if (unconfiguredProjectCreator is not null) - { - mock.SetupGet(h => h.Project).Returns(unconfiguredProjectCreator()); - } - - return mock.Object; + mock.SetupGet(h => h.ActiveConfiguredProjectProperties).Returns(projectProperties()); } + + if (unconfiguredProjectCreator is not null) + { + mock.SetupGet(h => h.Project).Returns(unconfiguredProjectCreator()); + } + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesMock.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesMock.cs index 6a1a3c809a..9d717f3633 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesMock.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUnconfiguredProjectVsServicesMock.cs @@ -2,40 +2,39 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal class IUnconfiguredProjectVsServicesMock : AbstractMock { - internal class IUnconfiguredProjectVsServicesMock : AbstractMock + public IUnconfiguredProjectVsServicesMock ImplementVsHierarchy(IVsHierarchy? hierarchy) { - public IUnconfiguredProjectVsServicesMock ImplementVsHierarchy(IVsHierarchy? hierarchy) - { - SetupGet(m => m.VsHierarchy) - .Returns(hierarchy); + SetupGet(m => m.VsHierarchy) + .Returns(hierarchy); - return this; - } + return this; + } - public IUnconfiguredProjectVsServicesMock ImplementVsProject(IVsProject4 project) - { - SetupGet(m => m.VsProject) - .Returns(project); + public IUnconfiguredProjectVsServicesMock ImplementVsProject(IVsProject4 project) + { + SetupGet(m => m.VsProject) + .Returns(project); - return this; - } + return this; + } - public IUnconfiguredProjectVsServicesMock ImplementThreadingService(IProjectThreadingService threadingService) - { - SetupGet(m => m.ThreadingService) - .Returns(threadingService); + public IUnconfiguredProjectVsServicesMock ImplementThreadingService(IProjectThreadingService threadingService) + { + SetupGet(m => m.ThreadingService) + .Returns(threadingService); - return this; - } + return this; + } - public IUnconfiguredProjectVsServicesMock ImplementActiveConfiguredProjectProperties(ProjectProperties? projectProperties) - { - SetupGet(m => m.ActiveConfiguredProjectProperties) - .Returns(projectProperties); + public IUnconfiguredProjectVsServicesMock ImplementActiveConfiguredProjectProperties(ProjectProperties? projectProperties) + { + SetupGet(m => m.ActiveConfiguredProjectProperties) + .Returns(projectProperties); - return this; - } + return this; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUserNotificationServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUserNotificationServicesFactory.cs index e2ea5d5280..9ef7a46bc5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUserNotificationServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IUserNotificationServicesFactory.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IUserNotificationServicesFactory { - internal static class IUserNotificationServicesFactory + public static IUserNotificationServices Create() { - public static IUserNotificationServices Create() - { - return Mock.Of(); - } + return Mock.Of(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAddProjectItemDlgFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAddProjectItemDlgFactory.cs index 6bc2c3dc54..e64d012bbc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAddProjectItemDlgFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAddProjectItemDlgFactory.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsAddProjectItemDlgFactory { - internal static class IVsAddProjectItemDlgFactory + public static IVsAddProjectItemDlg Create(int retVal = -1) { - public static IVsAddProjectItemDlg Create(int retVal = -1) - { - // All parameters are ignored, we just return the specified value. - return Implement((a, b, c, d, e, f, g, h, i) => retVal); - } + // All parameters are ignored, we just return the specified value. + return Implement((a, b, c, d, e, f, g, h, i) => retVal); + } - public static IVsAddProjectItemDlg Implement(Func body) - { - var anyGuid = It.IsAny(); - var anyLoc = It.IsAny(); - var anyFilt = It.IsAny(); - var anyInt = It.IsAny(); - return ImplementWithParams(body, anyGuid, anyLoc, anyFilt, anyInt); - } + public static IVsAddProjectItemDlg Implement(Func body) + { + var anyGuid = It.IsAny(); + var anyLoc = It.IsAny(); + var anyFilt = It.IsAny(); + var anyInt = It.IsAny(); + return ImplementWithParams(body, anyGuid, anyLoc, anyFilt, anyInt); + } - public static IVsAddProjectItemDlg ImplementWithParams(Func body, Guid g, string locations, string filter, int showAgain) - { - var dlg = new Mock(); - dlg.Setup(d => d.AddProjectItemDlg(It.IsAny(), ref g, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - ref locations, ref filter, out showAgain)).Returns(body); - return dlg.Object; - } + public static IVsAddProjectItemDlg ImplementWithParams(Func body, Guid g, string locations, string filter, int showAgain) + { + var dlg = new Mock(); + dlg.Setup(d => d.AddProjectItemDlg(It.IsAny(), ref g, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + ref locations, ref filter, out showAgain)).Returns(body); + return dlg.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAsyncFileChangeExMock.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAsyncFileChangeExMock.cs index 0ee28eaae4..2017b066e5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAsyncFileChangeExMock.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsAsyncFileChangeExMock.cs @@ -2,72 +2,71 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.Shell +namespace Microsoft.VisualStudio.Shell; + +public class IVsAsyncFileChangeExMock : IVsAsyncFileChangeEx { - public class IVsAsyncFileChangeExMock : IVsAsyncFileChangeEx + private uint _lastCookie; + private readonly Dictionary _watchedFiles = new(); + private readonly HashSet _uniqueFilesWatched = new(); + + public IEnumerable UniqueFilesWatched => _uniqueFilesWatched; + + public IEnumerable WatchedFiles => _watchedFiles.Values; + + public Task AdviseFileChangeAsync(string filename, _VSFILECHANGEFLAGS filter, IVsFreeThreadedFileChangeEvents2 sink, CancellationToken cancellationToken = default) + { + _uniqueFilesWatched.Add(filename); + + uint cookie = _lastCookie++; + _watchedFiles.Add(cookie, filename); + return Task.FromResult(cookie); + } + + public Task UnadviseFileChangeAsync(uint cookie, CancellationToken cancellationToken = default) + { + string file = _watchedFiles[cookie]; + _watchedFiles.Remove(cookie); + return Task.FromResult(file); + } + + public Task AdviseDirChangeAsync(string directory, bool watchSubdirectories, IVsFreeThreadedFileChangeEvents2 sink, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task IgnoreDirAsync(string directory, bool ignore, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task IgnoreFileAsync(uint cookie, string filename, bool ignore, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task SyncFileAsync(string filename, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UnadviseDirChangeAsync(uint cookie, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task FilterDirectoryChangesAsync(uint cookie, string[] extensions, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task UnadviseFileChangesAsync(IReadOnlyCollection cookies, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task UnadviseDirChangesAsync(IReadOnlyCollection cookies, CancellationToken cancellationToken = default) { - private uint _lastCookie; - private readonly Dictionary _watchedFiles = new(); - private readonly HashSet _uniqueFilesWatched = new(); - - public IEnumerable UniqueFilesWatched => _uniqueFilesWatched; - - public IEnumerable WatchedFiles => _watchedFiles.Values; - - public Task AdviseFileChangeAsync(string filename, _VSFILECHANGEFLAGS filter, IVsFreeThreadedFileChangeEvents2 sink, CancellationToken cancellationToken = default) - { - _uniqueFilesWatched.Add(filename); - - uint cookie = _lastCookie++; - _watchedFiles.Add(cookie, filename); - return Task.FromResult(cookie); - } - - public Task UnadviseFileChangeAsync(uint cookie, CancellationToken cancellationToken = default) - { - string file = _watchedFiles[cookie]; - _watchedFiles.Remove(cookie); - return Task.FromResult(file); - } - - public Task AdviseDirChangeAsync(string directory, bool watchSubdirectories, IVsFreeThreadedFileChangeEvents2 sink, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task IgnoreDirAsync(string directory, bool ignore, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task IgnoreFileAsync(uint cookie, string filename, bool ignore, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task SyncFileAsync(string filename, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task UnadviseDirChangeAsync(uint cookie, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task FilterDirectoryChangesAsync(uint cookie, string[] extensions, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public Task UnadviseFileChangesAsync(IReadOnlyCollection cookies, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task UnadviseDirChangesAsync(IReadOnlyCollection cookies, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsContainedLanguageFactoryFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsContainedLanguageFactoryFactory.cs index 35b0fffff9..8b75f82af8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsContainedLanguageFactoryFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsContainedLanguageFactoryFactory.cs @@ -1,12 +1,11 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.TextManager.Interop +namespace Microsoft.VisualStudio.TextManager.Interop; + +internal static class IVsContainedLanguageFactoryFactory { - internal static class IVsContainedLanguageFactoryFactory + public static IVsContainedLanguageFactory Create() { - public static IVsContainedLanguageFactory Create() - { - return Mock.Of(); - } + return Mock.Of(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsDebugger10Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsDebugger10Factory.cs index 3a47e0cb2c..52614e0f33 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsDebugger10Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsDebugger10Factory.cs @@ -1,15 +1,14 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsDebugger10Factory { - internal static class IVsDebugger10Factory + public static IVsDebugger10 ImplementIsIntegratedConsoleEnabled(bool enabled) { - public static IVsDebugger10 ImplementIsIntegratedConsoleEnabled(bool enabled) - { - var mock = new Mock(); - mock.Setup(d => d.IsIntegratedConsoleEnabled(out enabled)); + var mock = new Mock(); + mock.Setup(d => d.IsIntegratedConsoleEnabled(out enabled)); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsHierarchyFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsHierarchyFactory.cs index 895de48237..a23ea3f0bb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsHierarchyFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsHierarchyFactory.cs @@ -3,76 +3,75 @@ using Microsoft.VisualStudio.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsHierarchyFactory { - internal static class IVsHierarchyFactory + public static IVsHierarchy Create() { - public static IVsHierarchy Create() - { - var mock = new Mock(); + var mock = new Mock(); - return mock.As().Object; - } + return mock.As().Object; + } - public static IVsHierarchy ImplementGetProperty(int hr) - { - object result; - var mock = new Mock(); - mock.Setup(h => h.GetProperty(It.IsAny(), It.IsAny(), out result)) - .Returns(hr); + public static IVsHierarchy ImplementGetProperty(int hr) + { + object result; + var mock = new Mock(); + mock.Setup(h => h.GetProperty(It.IsAny(), It.IsAny(), out result)) + .Returns(hr); - mock.As(); + mock.As(); - return mock.Object; - } + return mock.Object; + } - public static IVsHierarchy ImplementGetProperty(object result) - { - var mock = new Mock(); - mock.Setup(h => h.GetProperty(It.IsAny(), It.IsAny(), out result)) - .Returns(0); + public static IVsHierarchy ImplementGetProperty(object result) + { + var mock = new Mock(); + mock.Setup(h => h.GetProperty(It.IsAny(), It.IsAny(), out result)) + .Returns(0); - mock.As(); + mock.As(); - return mock.Object; - } + return mock.Object; + } - public static IVsHierarchy ImplementAsUnconfiguredProject(UnconfiguredProject project) - { - var hier = new Mock(); - var browse = hier.As(); - browse.SetupGet(b => b.UnconfiguredProject).Returns(project); - return hier.Object; - } + public static IVsHierarchy ImplementAsUnconfiguredProject(UnconfiguredProject project) + { + var hier = new Mock(); + var browse = hier.As(); + browse.SetupGet(b => b.UnconfiguredProject).Returns(project); + return hier.Object; + } - public static void ImplementGetGuid(this IVsHierarchy hierarchy, VsHierarchyPropID propId, int hr) - { - Guid result; - var mock = Mock.Get(hierarchy); - mock.Setup(h => h.GetGuidProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) - .Returns(hr); - } + public static void ImplementGetGuid(this IVsHierarchy hierarchy, VsHierarchyPropID propId, int hr) + { + Guid result; + var mock = Mock.Get(hierarchy); + mock.Setup(h => h.GetGuidProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) + .Returns(hr); + } - public static void ImplementGetGuid(this IVsHierarchy hierarchy, VsHierarchyPropID propId, Guid result) - { - var mock = Mock.Get(hierarchy); - mock.Setup(h => h.GetGuidProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) - .Returns(0); - } + public static void ImplementGetGuid(this IVsHierarchy hierarchy, VsHierarchyPropID propId, Guid result) + { + var mock = Mock.Get(hierarchy); + mock.Setup(h => h.GetGuidProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) + .Returns(0); + } - public static void ImplementGetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID propId, int hr) - { - object result; - var mock = Mock.Get(hierarchy); - mock.Setup(h => h.GetProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) - .Returns(hr); - } + public static void ImplementGetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID propId, int hr) + { + object result; + var mock = Mock.Get(hierarchy); + mock.Setup(h => h.GetProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) + .Returns(hr); + } - public static void ImplementGetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID propId, object result) - { - var mock = Mock.Get(hierarchy); - mock.Setup(h => h.GetProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) - .Returns(0); - } + public static void ImplementGetProperty(this IVsHierarchy hierarchy, VsHierarchyPropID propId, object result) + { + var mock = Mock.Get(hierarchy); + mock.Setup(h => h.GetProperty(It.IsAny(), It.Is(p => p == (int)propId), out result)) + .Returns(0); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsLanguageServiceBuildErrorReporter2Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsLanguageServiceBuildErrorReporter2Factory.cs index 08775a3f7d..533dfe9145 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsLanguageServiceBuildErrorReporter2Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsLanguageServiceBuildErrorReporter2Factory.cs @@ -2,30 +2,29 @@ using Microsoft.VisualStudio.TextManager.Interop; -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsLanguageServiceBuildErrorReporter2Factory { - internal static class IVsLanguageServiceBuildErrorReporter2Factory + public static IVsLanguageServiceBuildErrorReporter2 ImplementClearErrors(Func action) { - public static IVsLanguageServiceBuildErrorReporter2 ImplementClearErrors(Func action) - { - var mock = new Mock(); + var mock = new Mock(); - var reporter = mock.As(); - reporter.Setup(r => r.ClearErrors()) - .Returns(action); + var reporter = mock.As(); + reporter.Setup(r => r.ClearErrors()) + .Returns(action); - return reporter.Object; - } + return reporter.Object; + } - public static IVsLanguageServiceBuildErrorReporter2 ImplementReportError(Action action) - { - var mock = new Mock(); + public static IVsLanguageServiceBuildErrorReporter2 ImplementReportError(Action action) + { + var mock = new Mock(); - var reporter = mock.As(); - reporter.Setup(r => r.ReportError2(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(action); + var reporter = mock.As(); + reporter.Setup(r => r.ReportError2(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(action); - return reporter.Object; - } + return reporter.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsOnlineServicesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsOnlineServicesFactory.cs index d14c9d0b6c..2d65416a3f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsOnlineServicesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsOnlineServicesFactory.cs @@ -2,18 +2,17 @@ using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.Mocks +namespace Microsoft.VisualStudio.Mocks; + +internal static class IVsOnlineServicesFactory { - internal static class IVsOnlineServicesFactory + public static IVsOnlineServices Create(bool online) { - public static IVsOnlineServices Create(bool online) - { - var mock = new Mock(); + var mock = new Mock(); - mock.SetupGet(s => s.ConnectedToVSOnline) - .Returns(online); + mock.SetupGet(s => s.ConnectedToVSOnline) + .Returns(online); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectDesignerPageService.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectDesignerPageService.cs index 2121439e6c..74d0d8de42 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectDesignerPageService.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectDesignerPageService.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.Properties +namespace Microsoft.VisualStudio.ProjectSystem.Properties; + +internal static class IVsProjectDesignerPageServiceFactory { - internal static class IVsProjectDesignerPageServiceFactory + public static IVsProjectDesignerPageService Create() { - public static IVsProjectDesignerPageService Create() - { - return Mock.Of(); - } + return Mock.Of(); + } - public static IVsProjectDesignerPageService ImplementIsProjectDesignerSupported(Func action) - { - var mock = new Mock(); - mock.SetupGet(s => s.IsProjectDesignerSupported) - .Returns(action); + public static IVsProjectDesignerPageService ImplementIsProjectDesignerSupported(Func action) + { + var mock = new Mock(); + mock.SetupGet(s => s.IsProjectDesignerSupported) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectSpecialFilesFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectSpecialFilesFactory.cs index 033287cebb..0a4cefd1bc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectSpecialFilesFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProjectSpecialFilesFactory.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsProjectSpecialFilesFactory { - internal static class IVsProjectSpecialFilesFactory + public static IVsProjectSpecialFiles ImplementGetFile(FuncWithOut action) { - public static IVsProjectSpecialFiles ImplementGetFile(FuncWithOut action) - { - var mock = new Mock().As(); + var mock = new Mock().As(); - uint itemId; - string fileName; - mock.Setup(s => s.GetFile(It.IsAny(), It.IsAny(), out itemId, out fileName)) - .Returns(action); + uint itemId; + string fileName; + mock.Setup(s => s.GetFile(It.IsAny(), It.IsAny(), out itemId, out fileName)) + .Returns(action); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProject_Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProject_Factory.cs index 60ad5f16af..3f4507eff6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProject_Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsProject_Factory.cs @@ -2,57 +2,56 @@ using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +// Named with an _ instead of IVsProjectFactory to avoid collisions with the actual IVsProjectFactory +// class. +internal static class IVsProject_Factory { - // Named with an _ instead of IVsProjectFactory to avoid collisions with the actual IVsProjectFactory - // class. - internal static class IVsProject_Factory + public static IVsProject4 ImplementIsDocumentInProject(int hr) + { + return ImplementIsDocumentInProject(hr, found: 0, itemid: 0); + } + + public static IVsProject4 ImplementIsDocumentInProject(bool found, uint itemid = 0) + { + return ImplementIsDocumentInProject(HResult.OK, found: found ? 1 : 0, itemid: itemid); + } + + public static IVsProject4 ImplementIsDocumentInProject(int hr, int found, uint itemid) + { + var mock = new Mock(); + + mock.Setup(p => p.IsDocumentInProject(It.IsAny(), out found, It.IsAny(), out itemid)) + .Returns(hr); + + return mock.Object; + } + + public static void ImplementOpenItemWithSpecific(this IVsProject4 project, Guid editorType, Guid logicalView, int hr) + { + IVsWindowFrame frame; + var mock = Mock.Get(project); + mock.Setup(h => h.OpenItemWithSpecific(It.IsAny(), It.IsAny(), ref editorType, It.IsAny(), ref logicalView, It.IsAny(), out frame)) + .Returns(hr); + } + + public static void ImplementOpenItemWithSpecific(this IVsProject4 project, Guid editorType, Guid logicalView, IVsWindowFrame? frame) + { + var mock = Mock.Get(project); + mock.Setup(h => h.OpenItemWithSpecific(It.IsAny(), It.IsAny(), ref editorType, It.IsAny(), ref logicalView, It.IsAny(), out frame)) + .Returns(0); + } + + public static void ImplementAddItemWithSpecific(this IVsProject4 project, Func addItemWithSpecificFunc) { - public static IVsProject4 ImplementIsDocumentInProject(int hr) - { - return ImplementIsDocumentInProject(hr, found: 0, itemid: 0); - } - - public static IVsProject4 ImplementIsDocumentInProject(bool found, uint itemid = 0) - { - return ImplementIsDocumentInProject(HResult.OK, found: found ? 1 : 0, itemid: itemid); - } - - public static IVsProject4 ImplementIsDocumentInProject(int hr, int found, uint itemid) - { - var mock = new Mock(); - - mock.Setup(p => p.IsDocumentInProject(It.IsAny(), out found, It.IsAny(), out itemid)) - .Returns(hr); - - return mock.Object; - } - - public static void ImplementOpenItemWithSpecific(this IVsProject4 project, Guid editorType, Guid logicalView, int hr) - { - IVsWindowFrame frame; - var mock = Mock.Get(project); - mock.Setup(h => h.OpenItemWithSpecific(It.IsAny(), It.IsAny(), ref editorType, It.IsAny(), ref logicalView, It.IsAny(), out frame)) - .Returns(hr); - } - - public static void ImplementOpenItemWithSpecific(this IVsProject4 project, Guid editorType, Guid logicalView, IVsWindowFrame? frame) - { - var mock = Mock.Get(project); - mock.Setup(h => h.OpenItemWithSpecific(It.IsAny(), It.IsAny(), ref editorType, It.IsAny(), ref logicalView, It.IsAny(), out frame)) - .Returns(0); - } - - public static void ImplementAddItemWithSpecific(this IVsProject4 project, Func addItemWithSpecificFunc) - { - var mock = Mock.Get(project); - Guid guidEditorType = Guid.Empty; - Guid rguidLogicalView = Guid.Empty; - mock.Setup(h => h.AddItemWithSpecific(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), ref guidEditorType, It.IsAny(), ref rguidLogicalView, It.IsAny())) - .Returns((itemId, op, itemName, cOpen, arrFiles, handle, flags, editorType, physView, logicalView, result) => - { - return addItemWithSpecificFunc(itemId, op, itemName, cOpen, arrFiles, result); - }); - } + var mock = Mock.Get(project); + Guid guidEditorType = Guid.Empty; + Guid rguidLogicalView = Guid.Empty; + mock.Setup(h => h.AddItemWithSpecific(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), ref guidEditorType, It.IsAny(), ref rguidLogicalView, It.IsAny())) + .Returns((itemId, op, itemName, cOpen, arrFiles, handle, flags, editorType, physView, logicalView, result) => + { + return addItemWithSpecificFunc(itemId, op, itemName, cOpen, arrFiles, result); + }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsServiceFactory.cs index 48152cf4e1..2aebe13bb1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsServiceFactory.cs @@ -1,28 +1,27 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IVsServiceFactory { - internal static class IVsServiceFactory + public static IVsService Create(T value) where T : class { - public static IVsService Create(T value) where T : class - { - var mock = new Mock>(); - mock.Setup(s => s.GetValueAsync(It.IsAny())) - .ReturnsAsync(() => value); + var mock = new Mock>(); + mock.Setup(s => s.GetValueAsync(It.IsAny())) + .ReturnsAsync(() => value); - return mock.Object; - } + return mock.Object; + } - public static IVsService Create(TInterface? value) - where TService : class where TInterface : class - { - var mock = new Mock>(); - mock.Setup(s => s.GetValueAsync(It.IsAny())) - .ReturnsAsync(() => value!); - mock.Setup(s => s.GetValueOrNullAsync(It.IsAny())) - .ReturnsAsync(() => value); + public static IVsService Create(TInterface? value) + where TService : class where TInterface : class + { + var mock = new Mock>(); + mock.Setup(s => s.GetValueAsync(It.IsAny())) + .ReturnsAsync(() => value!); + mock.Setup(s => s.GetValueOrNullAsync(It.IsAny())) + .ReturnsAsync(() => value); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionFactory.cs index 20975a530f..713674c2cd 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionFactory.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsSolutionFactory { - internal static class IVsSolutionFactory + public static FuncWithOut DefaultAdviseCallback => (IVsSolutionEvents events, out uint cookie) => { - public static FuncWithOut DefaultAdviseCallback => (IVsSolutionEvents events, out uint cookie) => - { - cookie = 0; - return VSConstants.S_OK; - }; + cookie = 0; + return VSConstants.S_OK; + }; - public static Func DefaultUnadviseCallback => (uint cookie) => VSConstants.S_OK; + public static Func DefaultUnadviseCallback => (uint cookie) => VSConstants.S_OK; - public static IVsSolution Create() => Mock.Of(); + public static IVsSolution Create() => Mock.Of(); - public static IVsSolution CreateWithSolutionDirectory(FuncWithOutThreeArgs func) - { - var mock = new Mock(); - string directory; - string solutionFile; - string userSettings; - mock.Setup(x => x.GetSolutionInfo(out directory, out solutionFile, out userSettings)).Returns(func); - return mock.Object; - } + public static IVsSolution CreateWithSolutionDirectory(FuncWithOutThreeArgs func) + { + var mock = new Mock(); + string directory; + string solutionFile; + string userSettings; + mock.Setup(x => x.GetSolutionInfo(out directory, out solutionFile, out userSettings)).Returns(func); + return mock.Object; + } - public static IVsSolution CreateWithAdviseUnadviseSolutionEvents(uint adviseCookie, bool? isFullyLoaded = null) + public static IVsSolution CreateWithAdviseUnadviseSolutionEvents(uint adviseCookie, bool? isFullyLoaded = null) + { + var mock = new Mock(); + mock.Setup(x => x.AdviseSolutionEvents(It.IsAny(), out adviseCookie)).Returns(VSConstants.S_OK); + mock.Setup(x => x.UnadviseSolutionEvents(It.IsAny())).Returns(VSConstants.S_OK); + if (isFullyLoaded != null) { - var mock = new Mock(); - mock.Setup(x => x.AdviseSolutionEvents(It.IsAny(), out adviseCookie)).Returns(VSConstants.S_OK); - mock.Setup(x => x.UnadviseSolutionEvents(It.IsAny())).Returns(VSConstants.S_OK); - if (isFullyLoaded != null) - { - object value = isFullyLoaded.Value; - mock.Setup(x => x.GetProperty(It.IsAny(), out value)).Returns(VSConstants.S_OK); - } - - return mock.Object; + object value = isFullyLoaded.Value; + mock.Setup(x => x.GetProperty(It.IsAny(), out value)).Returns(VSConstants.S_OK); } - public static IVsSolution Implement(FuncWithOut adviseCallback, - Func unadviseCallback, - FuncWithOutThreeArgs solutionInfoCallback) - { - var mock = new Mock(); - uint cookie; - string directory; - string solutionFile; - string userSettings; - - mock.Setup(x => x.AdviseSolutionEvents(It.IsAny(), out cookie)).Returns(adviseCallback); - mock.Setup(x => x.UnadviseSolutionEvents(It.IsAny())).Returns(unadviseCallback); - mock.Setup(x => x.GetSolutionInfo(out directory, out solutionFile, out userSettings)).Returns(solutionInfoCallback); - return mock.Object; - } + return mock.Object; + } + + public static IVsSolution Implement(FuncWithOut adviseCallback, + Func unadviseCallback, + FuncWithOutThreeArgs solutionInfoCallback) + { + var mock = new Mock(); + uint cookie; + string directory; + string solutionFile; + string userSettings; + + mock.Setup(x => x.AdviseSolutionEvents(It.IsAny(), out cookie)).Returns(adviseCallback); + mock.Setup(x => x.UnadviseSolutionEvents(It.IsAny())).Returns(unadviseCallback); + mock.Setup(x => x.GetSolutionInfo(out directory, out solutionFile, out userSettings)).Returns(solutionInfoCallback); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionRestoreServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionRestoreServiceFactory.cs index 0d1997b132..2042749f42 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionRestoreServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsSolutionRestoreServiceFactory.cs @@ -1,34 +1,33 @@ // 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.md file in the project root for more information. -namespace NuGet.SolutionRestoreManager +namespace NuGet.SolutionRestoreManager; + +internal class IVsSolutionRestoreServiceFactory { - internal class IVsSolutionRestoreServiceFactory - { - private readonly Mock _mock = new(); + private readonly Mock _mock = new(); - internal IVsSolutionRestoreServiceFactory WithNominateProjectAsync(Action action) - { - _mock.Setup(s => s.NominateProjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(action) - .ReturnsAsync(true); + internal IVsSolutionRestoreServiceFactory WithNominateProjectAsync(Action action) + { + _mock.Setup(s => s.NominateProjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(action) + .ReturnsAsync(true); - return this; - } + return this; + } - internal IVsSolutionRestoreServiceFactory WithRegisterRestoreInfoSourceAsync(Action? registerAction = null) + internal IVsSolutionRestoreServiceFactory WithRegisterRestoreInfoSourceAsync(Action? registerAction = null) + { + if (registerAction is not null) { - if (registerAction is not null) - { - _mock.Setup(s => s.RegisterRestoreInfoSourceAsync(It.IsAny(), It.IsAny())) - .Callback(registerAction); - } - - return this; + _mock.Setup(s => s.RegisterRestoreInfoSourceAsync(It.IsAny(), It.IsAny())) + .Callback(registerAction); } - internal IVsSolutionRestoreService5 Build() - { - return _mock.Object; - } + return this; + } + + internal IVsSolutionRestoreService5 Build() + { + return _mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsTaskFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsTaskFactory.cs index fcbe276978..37d5228875 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsTaskFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsTaskFactory.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsTaskFactory { - internal static class IVsTaskFactory + public static IVsTask FromResult(object? result) { - public static IVsTask FromResult(object? result) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(t => t.IsCompleted) - .Returns(true); - mock.Setup(t => t.GetResult()) - .Returns(result); + mock.Setup(t => t.IsCompleted) + .Returns(true); + mock.Setup(t => t.GetResult()) + .Returns(result); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsThreadedWaitDialogFactoryFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsThreadedWaitDialogFactoryFactory.cs index 75369c7802..1f9205338c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsThreadedWaitDialogFactoryFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsThreadedWaitDialogFactoryFactory.cs @@ -2,62 +2,61 @@ using Microsoft.VisualStudio.ProjectSystem.VS; -namespace Microsoft.VisualStudio.Shell.Interop -{ - internal static class IVsThreadedWaitDialogFactoryFactory - { - private delegate void CreateInstanceCallback(out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog); +namespace Microsoft.VisualStudio.Shell.Interop; - public static (IVsThreadedWaitDialogFactory dialogFactory, Action cancel) Create(string title = "", string message = "", bool isCancelable = false) - { - IVsThreadedWaitDialogCallback? callback = null; - var threadedWaitDialogFactoryMock = new Mock(); - var threadedWaitDialogMock = new Mock(); - threadedWaitDialogMock.Setup(m => m.StartWaitDialogWithCallback( - It.IsNotNull(), - It.IsNotNull(), - It.Is(s => s == null), - It.Is(s => s == null), - It.Is(s => s == null), - It.IsAny(), - It.IsInRange(0, int.MaxValue, Moq.Range.Inclusive), - It.Is(v => !v), - It.Is(i => i == 0), - It.Is(i => i == 0), - It.IsNotNull())) - .Callback((string szWaitCaption, - string szWaitMessage, - string szProgressText, - object varStatusBmpAnim, - string szStatusBarText, - bool fIsCancelable, - int iDelayToShowDialog, - bool fShowProgress, - int iTotalSteps, - int iCurrentStep, - IVsThreadedWaitDialogCallback pCallback) => - { - Assert.Equal(title, szWaitCaption); - Assert.Equal(message, szWaitMessage); - Assert.Equal(isCancelable, fIsCancelable); - callback = pCallback; - }); - threadedWaitDialogMock.Setup(m => m.EndWaitDialog(out It.Ref.IsAny)); - var threadedWaitDialog = threadedWaitDialogMock.Object; - threadedWaitDialogFactoryMock - .Setup(m => m.CreateInstance(out It.Ref.IsAny)) - .Callback(new CreateInstanceCallback((out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog) => - { - ppIVsThreadedWaitDialog = threadedWaitDialog; - })) - .Returns(HResult.OK); +internal static class IVsThreadedWaitDialogFactoryFactory +{ + private delegate void CreateInstanceCallback(out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog); - void Cancel() + public static (IVsThreadedWaitDialogFactory dialogFactory, Action cancel) Create(string title = "", string message = "", bool isCancelable = false) + { + IVsThreadedWaitDialogCallback? callback = null; + var threadedWaitDialogFactoryMock = new Mock(); + var threadedWaitDialogMock = new Mock(); + threadedWaitDialogMock.Setup(m => m.StartWaitDialogWithCallback( + It.IsNotNull(), + It.IsNotNull(), + It.Is(s => s == null), + It.Is(s => s == null), + It.Is(s => s == null), + It.IsAny(), + It.IsInRange(0, int.MaxValue, Moq.Range.Inclusive), + It.Is(v => !v), + It.Is(i => i == 0), + It.Is(i => i == 0), + It.IsNotNull())) + .Callback((string szWaitCaption, + string szWaitMessage, + string szProgressText, + object varStatusBmpAnim, + string szStatusBarText, + bool fIsCancelable, + int iDelayToShowDialog, + bool fShowProgress, + int iTotalSteps, + int iCurrentStep, + IVsThreadedWaitDialogCallback pCallback) => + { + Assert.Equal(title, szWaitCaption); + Assert.Equal(message, szWaitMessage); + Assert.Equal(isCancelable, fIsCancelable); + callback = pCallback; + }); + threadedWaitDialogMock.Setup(m => m.EndWaitDialog(out It.Ref.IsAny)); + var threadedWaitDialog = threadedWaitDialogMock.Object; + threadedWaitDialogFactoryMock + .Setup(m => m.CreateInstance(out It.Ref.IsAny)) + .Callback(new CreateInstanceCallback((out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog) => { - callback?.OnCanceled(); - } + ppIVsThreadedWaitDialog = threadedWaitDialog; + })) + .Returns(HResult.OK); - return (threadedWaitDialogFactoryMock.Object, Cancel); + void Cancel() + { + callback?.OnCanceled(); } + + return (threadedWaitDialogFactoryMock.Object, Cancel); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUIServiceFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUIServiceFactory.cs index 54d04c5c29..f52f932483 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUIServiceFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUIServiceFactory.cs @@ -1,26 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class IVsUIServiceFactory { - internal static class IVsUIServiceFactory + public static IVsUIService Create(T value) where T : class { - public static IVsUIService Create(T value) where T : class - { - var mock = new Mock>(); - mock.SetupGet(s => s.Value) - .Returns(() => value); + var mock = new Mock>(); + mock.SetupGet(s => s.Value) + .Returns(() => value); - return mock.Object; - } + return mock.Object; + } - public static IVsUIService Create(TInterface value) where TService : class - where TInterface : class? - { - var mock = new Mock>(); - mock.SetupGet(s => s.Value) - .Returns(() => value); + public static IVsUIService Create(TInterface value) where TService : class + where TInterface : class? + { + var mock = new Mock>(); + mock.SetupGet(s => s.Value) + .Returns(() => value); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEvents3Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEvents3Factory.cs index da9f282185..c79ad8a1e9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEvents3Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEvents3Factory.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsUpdateSolutionEvents3Factory { - internal static class IVsUpdateSolutionEvents3Factory + public static IVsUpdateSolutionEvents3 Create( + Action? onUpdateSolutionBegin = null, + Action? onUpdateSolutionCancel = null, + Action? onUpdateSolutionDone = null) { - public static IVsUpdateSolutionEvents3 Create( - Action? onUpdateSolutionBegin = null, - Action? onUpdateSolutionCancel = null, - Action? onUpdateSolutionDone = null) - { - var solutionEventsListener = new Mock(); + var solutionEventsListener = new Mock(); - var anyInt = It.IsAny(); - solutionEventsListener.As().Setup(b => b.UpdateSolution_Begin(ref anyInt)) - .Callback(() => onUpdateSolutionBegin?.Invoke()) - .Returns(VSConstants.S_OK); + var anyInt = It.IsAny(); + solutionEventsListener.As().Setup(b => b.UpdateSolution_Begin(ref anyInt)) + .Callback(() => onUpdateSolutionBegin?.Invoke()) + .Returns(VSConstants.S_OK); - solutionEventsListener.As().Setup(b => b.UpdateSolution_Cancel()) - .Callback(() => onUpdateSolutionCancel?.Invoke()) - .Returns(VSConstants.S_OK); + solutionEventsListener.As().Setup(b => b.UpdateSolution_Cancel()) + .Callback(() => onUpdateSolutionCancel?.Invoke()) + .Returns(VSConstants.S_OK); - solutionEventsListener.As().Setup(b => b.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(() => onUpdateSolutionDone?.Invoke()) - .Returns(VSConstants.S_OK); + solutionEventsListener.As().Setup(b => b.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(() => onUpdateSolutionDone?.Invoke()) + .Returns(VSConstants.S_OK); - return solutionEventsListener.Object; - } + return solutionEventsListener.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEventsFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEventsFactory.cs index b23ccfebec..0fc4388856 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEventsFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpdateSolutionEventsFactory.cs @@ -1,30 +1,29 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsUpdateSolutionEventsFactory { - internal static class IVsUpdateSolutionEventsFactory + public static IVsUpdateSolutionEvents Create( + Action? onUpdateSolutionBegin = null, + Action? onUpdateSolutionCancel = null, + Action? onUpdateSolutionDone = null) { - public static IVsUpdateSolutionEvents Create( - Action? onUpdateSolutionBegin = null, - Action? onUpdateSolutionCancel = null, - Action? onUpdateSolutionDone = null) - { - var solutionEventsListener = new Mock(); + var solutionEventsListener = new Mock(); - var anyInt = It.IsAny(); - solutionEventsListener.Setup(b => b.UpdateSolution_Begin(ref anyInt)) - .Callback(() => onUpdateSolutionBegin?.Invoke()) - .Returns(VSConstants.S_OK); + var anyInt = It.IsAny(); + solutionEventsListener.Setup(b => b.UpdateSolution_Begin(ref anyInt)) + .Callback(() => onUpdateSolutionBegin?.Invoke()) + .Returns(VSConstants.S_OK); - solutionEventsListener.Setup(b => b.UpdateSolution_Cancel()) - .Callback(() => onUpdateSolutionCancel?.Invoke()) - .Returns(VSConstants.S_OK); + solutionEventsListener.Setup(b => b.UpdateSolution_Cancel()) + .Callback(() => onUpdateSolutionCancel?.Invoke()) + .Returns(VSConstants.S_OK); - solutionEventsListener.Setup(b => b.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(() => onUpdateSolutionDone?.Invoke()) - .Returns(VSConstants.S_OK); + solutionEventsListener.Setup(b => b.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(() => onUpdateSolutionDone?.Invoke()) + .Returns(VSConstants.S_OK); - return solutionEventsListener.Object; - } + return solutionEventsListener.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpgradeLoggerFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpgradeLoggerFactory.cs index c33db3bbf4..69a43e135d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpgradeLoggerFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsUpgradeLoggerFactory.cs @@ -1,53 +1,52 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsUpgradeLoggerFactory { - internal static class IVsUpgradeLoggerFactory + public static IVsUpgradeLogger CreateLogger(IList messages) { - public static IVsUpgradeLogger CreateLogger(IList messages) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(pl => pl.LogMessage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((level, project, file, message) => messages.Add(new LogMessage(level, project, file, message))); + mock.Setup(pl => pl.LogMessage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((level, project, file, message) => messages.Add(new LogMessage(level, project, file, message))); - return mock.Object; - } + return mock.Object; } +} - internal class LogMessage +internal class LogMessage +{ + public uint Level { get; } + public string Project { get; } + public string File { get; } + public string Message { get; } + + public LogMessage(uint level, string project, string file, string message) { - public uint Level { get; } - public string Project { get; } - public string File { get; } - public string Message { get; } + Level = level; + Project = project; + File = file; + Message = message; + } - public LogMessage(uint level, string project, string file, string message) + public override bool Equals(object obj) + { + if (obj is null) { - Level = level; - Project = project; - File = file; - Message = message; + return false; } - public override bool Equals(object obj) + if (obj is not LogMessage other) { - if (obj is null) - { - return false; - } - - if (obj is not LogMessage other) - { - return false; - } - - return Level == other.Level && Project.Equals(other.Project) && File.Equals(other.File) && Message.Equals(other.Message); + return false; } - public override int GetHashCode() - { - return (Level.GetHashCode() * 31) + (Project.GetHashCode() * 3) + (File.GetHashCode() * 7) + (Message.GetHashCode() * 5); - } + return Level == other.Level && Project.Equals(other.Project) && File.Equals(other.File) && Message.Equals(other.Message); + } + + public override int GetHashCode() + { + return (Level.GetHashCode() * 31) + (Project.GetHashCode() * 3) + (File.GetHashCode() * 7) + (Message.GetHashCode() * 5); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsWindowFrameFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsWindowFrameFactory.cs index 52750ea509..dda58b7d6c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsWindowFrameFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IVsWindowFrameFactory.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal static class IVsWindowFrameFactory { - internal static class IVsWindowFrameFactory + public static IVsWindowFrame ImplementShow(Func action) { - public static IVsWindowFrame ImplementShow(Func action) - { - var mock = new Mock(); - mock.Setup(h => h.Show()) - .Returns(action()); + var mock = new Mock(); + mock.Setup(h => h.Show()) + .Returns(action()); - return mock.Object; - } + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceMockFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceMockFactory.cs index ea5ad13fc2..b95e92d48d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceMockFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceMockFactory.cs @@ -3,48 +3,47 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal static class IWorkspaceMockFactory { - internal static class IWorkspaceMockFactory + public static IWorkspace ImplementContextId(string contextId) { - public static IWorkspace ImplementContextId(string contextId) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(c => c.ContextId) - .Returns(contextId); + mock.Setup(c => c.ContextId) + .Returns(contextId); - return mock.Object; - } + return mock.Object; + } - public static IWorkspace ImplementContext(IWorkspaceProjectContext context, string? contextId = null) - { - var mock = new Mock(); + public static IWorkspace ImplementContext(IWorkspaceProjectContext context, string? contextId = null) + { + var mock = new Mock(); - mock.Setup(c => c.Context) - .Returns(context); + mock.Setup(c => c.Context) + .Returns(context); - mock.Setup(c => c.ContextId) - .Returns(contextId!); + mock.Setup(c => c.ContextId) + .Returns(contextId!); - return mock.Object; - } + return mock.Object; + } - public static IWorkspace ImplementErrorReporter(Func action) - { - var mock = new Mock(); + public static IWorkspace ImplementErrorReporter(Func action) + { + var mock = new Mock(); - mock.SetupGet(c => c.ErrorReporter) - .Returns(action); + mock.SetupGet(c => c.ErrorReporter) + .Returns(action); - return mock.Object; - } + return mock.Object; + } - public static IWorkspace Create() - { - var context = IWorkspaceProjectContextMockFactory.Create(); + public static IWorkspace Create() + { + var context = IWorkspaceProjectContextMockFactory.Create(); - return ImplementContext(context); - } + return ImplementContext(context); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs index e14b35e844..21f44a5565 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextFactoryFactory.cs @@ -2,31 +2,30 @@ using Moq.Language.Flow; -namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem +namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem; + +internal static class IWorkspaceProjectContextFactoryFactory { - internal static class IWorkspaceProjectContextFactoryFactory + public static IWorkspaceProjectContextFactory ImplementCreateProjectContext(Func action) { - public static IWorkspaceProjectContextFactory ImplementCreateProjectContext(Func action) - { - var mock = new Mock(MockBehavior.Strict); + var mock = new Mock(MockBehavior.Strict); - mock.SetupCreateProjectContext().ReturnsAsync(action); + mock.SetupCreateProjectContext().ReturnsAsync(action); - return mock.Object; - } + return mock.Object; + } - public static IWorkspaceProjectContextFactory ImplementCreateProjectContextThrows(Exception exception) - { - var mock = new Mock(MockBehavior.Strict); + public static IWorkspaceProjectContextFactory ImplementCreateProjectContextThrows(Exception exception) + { + var mock = new Mock(MockBehavior.Strict); - mock.SetupCreateProjectContext().Throws(exception); + mock.SetupCreateProjectContext().Throws(exception); - return mock.Object; - } + return mock.Object; + } - public static ISetup> SetupCreateProjectContext(this Mock mock) - { - return mock.Setup(c => c.CreateProjectContextAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); - } + public static ISetup> SetupCreateProjectContext(this Mock mock) + { + return mock.Setup(c => c.CreateProjectContextAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMock.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMock.cs index cab67c146c..c7a08be801 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMock.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMock.cs @@ -1,19 +1,18 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem +namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem; + +internal class IWorkspaceProjectContextMock : AbstractMock { - internal class IWorkspaceProjectContextMock : AbstractMock + public IWorkspaceProjectContextMock() { - public IWorkspaceProjectContextMock() - { - SetupAllProperties(); + SetupAllProperties(); - Mock batchScopeDisposable = new(); + Mock batchScopeDisposable = new(); - batchScopeDisposable.Setup(o => o.DisposeAsync()); + batchScopeDisposable.Setup(o => o.DisposeAsync()); - Setup(o => o.CreateBatchScopeAsync(It.IsAny())) - .Returns(new ValueTask(batchScopeDisposable.Object)); - } + Setup(o => o.CreateBatchScopeAsync(It.IsAny())) + .Returns(new ValueTask(batchScopeDisposable.Object)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMockFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMockFactory.cs index 9bfa70f6db..4401950664 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMockFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceProjectContextMockFactory.cs @@ -3,83 +3,82 @@ using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.ProjectSystem; -namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem +namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem; + +internal static class IWorkspaceProjectContextMockFactory { - internal static class IWorkspaceProjectContextMockFactory + public static IWorkspaceProjectContext ImplementSetProperty(Action action) { - public static IWorkspaceProjectContext ImplementSetProperty(Action action) - { - var mock = new Mock(); + var mock = new Mock(); - mock.Setup(c => c.SetProperty(It.IsAny(), It.IsAny())) - .Callback(action); + mock.Setup(c => c.SetProperty(It.IsAny(), It.IsAny())) + .Callback(action); - return mock.Object; - } + return mock.Object; + } + + public static IWorkspaceProjectContext Create() + { + return new IWorkspaceProjectContextMock().Object; + } + + public static IWorkspaceProjectContext CreateForDynamicFiles(UnconfiguredProject project, Action? addDynamicFile = null) + { + var context = new IWorkspaceProjectContextMock(); - public static IWorkspaceProjectContext Create() + context.SetupGet(c => c.ProjectFilePath) + .Returns(project.FullPath); + + if (addDynamicFile is not null) { - return new IWorkspaceProjectContextMock().Object; + context.Setup(c => c.AddDynamicFile(It.IsAny(), It.IsAny>())) + .Callback>((p1, p2) => addDynamicFile(p1)); } - public static IWorkspaceProjectContext CreateForDynamicFiles(UnconfiguredProject project, Action? addDynamicFile = null) - { - var context = new IWorkspaceProjectContextMock(); + return context.Object; + } - context.SetupGet(c => c.ProjectFilePath) - .Returns(project.FullPath); + public static IWorkspaceProjectContext CreateForSourceFiles(UnconfiguredProject project, Action? addSourceFile = null, Action? removeSourceFile = null) + { + var context = new IWorkspaceProjectContextMock(); - if (addDynamicFile is not null) - { - context.Setup(c => c.AddDynamicFile(It.IsAny(), It.IsAny>())) - .Callback>((p1, p2) => addDynamicFile(p1)); - } + context.SetupGet(c => c.ProjectFilePath) + .Returns(project.FullPath); - return context.Object; + if (addSourceFile is not null) + { + context.Setup(c => c.AddSourceFile(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Callback, SourceCodeKind>((p1, p2, p3, p4) => addSourceFile(p1)); } - public static IWorkspaceProjectContext CreateForSourceFiles(UnconfiguredProject project, Action? addSourceFile = null, Action? removeSourceFile = null) + if (removeSourceFile is not null) { - var context = new IWorkspaceProjectContextMock(); + context.Setup(c => c.RemoveSourceFile(It.IsAny())) + .Callback(removeSourceFile); + } - context.SetupGet(c => c.ProjectFilePath) - .Returns(project.FullPath); + return context.Object; + } - if (addSourceFile is not null) - { - context.Setup(c => c.AddSourceFile(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) - .Callback, SourceCodeKind>((p1, p2, p3, p4) => addSourceFile(p1)); - } + public static IWorkspaceProjectContext CreateForMetadataReferences(UnconfiguredProject project, Action? addMetadataReference = null, Action? removeMetadataReference = null) + { + var context = new Mock(); - if (removeSourceFile is not null) - { - context.Setup(c => c.RemoveSourceFile(It.IsAny())) - .Callback(removeSourceFile); - } + context.SetupGet(c => c.ProjectFilePath) + .Returns(project.FullPath); - return context.Object; + if (addMetadataReference is not null) + { + context.Setup(c => c.AddMetadataReference(It.IsAny(), It.IsAny())) + .Callback((p1, p2) => addMetadataReference(p1)); } - public static IWorkspaceProjectContext CreateForMetadataReferences(UnconfiguredProject project, Action? addMetadataReference = null, Action? removeMetadataReference = null) + if (removeMetadataReference is not null) { - var context = new Mock(); - - context.SetupGet(c => c.ProjectFilePath) - .Returns(project.FullPath); - - if (addMetadataReference is not null) - { - context.Setup(c => c.AddMetadataReference(It.IsAny(), It.IsAny())) - .Callback((p1, p2) => addMetadataReference(p1)); - } - - if (removeMetadataReference is not null) - { - context.Setup(c => c.RemoveMetadataReference(It.IsAny())) - .Callback(removeMetadataReference); - } - - return context.Object; + context.Setup(c => c.RemoveMetadataReference(It.IsAny())) + .Callback(removeMetadataReference); } + + return context.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceWriterFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceWriterFactory.cs index 0a8cac60ea..693112d216 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceWriterFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/IWorkspaceWriterFactory.cs @@ -3,62 +3,61 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices; + +internal static class IWorkspaceWriterFactory { - internal static class IWorkspaceWriterFactory + public static IWorkspaceWriter Create() + { + return Mock.Of(); + } + + public static IWorkspaceWriter ImplementContextId(string contextId) + { + var workspace = IWorkspaceMockFactory.ImplementContextId(contextId); + + return new WorkspaceWriter(workspace); + } + + public static IWorkspaceWriter ImplementErrorReporter(Func func) + { + var workspace = IWorkspaceMockFactory.ImplementErrorReporter(func); + + return new WorkspaceWriter(workspace); + } + + public static IWorkspaceWriter ImplementProjectContextAccessor(IWorkspace workspace) { - public static IWorkspaceWriter Create() + return new WorkspaceWriter(workspace); + } + + private class WorkspaceWriter : IWorkspaceWriter + { + private readonly IWorkspace _workspace; + + public WorkspaceWriter(IWorkspace workspace) { - return Mock.Of(); + _workspace = workspace; } - public static IWorkspaceWriter ImplementContextId(string contextId) + public Task IsEnabledAsync(CancellationToken cancellationToken = default) { - var workspace = IWorkspaceMockFactory.ImplementContextId(contextId); - - return new WorkspaceWriter(workspace); + return TaskResult.True; } - public static IWorkspaceWriter ImplementErrorReporter(Func func) + public Task WhenInitialized(CancellationToken cancellationToken = default) { - var workspace = IWorkspaceMockFactory.ImplementErrorReporter(func); - - return new WorkspaceWriter(workspace); + return Task.CompletedTask; } - public static IWorkspaceWriter ImplementProjectContextAccessor(IWorkspace workspace) + public Task WriteAsync(Func action, CancellationToken cancellationToken = default) { - return new WorkspaceWriter(workspace); + return action(_workspace); } - private class WorkspaceWriter : IWorkspaceWriter + public Task WriteAsync(Func> func, CancellationToken cancellationToken = default) { - private readonly IWorkspace _workspace; - - public WorkspaceWriter(IWorkspace workspace) - { - _workspace = workspace; - } - - public Task IsEnabledAsync(CancellationToken cancellationToken = default) - { - return TaskResult.True; - } - - public Task WhenInitialized(CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } - - public Task WriteAsync(Func action, CancellationToken cancellationToken = default) - { - return action(_workspace); - } - - public Task WriteAsync(Func> func, CancellationToken cancellationToken = default) - { - return func(_workspace)!; - } + return func(_workspace)!; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ProjectFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ProjectFactory.cs index b9cd9772ae..e13abd0aa0 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ProjectFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/ProjectFactory.cs @@ -2,37 +2,36 @@ using EnvDTE80; -namespace EnvDTE +namespace EnvDTE; + +internal static class ProjectFactory { - internal static class ProjectFactory + public static Project Create() + { + return Mock.Of(); + } + + public static Project ImplementObject(Func action) + { + var mock = new Mock(); + mock.SetupGet(p => p.Object) + .Returns(action); + + return mock.Object; + } + + public static Project CreateWithSolution(Solution2 solution) + { + var mock = new Mock(); + + mock.SetupGet(p => p.DTE.Solution).Returns((Solution)solution); + + return mock.Object; + } + + internal static void ImplementCodeModelLanguage(Project project, string language) { - public static Project Create() - { - return Mock.Of(); - } - - public static Project ImplementObject(Func action) - { - var mock = new Mock(); - mock.SetupGet(p => p.Object) - .Returns(action); - - return mock.Object; - } - - public static Project CreateWithSolution(Solution2 solution) - { - var mock = new Mock(); - - mock.SetupGet(p => p.DTE.Solution).Returns((Solution)solution); - - return mock.Object; - } - - internal static void ImplementCodeModelLanguage(Project project, string language) - { - var mock = Mock.Get(project); - mock.SetupGet(p => p.CodeModel.Language).Returns(language); - } + var mock = Mock.Get(project); + mock.SetupGet(p => p.CodeModel.Language).Returns(language); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/PropertiesAvailableStatusFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/PropertiesAvailableStatusFactory.cs index 318509f6ca..f46b72e3d2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/PropertiesAvailableStatusFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/PropertiesAvailableStatusFactory.cs @@ -2,138 +2,137 @@ using Microsoft.VisualStudio.ProjectSystem.Query; -namespace Microsoft.VisualStudio.ProjectSystem +namespace Microsoft.VisualStudio.ProjectSystem; + +internal static class PropertiesAvailableStatusFactory { - internal static class PropertiesAvailableStatusFactory + public static IUIEditorMetadataPropertiesAvailableStatus CreateUIEditorMetadataAvailableStatus( + bool includeAllProperties = false, + bool? includeName = null, + bool? includeValue = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); + mock.SetupGet(m => m.Value).Returns(includeValue ?? includeAllProperties); + + return mock.Object; + } + + public static IUIPropertyPropertiesAvailableStatus CreateUIPropertyPropertiesAvailableStatus( + bool includeAllProperties = false, + bool? includeName = null, + bool? includeDisplayName = null, + bool? includeDescription = null, + bool? includeConfigurationIndependent = null, + bool? includeHelpUrl = null, + bool? includeCategoryName = null, + bool? includeIsVisible = null, + bool? includeOrder = null, + bool? includeType = null, + bool? includeSearchTerms = null, + bool? includeDependsOn = null, + bool? includeVisibilityCondition = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); + mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); + mock.SetupGet(m => m.Description).Returns(includeDescription ?? includeAllProperties); + mock.SetupGet(m => m.ConfigurationIndependent).Returns(includeConfigurationIndependent ?? includeAllProperties); + mock.SetupGet(m => m.HelpUrl).Returns(includeHelpUrl ?? includeAllProperties); + mock.SetupGet(m => m.CategoryName).Returns(includeCategoryName ?? includeAllProperties); + mock.SetupGet(m => m.IsVisible).Returns(includeIsVisible ?? includeAllProperties); + mock.SetupGet(m => m.Order).Returns(includeOrder ?? includeAllProperties); + mock.SetupGet(m => m.Type).Returns(includeType ?? includeAllProperties); + mock.SetupGet(m => m.SearchTerms).Returns(includeSearchTerms ?? includeAllProperties); + mock.SetupGet(m => m.DependsOn).Returns(includeDependsOn ?? includeAllProperties); + mock.SetupGet(m => m.VisibilityCondition).Returns(includeVisibilityCondition ?? includeAllProperties); + + return mock.Object; + } + + public static ISupportedValuePropertiesAvailableStatus CreateSupportedValuesPropertiesAvailableStatus( + bool includeAllProperties = false, + bool? includeDisplayName = null, + bool? includeValue = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); + mock.SetupGet(m => m.Value).Returns(includeValue ?? includeAllProperties); + + return mock.Object; + } + + public static ICategoryPropertiesAvailableStatus CreateCategoryPropertiesAvailableStatus( + bool includeAllProperties = false, + bool? includeDisplayName = null, + bool? includeName = null, + bool? includeOrder = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); + mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); + mock.SetupGet(m => m.Order).Returns(includeOrder ?? includeAllProperties); + + return mock.Object; + } + + public static IConfigurationDimensionPropertiesAvailableStatus CreateConfigurationDimensionAvailableStatus( + bool includeAllProperties = false, + bool? includeName = null, + bool? includeValue = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); + mock.SetupGet(m => m.Value).Returns(includeValue ?? includeAllProperties); + + return mock.Object; + } + + public static IPropertyPagePropertiesAvailableStatus CreatePropertyPagePropertiesAvailableStatus( + bool includeAllProperties = false, + bool? includeName = null, + bool? includeDisplayName = null, + bool? includeOrder = null, + bool? includeKind = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); + mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); + mock.SetupGet(m => m.Order).Returns(includeOrder ?? includeAllProperties); + mock.SetupGet(m => m.Kind).Returns(includeKind ?? includeAllProperties); + + return mock.Object; + } + + public static IUIPropertyEditorPropertiesAvailableStatus CreateUIPropertyEditorPropertiesAvailableStatus( + bool includeAllProperties = false, + bool? includeName = null, + bool? includeMetadata = null) { - public static IUIEditorMetadataPropertiesAvailableStatus CreateUIEditorMetadataAvailableStatus( - bool includeAllProperties = false, - bool? includeName = null, - bool? includeValue = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); - mock.SetupGet(m => m.Value).Returns(includeValue ?? includeAllProperties); - - return mock.Object; - } - - public static IUIPropertyPropertiesAvailableStatus CreateUIPropertyPropertiesAvailableStatus( - bool includeAllProperties = false, - bool? includeName = null, - bool? includeDisplayName = null, - bool? includeDescription = null, - bool? includeConfigurationIndependent = null, - bool? includeHelpUrl = null, - bool? includeCategoryName = null, - bool? includeIsVisible = null, - bool? includeOrder = null, - bool? includeType = null, - bool? includeSearchTerms = null, - bool? includeDependsOn = null, - bool? includeVisibilityCondition = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); - mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); - mock.SetupGet(m => m.Description).Returns(includeDescription ?? includeAllProperties); - mock.SetupGet(m => m.ConfigurationIndependent).Returns(includeConfigurationIndependent ?? includeAllProperties); - mock.SetupGet(m => m.HelpUrl).Returns(includeHelpUrl ?? includeAllProperties); - mock.SetupGet(m => m.CategoryName).Returns(includeCategoryName ?? includeAllProperties); - mock.SetupGet(m => m.IsVisible).Returns(includeIsVisible ?? includeAllProperties); - mock.SetupGet(m => m.Order).Returns(includeOrder ?? includeAllProperties); - mock.SetupGet(m => m.Type).Returns(includeType ?? includeAllProperties); - mock.SetupGet(m => m.SearchTerms).Returns(includeSearchTerms ?? includeAllProperties); - mock.SetupGet(m => m.DependsOn).Returns(includeDependsOn ?? includeAllProperties); - mock.SetupGet(m => m.VisibilityCondition).Returns(includeVisibilityCondition ?? includeAllProperties); - - return mock.Object; - } - - public static ISupportedValuePropertiesAvailableStatus CreateSupportedValuesPropertiesAvailableStatus( - bool includeAllProperties = false, - bool? includeDisplayName = null, - bool? includeValue = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); - mock.SetupGet(m => m.Value).Returns(includeValue ?? includeAllProperties); - - return mock.Object; - } - - public static ICategoryPropertiesAvailableStatus CreateCategoryPropertiesAvailableStatus( - bool includeAllProperties = false, - bool? includeDisplayName = null, - bool? includeName = null, - bool? includeOrder = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); - mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); - mock.SetupGet(m => m.Order).Returns(includeOrder ?? includeAllProperties); - - return mock.Object; - } - - public static IConfigurationDimensionPropertiesAvailableStatus CreateConfigurationDimensionAvailableStatus( - bool includeAllProperties = false, - bool? includeName = null, - bool? includeValue = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); - mock.SetupGet(m => m.Value).Returns(includeValue ?? includeAllProperties); - - return mock.Object; - } - - public static IPropertyPagePropertiesAvailableStatus CreatePropertyPagePropertiesAvailableStatus( - bool includeAllProperties = false, - bool? includeName = null, - bool? includeDisplayName = null, - bool? includeOrder = null, - bool? includeKind = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); - mock.SetupGet(m => m.DisplayName).Returns(includeDisplayName ?? includeAllProperties); - mock.SetupGet(m => m.Order).Returns(includeOrder ?? includeAllProperties); - mock.SetupGet(m => m.Kind).Returns(includeKind ?? includeAllProperties); - - return mock.Object; - } - - public static IUIPropertyEditorPropertiesAvailableStatus CreateUIPropertyEditorPropertiesAvailableStatus( - bool includeAllProperties = false, - bool? includeName = null, - bool? includeMetadata = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); - mock.SetupGet(m => m.Metadata).Returns(includeMetadata ?? includeAllProperties); - - return mock.Object; - } - - public static IUIPropertyValuePropertiesAvailableStatus CreateUIPropertyValuePropertiesAvailableStatus( - bool includeAllProperties = false, - bool? includeEvaluatedValue = null, - bool? includeUnevaluatedValue = null) - { - var mock = new Mock(); - - mock.SetupGet(m => m.EvaluatedValue).Returns(includeEvaluatedValue ?? includeAllProperties); - mock.SetupGet(m => m.UnevaluatedValue).Returns(includeUnevaluatedValue ?? includeAllProperties); - - return mock.Object; - } + var mock = new Mock(); + + mock.SetupGet(m => m.Name).Returns(includeName ?? includeAllProperties); + mock.SetupGet(m => m.Metadata).Returns(includeMetadata ?? includeAllProperties); + + return mock.Object; + } + + public static IUIPropertyValuePropertiesAvailableStatus CreateUIPropertyValuePropertiesAvailableStatus( + bool includeAllProperties = false, + bool? includeEvaluatedValue = null, + bool? includeUnevaluatedValue = null) + { + var mock = new Mock(); + + mock.SetupGet(m => m.EvaluatedValue).Returns(includeEvaluatedValue ?? includeAllProperties); + mock.SetupGet(m => m.UnevaluatedValue).Returns(includeUnevaluatedValue ?? includeAllProperties); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/Reference3Factory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/Reference3Factory.cs index 062d2c219f..597e6feadd 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/Reference3Factory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/Reference3Factory.cs @@ -2,46 +2,45 @@ using VSLangProj; -namespace VSLangProj80 +namespace VSLangProj80; + +internal static class Reference3Factory { - internal static class Reference3Factory + public static Reference3 CreateComReference() + { + var mock = new Mock(); + mock.SetupGet(r => r.Type) + .Returns(prjReferenceType.prjReferenceTypeActiveX); + + return mock.Object; + } + + public static Reference3 CreateAssemblyReference( + string name, + string? version = null, + string? path = null, + prjReferenceType type = prjReferenceType.prjReferenceTypeAssembly, + __PROJECTREFERENCETYPE refType = __PROJECTREFERENCETYPE.PROJREFTYPE_ASSEMBLY) { - public static Reference3 CreateComReference() - { - var mock = new Mock(); - mock.SetupGet(r => r.Type) - .Returns(prjReferenceType.prjReferenceTypeActiveX); - - return mock.Object; - } - - public static Reference3 CreateAssemblyReference( - string name, - string? version = null, - string? path = null, - prjReferenceType type = prjReferenceType.prjReferenceTypeAssembly, - __PROJECTREFERENCETYPE refType = __PROJECTREFERENCETYPE.PROJREFTYPE_ASSEMBLY) - { - var mock = new Mock(); - mock.SetupGet(r => r.Name) - .Returns(name); - - mock.SetupGet(r => r.Version) - .Returns(version); - - mock.SetupGet(r => r.Path) - .Returns(path); - - mock.SetupGet(r => r.Resolved) - .Returns(path is not null); - - mock.SetupGet(r => r.Type) - .Returns(type); - - mock.SetupGet(r => r.RefType) - .Returns((uint)refType); - - return mock.Object; - } + var mock = new Mock(); + mock.SetupGet(r => r.Name) + .Returns(name); + + mock.SetupGet(r => r.Version) + .Returns(version); + + mock.SetupGet(r => r.Path) + .Returns(path); + + mock.SetupGet(r => r.Resolved) + .Returns(path is not null); + + mock.SetupGet(r => r.Type) + .Returns(type); + + mock.SetupGet(r => r.RefType) + .Returns((uint)refType); + + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SVsServiceProviderFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SVsServiceProviderFactory.cs index 13e7327d3e..1036377d4d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SVsServiceProviderFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SVsServiceProviderFactory.cs @@ -2,15 +2,14 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal static class SVsServiceProviderFactory { - internal static class SVsServiceProviderFactory + public static SVsServiceProvider Create(object? service = null) { - public static SVsServiceProvider Create(object? service = null) - { - var mock = new Mock(); - mock.Setup(s => s.GetService(It.IsAny())).Returns(service); - return mock.Object; - } + var mock = new Mock(); + mock.Setup(s => s.GetService(It.IsAny())).Returns(service); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SolutionFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SolutionFactory.cs index 220203a516..7eca3888b4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SolutionFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/SolutionFactory.cs @@ -2,32 +2,31 @@ using EnvDTE; -namespace EnvDTE80 +namespace EnvDTE80; + +internal static class SolutionFactory { - internal static class SolutionFactory + public static Solution Create() { - public static Solution Create() - { - var mock = new Mock(); - return mock.As().Object; - } + var mock = new Mock(); + return mock.As().Object; + } - public static Solution2 CreateWithGetProjectItemTemplate(Func projectItemsTemplatePathFunc) - { - var mock = new Mock(); - mock.As(); - mock.Setup(s => s.GetProjectItemTemplate(It.IsAny(), It.IsAny())) - .Returns(projectItemsTemplatePathFunc); + public static Solution2 CreateWithGetProjectItemTemplate(Func projectItemsTemplatePathFunc) + { + var mock = new Mock(); + mock.As(); + mock.Setup(s => s.GetProjectItemTemplate(It.IsAny(), It.IsAny())) + .Returns(projectItemsTemplatePathFunc); - return mock.Object; - } + return mock.Object; + } - public static Solution ImplementFindProjectItem(Func callback) - { - var mock = new Mock(); - mock.As(); - mock.Setup(m => m.FindProjectItem(It.IsAny())).Returns(callback); - return mock.Object; - } + public static Solution ImplementFindProjectItem(Func callback) + { + var mock = new Mock(); + mock.As(); + mock.Setup(m => m.FindProjectItem(It.IsAny())).Returns(callback); + return mock.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VSProjectFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VSProjectFactory.cs index 325d7fbb93..c57b2ef0ad 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VSProjectFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VSProjectFactory.cs @@ -3,52 +3,51 @@ using System.Collections; using EnvDTE; -namespace VSLangProj +namespace VSLangProj; + +internal static class VSProjectFactory { - internal static class VSProjectFactory + public static VSProject ImplementReferences(IEnumerable references) { - public static VSProject ImplementReferences(IEnumerable references) - { - var vsProjectReferences = new VSProjectReferences(references); + var vsProjectReferences = new VSProjectReferences(references); - var mock = new Mock(); - mock.SetupGet(p => p.References) - .Returns(vsProjectReferences); + var mock = new Mock(); + mock.SetupGet(p => p.References) + .Returns(vsProjectReferences); - return mock.Object; - } + return mock.Object; + } - private class VSProjectReferences : References - { - private readonly IEnumerable _references; + private class VSProjectReferences : References + { + private readonly IEnumerable _references; - public VSProjectReferences(IEnumerable references) - { - _references = references; - } + public VSProjectReferences(IEnumerable references) + { + _references = references; + } - public IEnumerator GetEnumerator() - { - return _references.GetEnumerator(); - } + public IEnumerator GetEnumerator() + { + return _references.GetEnumerator(); + } - public Reference Item(object index) => throw new NotImplementedException(); + public Reference Item(object index) => throw new NotImplementedException(); - public Reference Find(string bstrIdentity) => throw new NotImplementedException(); + public Reference Find(string bstrIdentity) => throw new NotImplementedException(); - public Reference Add(string bstrPath) => throw new NotImplementedException(); + public Reference Add(string bstrPath) => throw new NotImplementedException(); - public Reference AddActiveX(string bstrTypeLibGuid, int lMajorVer = 0, int lMinorVer = 0, int lLocaleId = 0, string bstrWrapperTool = "") => throw new NotImplementedException(); + public Reference AddActiveX(string bstrTypeLibGuid, int lMajorVer = 0, int lMinorVer = 0, int lLocaleId = 0, string bstrWrapperTool = "") => throw new NotImplementedException(); - public Reference AddProject(Project pProject) => throw new NotImplementedException(); + public Reference AddProject(Project pProject) => throw new NotImplementedException(); - public DTE DTE => throw new NotImplementedException(); + public DTE DTE => throw new NotImplementedException(); - public object Parent => throw new NotImplementedException(); + public object Parent => throw new NotImplementedException(); - public Project ContainingProject => throw new NotImplementedException(); + public Project ContainingProject => throw new NotImplementedException(); - public int Count => throw new NotImplementedException(); - } + public int Count => throw new NotImplementedException(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VisualBasicNamespaceImportsListFactory.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VisualBasicNamespaceImportsListFactory.cs index 7f34c6f494..0ae09d9f7a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VisualBasicNamespaceImportsListFactory.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VisualBasicNamespaceImportsListFactory.cs @@ -1,84 +1,83 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +internal static class VisualBasicNamespaceImportsListFactory { - internal static class VisualBasicNamespaceImportsListFactory + public static TestDotNetNamespaceImportsList CreateInstance(params string[] list) { - public static TestDotNetNamespaceImportsList CreateInstance(params string[] list) - { - var newList = new TestDotNetNamespaceImportsList( - UnconfiguredProjectFactory.Create(), - IProjectThreadingServiceFactory.Create(), - IActiveConfiguredProjectSubscriptionServiceFactory.Create()); + var newList = new TestDotNetNamespaceImportsList( + UnconfiguredProjectFactory.Create(), + IProjectThreadingServiceFactory.Create(), + IActiveConfiguredProjectSubscriptionServiceFactory.Create()); - newList.VSImports = new Lazy(() => new TestDotNetVsImports( - Mock.Of(), - IProjectThreadingServiceFactory.Create(), - IActiveConfiguredValueFactory.ImplementValue(()=> ConfiguredProjectFactory.Create()), - IProjectAccessorFactory.Create(), - IUnconfiguredProjectVsServicesFactory.Create(), - newList)); + newList.VSImports = new Lazy(() => new TestDotNetVsImports( + Mock.Of(), + IProjectThreadingServiceFactory.Create(), + IActiveConfiguredValueFactory.ImplementValue(()=> ConfiguredProjectFactory.Create()), + IProjectAccessorFactory.Create(), + IUnconfiguredProjectVsServicesFactory.Create(), + newList)); - newList.TestApply(list); + newList.TestApply(list); - newList.ImportsAdded.Clear(); - newList.ImportsRemoved.Clear(); + newList.ImportsAdded.Clear(); + newList.ImportsRemoved.Clear(); - return newList; - } + return newList; + } - internal class TestDotNetNamespaceImportsList : DotNetNamespaceImportsList + internal class TestDotNetNamespaceImportsList : DotNetNamespaceImportsList + { + public TestDotNetNamespaceImportsList(UnconfiguredProject project, + IProjectThreadingService threadingService, + IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscriptionService) + : base(project, threadingService, activeConfiguredProjectSubscriptionService) { - public TestDotNetNamespaceImportsList(UnconfiguredProject project, - IProjectThreadingService threadingService, - IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscriptionService) - : base(project, threadingService, activeConfiguredProjectSubscriptionService) - { - SkipInitialization = true; - } + SkipInitialization = true; + } - public List ImportsAdded { get; } = new List(); + public List ImportsAdded { get; } = new List(); - public List ImportsRemoved { get; } = new List(); + public List ImportsRemoved { get; } = new List(); - internal void TestApply(IProjectSubscriptionUpdate projectSubscriptionUpdate) - { - var projectVersionedValue = IProjectVersionedValueFactory.Create(projectSubscriptionUpdate); + internal void TestApply(IProjectSubscriptionUpdate projectSubscriptionUpdate) + { + var projectVersionedValue = IProjectVersionedValueFactory.Create(projectSubscriptionUpdate); - var result = PreprocessAsync(projectVersionedValue, null); + var result = PreprocessAsync(projectVersionedValue, null); - _ = ApplyAsync(result.Result!); - } + _ = ApplyAsync(result.Result!); + } - internal void TestApply(string[] list) - { - _ = ApplyAsync(IProjectVersionedValueFactory.Create(ImmutableList.CreateRange(list))); - } + internal void TestApply(string[] list) + { + _ = ApplyAsync(IProjectVersionedValueFactory.Create(ImmutableList.CreateRange(list))); + } + } + + private class TestDotNetVsImports : DotNetVSImports + { + private readonly TestDotNetNamespaceImportsList _testImportsList; + public TestDotNetVsImports(VSLangProj.VSProject vsProject, + IProjectThreadingService threadingService, + IActiveConfiguredValue activeConfiguredProject, + IProjectAccessor projectAccessor, + IUnconfiguredProjectVsServices unconfiguredProjectVSServices, + TestDotNetNamespaceImportsList importsList) + : base(vsProject, threadingService, activeConfiguredProject, projectAccessor, unconfiguredProjectVSServices, importsList) + { + _testImportsList = importsList; + } + + internal override void OnImportAdded(string importNamespace) + { + _testImportsList.ImportsAdded.Add(importNamespace); } - private class TestDotNetVsImports : DotNetVSImports + internal override void OnImportRemoved(string importNamespace) { - private readonly TestDotNetNamespaceImportsList _testImportsList; - public TestDotNetVsImports(VSLangProj.VSProject vsProject, - IProjectThreadingService threadingService, - IActiveConfiguredValue activeConfiguredProject, - IProjectAccessor projectAccessor, - IUnconfiguredProjectVsServices unconfiguredProjectVSServices, - TestDotNetNamespaceImportsList importsList) - : base(vsProject, threadingService, activeConfiguredProject, projectAccessor, unconfiguredProjectVSServices, importsList) - { - _testImportsList = importsList; - } - - internal override void OnImportAdded(string importNamespace) - { - _testImportsList.ImportsAdded.Add(importNamespace); - } - - internal override void OnImportRemoved(string importNamespace) - { - _testImportsList.ImportsRemoved.Add(importNamespace); - } + _testImportsList.ImportsRemoved.Add(importNamespace); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VsStartupProjectsListService.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VsStartupProjectsListService.cs index d037209dd8..acc9890a4d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VsStartupProjectsListService.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Mocks/VsStartupProjectsListService.cs @@ -1,20 +1,19 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell.Interop +namespace Microsoft.VisualStudio.Shell.Interop; + +internal class VsStartupProjectsListService : IVsStartupProjectsListService { - internal class VsStartupProjectsListService : IVsStartupProjectsListService - { - public Guid? ProjectGuid { get; private set; } + public Guid? ProjectGuid { get; private set; } - public void AddProject(ref Guid guidProject) - { - ProjectGuid = guidProject; - } + public void AddProject(ref Guid guidProject) + { + ProjectGuid = guidProject; + } - public void RemoveProject(ref Guid guidProject) - { - if (guidProject == ProjectGuid) - ProjectGuid = null; - } + public void RemoveProject(ref Guid guidProject) + { + if (guidProject == ProjectGuid) + ProjectGuid = null; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProviderTests.cs index cea5c7f175..e6134236c4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/CSharp/CSharpExtenderCATIDProviderTests.cs @@ -3,46 +3,45 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Properties; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.CSharp; + +public class CSharpExtenderCATIDProviderTests { - public class CSharpExtenderCATIDProviderTests + [Fact] + public void GetExtenderCATID_CatIdsUnknown_ReturnsNull() + { + var provider = CreateInstance(); + + var result = provider.GetExtenderCATID(ExtenderCATIDType.Unknown, treeNode: null); + + Assert.Null(result); + } + + [Theory] + [InlineData(ExtenderCATIDType.HierarchyExtensionObject, PrjCATID.prjCATIDProject)] + [InlineData(ExtenderCATIDType.AutomationProject, PrjCATID.prjCATIDProject)] + [InlineData(ExtenderCATIDType.AutomationProjectItem, PrjCATID.prjCATIDProjectItem)] + [InlineData(ExtenderCATIDType.HierarchyBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject)] + [InlineData(ExtenderCATIDType.HierarchyConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectConfigBrowseObject)] + [InlineData(ExtenderCATIDType.AutomationReference, PrjBrowseObjectCATID.prjCATIDCSharpReferenceBrowseObject)] + [InlineData(ExtenderCATIDType.ReferenceBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpReferenceBrowseObject)] + [InlineData(ExtenderCATIDType.ProjectBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject)] + [InlineData(ExtenderCATIDType.ProjectConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectConfigBrowseObject)] + [InlineData(ExtenderCATIDType.FileBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpFileBrowseObject)] + [InlineData(ExtenderCATIDType.AutomationFolderProperties, PrjBrowseObjectCATID.prjCATIDCSharpFolderBrowseObject)] + [InlineData(ExtenderCATIDType.FolderBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpFolderBrowseObject)] + [InlineData(ExtenderCATIDType.ConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpConfig)] + public void GetExtenderCATID_ReturnsCorrectCadId(ExtenderCATIDType input, string expected) + { + var provider = CreateInstance(); + + var result = provider.GetExtenderCATID(input, treeNode: null); + + Assert.Equal(expected, result); + } + + private static CSharpExtenderCATIDProvider CreateInstance() { - [Fact] - public void GetExtenderCATID_CatIdsUnknown_ReturnsNull() - { - var provider = CreateInstance(); - - var result = provider.GetExtenderCATID(ExtenderCATIDType.Unknown, treeNode: null); - - Assert.Null(result); - } - - [Theory] - [InlineData(ExtenderCATIDType.HierarchyExtensionObject, PrjCATID.prjCATIDProject)] - [InlineData(ExtenderCATIDType.AutomationProject, PrjCATID.prjCATIDProject)] - [InlineData(ExtenderCATIDType.AutomationProjectItem, PrjCATID.prjCATIDProjectItem)] - [InlineData(ExtenderCATIDType.HierarchyBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject)] - [InlineData(ExtenderCATIDType.HierarchyConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectConfigBrowseObject)] - [InlineData(ExtenderCATIDType.AutomationReference, PrjBrowseObjectCATID.prjCATIDCSharpReferenceBrowseObject)] - [InlineData(ExtenderCATIDType.ReferenceBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpReferenceBrowseObject)] - [InlineData(ExtenderCATIDType.ProjectBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject)] - [InlineData(ExtenderCATIDType.ProjectConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpProjectConfigBrowseObject)] - [InlineData(ExtenderCATIDType.FileBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpFileBrowseObject)] - [InlineData(ExtenderCATIDType.AutomationFolderProperties, PrjBrowseObjectCATID.prjCATIDCSharpFolderBrowseObject)] - [InlineData(ExtenderCATIDType.FolderBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpFolderBrowseObject)] - [InlineData(ExtenderCATIDType.ConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDCSharpConfig)] - public void GetExtenderCATID_ReturnsCorrectCadId(ExtenderCATIDType input, string expected) - { - var provider = CreateInstance(); - - var result = provider.GetExtenderCATID(input, treeNode: null); - - Assert.Equal(expected, result); - } - - private static CSharpExtenderCATIDProvider CreateInstance() - { - return new CSharpExtenderCATIDProvider(); - } + return new CSharpExtenderCATIDProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProviderTests.cs index 8b8b5c6812..9f4d224a72 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VisualBasicExtenderCATIDProviderTests.cs @@ -3,46 +3,45 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Properties; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic; + +public class VisualBasicExtenderCATIDProviderTests { - public class VisualBasicExtenderCATIDProviderTests + [Fact] + public void GetExtenderCATID_CatIdsUnknown_ReturnsNull() + { + var provider = CreateInstance(); + + var result = provider.GetExtenderCATID(ExtenderCATIDType.Unknown, treeNode: null); + + Assert.Null(result); + } + + [Theory] + [InlineData(ExtenderCATIDType.HierarchyExtensionObject, PrjCATID.prjCATIDProject)] + [InlineData(ExtenderCATIDType.AutomationProject, PrjCATID.prjCATIDProject)] + [InlineData(ExtenderCATIDType.AutomationProjectItem, PrjCATID.prjCATIDProjectItem)] + [InlineData(ExtenderCATIDType.HierarchyBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectBrowseObject)] + [InlineData(ExtenderCATIDType.HierarchyConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectConfigBrowseObject)] + [InlineData(ExtenderCATIDType.AutomationReference, PrjBrowseObjectCATID.prjCATIDVBReferenceBrowseObject)] + [InlineData(ExtenderCATIDType.ReferenceBrowseObject, PrjBrowseObjectCATID.prjCATIDVBReferenceBrowseObject)] + [InlineData(ExtenderCATIDType.ProjectBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectBrowseObject)] + [InlineData(ExtenderCATIDType.ProjectConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectConfigBrowseObject)] + [InlineData(ExtenderCATIDType.FileBrowseObject, PrjBrowseObjectCATID.prjCATIDVBFileBrowseObject)] + [InlineData(ExtenderCATIDType.AutomationFolderProperties, PrjBrowseObjectCATID.prjCATIDVBFolderBrowseObject)] + [InlineData(ExtenderCATIDType.FolderBrowseObject, PrjBrowseObjectCATID.prjCATIDVBFolderBrowseObject)] + [InlineData(ExtenderCATIDType.ConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDVBConfig)] + public void GetExtenderCATID_ReturnsCorrectCadId(ExtenderCATIDType input, string expected) + { + var provider = CreateInstance(); + + var result = provider.GetExtenderCATID(input, treeNode: null); + + Assert.Equal(expected, result); + } + + private static VisualBasicExtenderCATIDProvider CreateInstance() { - [Fact] - public void GetExtenderCATID_CatIdsUnknown_ReturnsNull() - { - var provider = CreateInstance(); - - var result = provider.GetExtenderCATID(ExtenderCATIDType.Unknown, treeNode: null); - - Assert.Null(result); - } - - [Theory] - [InlineData(ExtenderCATIDType.HierarchyExtensionObject, PrjCATID.prjCATIDProject)] - [InlineData(ExtenderCATIDType.AutomationProject, PrjCATID.prjCATIDProject)] - [InlineData(ExtenderCATIDType.AutomationProjectItem, PrjCATID.prjCATIDProjectItem)] - [InlineData(ExtenderCATIDType.HierarchyBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectBrowseObject)] - [InlineData(ExtenderCATIDType.HierarchyConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectConfigBrowseObject)] - [InlineData(ExtenderCATIDType.AutomationReference, PrjBrowseObjectCATID.prjCATIDVBReferenceBrowseObject)] - [InlineData(ExtenderCATIDType.ReferenceBrowseObject, PrjBrowseObjectCATID.prjCATIDVBReferenceBrowseObject)] - [InlineData(ExtenderCATIDType.ProjectBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectBrowseObject)] - [InlineData(ExtenderCATIDType.ProjectConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDVBProjectConfigBrowseObject)] - [InlineData(ExtenderCATIDType.FileBrowseObject, PrjBrowseObjectCATID.prjCATIDVBFileBrowseObject)] - [InlineData(ExtenderCATIDType.AutomationFolderProperties, PrjBrowseObjectCATID.prjCATIDVBFolderBrowseObject)] - [InlineData(ExtenderCATIDType.FolderBrowseObject, PrjBrowseObjectCATID.prjCATIDVBFolderBrowseObject)] - [InlineData(ExtenderCATIDType.ConfigurationBrowseObject, PrjBrowseObjectCATID.prjCATIDVBConfig)] - public void GetExtenderCATID_ReturnsCorrectCadId(ExtenderCATIDType input, string expected) - { - var provider = CreateInstance(); - - var result = provider.GetExtenderCATID(input, treeNode: null); - - Assert.Equal(expected, result); - } - - private static VisualBasicExtenderCATIDProvider CreateInstance() - { - return new VisualBasicExtenderCATIDProvider(); - } + return new VisualBasicExtenderCATIDProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VsProjectEventsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VsProjectEventsTests.cs index 01bfc90965..3b21cbb7ad 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VsProjectEventsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasic/VsProjectEventsTests.cs @@ -2,61 +2,60 @@ using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation.VisualBasic; + +public class VsProjectEventsTests { - public class VsProjectEventsTests + [Fact] + public void VSProjectEvents_Properties() { - [Fact] - public void VSProjectEvents_Properties() + var referenceEvents = Mock.Of(); + var buildManagerEvents = Mock.Of(); + + var projectEventsMock = new Mock(); + projectEventsMock.Setup(e => e.ReferencesEvents) + .Returns(referenceEvents); + projectEventsMock.Setup(e => e.BuildManagerEvents) + .Returns(buildManagerEvents); + + var innerVSProjectMock = new Mock(); + innerVSProjectMock.Setup(p => p.Events) + .Returns(projectEventsMock.Object); + + var unconfiguredProjectMock = new Mock(); + unconfiguredProjectMock.SetupGet(p => p.Capabilities) + .Returns((IProjectCapabilitiesScope?)null); + + var importEvents = Mock.Of(); + var importsEventsImpl = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) { - var referenceEvents = Mock.Of(); - var buildManagerEvents = Mock.Of(); - - var projectEventsMock = new Mock(); - projectEventsMock.Setup(e => e.ReferencesEvents) - .Returns(referenceEvents); - projectEventsMock.Setup(e => e.BuildManagerEvents) - .Returns(buildManagerEvents); - - var innerVSProjectMock = new Mock(); - innerVSProjectMock.Setup(p => p.Events) - .Returns(projectEventsMock.Object); - - var unconfiguredProjectMock = new Mock(); - unconfiguredProjectMock.SetupGet(p => p.Capabilities) - .Returns((IProjectCapabilitiesScope?)null); - - var importEvents = Mock.Of(); - var importsEventsImpl = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) - { - new Lazy(() => importEvents, IOrderPrecedenceMetadataViewFactory.Create("VisualBasic")) - }; - var vsProjectEvents = GetVSProjectEvents(innerVSProjectMock.Object, unconfiguredProjectMock.Object); - - vsProjectEvents.SetImportsEventsImpl(importsEventsImpl); - - Assert.NotNull(vsProjectEvents); - Assert.Equal(referenceEvents, vsProjectEvents.ReferencesEvents); - Assert.Equal(buildManagerEvents, vsProjectEvents.BuildManagerEvents); - Assert.Equal(importEvents, vsProjectEvents.ImportsEvents); - } + new Lazy(() => importEvents, IOrderPrecedenceMetadataViewFactory.Create("VisualBasic")) + }; + var vsProjectEvents = GetVSProjectEvents(innerVSProjectMock.Object, unconfiguredProjectMock.Object); + + vsProjectEvents.SetImportsEventsImpl(importsEventsImpl); - private static VSProjectEventsTestImpl GetVSProjectEvents(VSLangProj.VSProject vsproject, UnconfiguredProject project) + Assert.NotNull(vsProjectEvents); + Assert.Equal(referenceEvents, vsProjectEvents.ReferencesEvents); + Assert.Equal(buildManagerEvents, vsProjectEvents.BuildManagerEvents); + Assert.Equal(importEvents, vsProjectEvents.ImportsEvents); + } + + private static VSProjectEventsTestImpl GetVSProjectEvents(VSLangProj.VSProject vsproject, UnconfiguredProject project) + { + return new VSProjectEventsTestImpl(vsproject, project); + } + + internal class VSProjectEventsTestImpl : VisualBasicVSProjectEvents + { + public VSProjectEventsTestImpl(VSLangProj.VSProject vsProject, UnconfiguredProject project) + : base(vsProject, project) { - return new VSProjectEventsTestImpl(vsproject, project); } - internal class VSProjectEventsTestImpl : VisualBasicVSProjectEvents + internal void SetImportsEventsImpl(OrderPrecedenceImportCollection importsEventsImpl) { - public VSProjectEventsTestImpl(VSLangProj.VSProject vsProject, UnconfiguredProject project) - : base(vsProject, project) - { - } - - internal void SetImportsEventsImpl(OrderPrecedenceImportCollection importsEventsImpl) - { - ImportsEventsImpl = importsEventsImpl; - } + ImportsEventsImpl = importsEventsImpl; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasicNamespaceImportsListTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasicNamespaceImportsListTests.cs index 1bf5a26b42..a05427b6db 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasicNamespaceImportsListTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VisualBasicNamespaceImportsListTests.cs @@ -1,121 +1,120 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +public class VisualBasicNamespaceImportsListTests { - public class VisualBasicNamespaceImportsListTests + [Fact] + public void UnderlyingListBasedPropertiesTest() { - [Fact] - public void UnderlyingListBasedPropertiesTest() - { - var list = VisualBasicNamespaceImportsListFactory.CreateInstance("A", "B"); + var list = VisualBasicNamespaceImportsListFactory.CreateInstance("A", "B"); - //Count - Assert.Equal(2, list.Count); + //Count + Assert.Equal(2, list.Count); - //GetEnumerator - var enumerator = list.GetEnumerator(); - Assert.True(enumerator.MoveNext()); - Assert.Equal("A", enumerator.Current); - Assert.True(enumerator.MoveNext()); - Assert.Equal("B", enumerator.Current); - Assert.False(enumerator.MoveNext()); + //GetEnumerator + var enumerator = list.GetEnumerator(); + Assert.True(enumerator.MoveNext()); + Assert.Equal("A", enumerator.Current); + Assert.True(enumerator.MoveNext()); + Assert.Equal("B", enumerator.Current); + Assert.False(enumerator.MoveNext()); - //IsPresent(string) - Assert.Throws("bstrImport", () => - { - list.IsPresent(null!); - }); - Assert.Throws("bstrImport", () => - { - list.IsPresent(""); - }); - Assert.True(list.IsPresent("A")); - Assert.False(list.IsPresent("C")); - - //IsPresent(int) - Assert.Throws("indexInt", () => - { - list.IsPresent(0); - }); - Assert.Throws("indexInt", () => - { - list.IsPresent(3); - }); - Assert.True(list.IsPresent(1)); - Assert.True(list.IsPresent(2)); + //IsPresent(string) + Assert.Throws("bstrImport", () => + { + list.IsPresent(null!); + }); + Assert.Throws("bstrImport", () => + { + list.IsPresent(""); + }); + Assert.True(list.IsPresent("A")); + Assert.False(list.IsPresent("C")); - //Item(int) - Assert.Throws("lIndex", () => - { - list.Item(0); - }); - Assert.Throws("lIndex", () => - { - list.Item(3); - }); - Assert.Equal("A", list.Item(1)); - Assert.Equal("B", list.Item(2)); - } + //IsPresent(int) + Assert.Throws("indexInt", () => + { + list.IsPresent(0); + }); + Assert.Throws("indexInt", () => + { + list.IsPresent(3); + }); + Assert.True(list.IsPresent(1)); + Assert.True(list.IsPresent(2)); - [Theory] - [InlineData(new string[0], new [] { "A", "B", "C", "D" } , new [] { "A", "B", "C", "D" }, new string[0] )] // Initial add - [InlineData(new [] { "A", "B", "C", "D" }, new [] { "A", "B", "C" } , new string[0], new [] { "D" } )] // Remove from the end - [InlineData(new [] { "A", "B", "C" }, new [] { "B", "C" } , new string[0], new [] { "A" } )] // Remove from the beginning - [InlineData(new [] { "B", "C" }, new [] { "A", "B", "C" } , new [] { "A" }, new string[0] )] // Add at the beginning - [InlineData(new [] { "A", "B", "C" }, new [] { "A", "B", "C", "E" } , new [] { "E" }, new string[0] )] // Add at the end - [InlineData(new [] { "A", "B", "C", "E"}, new [] { "A", "B", "C", "D", "E" } , new [] { "D" }, new string[0] )] // Add in the middle - [InlineData(new [] { "A", "B", "C", "D", "E" }, new [] { "A", "B", "D", "E" } , new string[0], new [] { "C" } )] // Remove from the middle - [InlineData(new [] { "A", "B", "D", "E" }, new [] { "B", "C", "E", "F" } , new [] { "C", "F" }, new [] { "A", "D" } )] // Addition and deletion in jumbled order with the same no of elements as before - - public void UpdateNamespaceImportListTest(string[] initialState, string[] updateToApply, string[] expectedAdded, string[] expectedRemoved) + //Item(int) + Assert.Throws("lIndex", () => + { + list.Item(0); + }); + Assert.Throws("lIndex", () => { - var list = VisualBasicNamespaceImportsListFactory.CreateInstance(initialState); + list.Item(3); + }); + Assert.Equal("A", list.Item(1)); + Assert.Equal("B", list.Item(2)); + } - var json = ConstructNamespaceImportChangeJson(updateToApply); - var projectSubscriptionUpdate = IProjectSubscriptionUpdateFactory.FromJson(json); + [Theory] + [InlineData(new string[0], new [] { "A", "B", "C", "D" } , new [] { "A", "B", "C", "D" }, new string[0] )] // Initial add + [InlineData(new [] { "A", "B", "C", "D" }, new [] { "A", "B", "C" } , new string[0], new [] { "D" } )] // Remove from the end + [InlineData(new [] { "A", "B", "C" }, new [] { "B", "C" } , new string[0], new [] { "A" } )] // Remove from the beginning + [InlineData(new [] { "B", "C" }, new [] { "A", "B", "C" } , new [] { "A" }, new string[0] )] // Add at the beginning + [InlineData(new [] { "A", "B", "C" }, new [] { "A", "B", "C", "E" } , new [] { "E" }, new string[0] )] // Add at the end + [InlineData(new [] { "A", "B", "C", "E"}, new [] { "A", "B", "C", "D", "E" } , new [] { "D" }, new string[0] )] // Add in the middle + [InlineData(new [] { "A", "B", "C", "D", "E" }, new [] { "A", "B", "D", "E" } , new string[0], new [] { "C" } )] // Remove from the middle + [InlineData(new [] { "A", "B", "D", "E" }, new [] { "B", "C", "E", "F" } , new [] { "C", "F" }, new [] { "A", "D" } )] // Addition and deletion in jumbled order with the same no of elements as before - list.TestApply(projectSubscriptionUpdate); + public void UpdateNamespaceImportListTest(string[] initialState, string[] updateToApply, string[] expectedAdded, string[] expectedRemoved) + { + var list = VisualBasicNamespaceImportsListFactory.CreateInstance(initialState); - // Updates represent the final state, so they are the expected list too - Assert.Equal(updateToApply.OrderBy(s=>s), list.OrderBy(s=>s)); - Assert.Equal(expectedAdded.OrderBy(s=>s), list.ImportsAdded.OrderBy(s=>s)); - Assert.Equal(expectedRemoved.OrderBy(s=>s), list.ImportsRemoved.OrderBy(s=>s)); + var json = ConstructNamespaceImportChangeJson(updateToApply); + var projectSubscriptionUpdate = IProjectSubscriptionUpdateFactory.FromJson(json); - return; + list.TestApply(projectSubscriptionUpdate); - static string ConstructNamespaceImportChangeJson(string[] importNames) + // Updates represent the final state, so they are the expected list too + Assert.Equal(updateToApply.OrderBy(s=>s), list.OrderBy(s=>s)); + Assert.Equal(expectedAdded.OrderBy(s=>s), list.ImportsAdded.OrderBy(s=>s)); + Assert.Equal(expectedRemoved.OrderBy(s=>s), list.ImportsRemoved.OrderBy(s=>s)); + + return; + + static string ConstructNamespaceImportChangeJson(string[] importNames) + { + var json = + """ + { + "ProjectChanges": { + "NamespaceImport": { + "Difference": { + "AnyChanges": "True" + }, + "After": { + "Items": { + """; + + for (int i = 0; i < importNames.Length; i++) { - var json = - """ - { - "ProjectChanges": { - "NamespaceImport": { - "Difference": { - "AnyChanges": "True" - }, - "After": { - "Items": { - """; - - for (int i = 0; i < importNames.Length; i++) + json += @" """ + importNames[i] + @""" : {}"; + if (i != importNames.Length - 1) { - json += @" """ + importNames[i] + @""" : {}"; - if (i != importNames.Length - 1) - { - json += ","; - } + json += ","; } + } - json += - """ - } - } + json += + """ + } } } } - """; - return json; - } + } + """; + return json; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsImportsTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsImportsTests.cs index f29435fd79..cd4a07fa8a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsImportsTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsImportsTests.cs @@ -3,93 +3,92 @@ using EnvDTE; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +public class VsImportsTests { - public class VsImportsTests + [Fact] + public void Constructor_NotNull() + { + var vsimports = CreateInstance( + Mock.Of(), + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + VisualBasicNamespaceImportsListFactory.CreateInstance()); + + Assert.NotNull(vsimports); + } + + [Fact] + public void VsImports_PropertiesCheck() + { + var dte = Mock.Of(); + var project = Mock.Of(); + + var vsProjectMock = new Mock(); + vsProjectMock.Setup(p => p.DTE) + .Returns(dte); + vsProjectMock.Setup(p => p.Project) + .Returns(project); + + var vsimports = CreateInstance( + vsProjectMock.Object, + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + VisualBasicNamespaceImportsListFactory.CreateInstance()); + + Assert.Equal(dte, vsimports.DTE); + Assert.Equal(project, vsimports.ContainingProject); + } + + [Fact] + public void VsImports_ImportsAddRemoveCheck() + { + var dispImportsEventsMock = new Mock<_dispImportsEvents>(); + const string importName = "Something"; + dispImportsEventsMock.Setup(d => d.ImportAdded(It.Is(s => s == importName))) + .Verifiable(); + dispImportsEventsMock.Setup(d => d.ImportRemoved(It.Is(s => s == importName))) + .Verifiable(); + + var vsimports = CreateInstance( + Mock.Of(), + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + VisualBasicNamespaceImportsListFactory.CreateInstance("A", "B")); + + vsimports.OnSinkAdded(dispImportsEventsMock.Object); + + vsimports.OnImportAdded(importName); + vsimports.OnImportRemoved(importName); + + dispImportsEventsMock.VerifyAll(); + + vsimports.OnSinkRemoved(dispImportsEventsMock.Object); + + vsimports.OnImportAdded(importName); + vsimports.OnImportRemoved(importName); + + dispImportsEventsMock.Verify(d => d.ImportAdded(It.IsAny()), Times.Once); + dispImportsEventsMock.Verify(d => d.ImportRemoved(It.IsAny()), Times.Once); + + Assert.Equal(2, vsimports.Count); + } + + private static DotNetVSImports CreateInstance( + VSLangProj.VSProject vsProject, + IProjectThreadingService threadingService, + IActiveConfiguredValue activeConfiguredProject, + IProjectAccessor projectAccessor, + IUnconfiguredProjectVsServices vsServices, + DotNetNamespaceImportsList importList) { - [Fact] - public void Constructor_NotNull() - { - var vsimports = CreateInstance( - Mock.Of(), - Mock.Of(), - Mock.Of>(), - Mock.Of(), - Mock.Of(), - VisualBasicNamespaceImportsListFactory.CreateInstance()); - - Assert.NotNull(vsimports); - } - - [Fact] - public void VsImports_PropertiesCheck() - { - var dte = Mock.Of(); - var project = Mock.Of(); - - var vsProjectMock = new Mock(); - vsProjectMock.Setup(p => p.DTE) - .Returns(dte); - vsProjectMock.Setup(p => p.Project) - .Returns(project); - - var vsimports = CreateInstance( - vsProjectMock.Object, - Mock.Of(), - Mock.Of>(), - Mock.Of(), - Mock.Of(), - VisualBasicNamespaceImportsListFactory.CreateInstance()); - - Assert.Equal(dte, vsimports.DTE); - Assert.Equal(project, vsimports.ContainingProject); - } - - [Fact] - public void VsImports_ImportsAddRemoveCheck() - { - var dispImportsEventsMock = new Mock<_dispImportsEvents>(); - const string importName = "Something"; - dispImportsEventsMock.Setup(d => d.ImportAdded(It.Is(s => s == importName))) - .Verifiable(); - dispImportsEventsMock.Setup(d => d.ImportRemoved(It.Is(s => s == importName))) - .Verifiable(); - - var vsimports = CreateInstance( - Mock.Of(), - Mock.Of(), - Mock.Of>(), - Mock.Of(), - Mock.Of(), - VisualBasicNamespaceImportsListFactory.CreateInstance("A", "B")); - - vsimports.OnSinkAdded(dispImportsEventsMock.Object); - - vsimports.OnImportAdded(importName); - vsimports.OnImportRemoved(importName); - - dispImportsEventsMock.VerifyAll(); - - vsimports.OnSinkRemoved(dispImportsEventsMock.Object); - - vsimports.OnImportAdded(importName); - vsimports.OnImportRemoved(importName); - - dispImportsEventsMock.Verify(d => d.ImportAdded(It.IsAny()), Times.Once); - dispImportsEventsMock.Verify(d => d.ImportRemoved(It.IsAny()), Times.Once); - - Assert.Equal(2, vsimports.Count); - } - - private static DotNetVSImports CreateInstance( - VSLangProj.VSProject vsProject, - IProjectThreadingService threadingService, - IActiveConfiguredValue activeConfiguredProject, - IProjectAccessor projectAccessor, - IUnconfiguredProjectVsServices vsServices, - DotNetNamespaceImportsList importList) - { - return new DotNetVSImports(vsProject, threadingService, activeConfiguredProject, projectAccessor, vsServices, importList); - } + return new DotNetVSImports(vsProject, threadingService, activeConfiguredProject, projectAccessor, vsServices, importList); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsLangProjectPropertiesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsLangProjectPropertiesTests.cs index 855ac17463..478e19508f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsLangProjectPropertiesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Automation/VsLangProjectPropertiesTests.cs @@ -3,203 +3,202 @@ using VSLangProj; using VSLangProj110; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation +namespace Microsoft.VisualStudio.ProjectSystem.VS.Automation; + +public class VSProject_VSLangProjectPropertiesTests { - public class VSProject_VSLangProjectPropertiesTests + [Fact] + public void NotNull() { - [Fact] - public void NotNull() - { - var unconfiguredProjectMock = new Mock(); - unconfiguredProjectMock.SetupGet(p => p.Capabilities) - .Returns((IProjectCapabilitiesScope?)null); - - var vsproject = CreateInstance( - Mock.Of(), - threadingService: Mock.Of(), - projectProperties: Mock.Of>()); - Assert.NotNull(vsproject); - } + var unconfiguredProjectMock = new Mock(); + unconfiguredProjectMock.SetupGet(p => p.Capabilities) + .Returns((IProjectCapabilitiesScope?)null); + + var vsproject = CreateInstance( + Mock.Of(), + threadingService: Mock.Of(), + projectProperties: Mock.Of>()); + Assert.NotNull(vsproject); + } - [Fact] - public void ImportsAndEventsAsNull() - { - var imports = Mock.Of(); - var events = Mock.Of(); - var innerVSProjectMock = new Mock(); - - innerVSProjectMock.Setup(p => p.Imports) - .Returns(imports); - - innerVSProjectMock.Setup(p => p.Events) - .Returns(events); - - var vsproject = CreateInstance( - innerVSProjectMock.Object, - threadingService: Mock.Of(), - projectProperties: Mock.Of>()); - Assert.NotNull(vsproject); - Assert.True(imports.Equals(vsproject.Imports)); - Assert.Equal(events, vsproject.Events); - } + [Fact] + public void ImportsAndEventsAsNull() + { + var imports = Mock.Of(); + var events = Mock.Of(); + var innerVSProjectMock = new Mock(); + + innerVSProjectMock.Setup(p => p.Imports) + .Returns(imports); + + innerVSProjectMock.Setup(p => p.Events) + .Returns(events); + + var vsproject = CreateInstance( + innerVSProjectMock.Object, + threadingService: Mock.Of(), + projectProperties: Mock.Of>()); + Assert.NotNull(vsproject); + Assert.True(imports.Equals(vsproject.Imports)); + Assert.Equal(events, vsproject.Events); + } - [Fact] - public void ImportsAndEventsAsNonNull() + [Fact] + public void ImportsAndEventsAsNonNull() + { + var imports = Mock.Of(); + var importsImpl = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) { - var imports = Mock.Of(); - var importsImpl = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) - { - new Lazy(() => imports, IOrderPrecedenceMetadataViewFactory.Create("VisualBasic")) - }; - var events = Mock.Of(); - var vsProjectEventsImpl = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) - { - new Lazy(() => events, IOrderPrecedenceMetadataViewFactory.Create("VisualBasic")) - }; - - var innerVSProjectMock = new Mock(); - - var unconfiguredProjectMock = new Mock(); - unconfiguredProjectMock.SetupGet(p => p.Capabilities) - .Returns((IProjectCapabilitiesScope?)null); - - var vsproject = new VSProjectTestImpl( - innerVSProjectMock.Object, - threadingService: Mock.Of(), - projectProperties: Mock.Of>(), - project: unconfiguredProjectMock.Object, - buildManager: Mock.Of()); - - vsproject.SetImportsImpl(importsImpl); - vsproject.SetVSProjectEventsImpl(vsProjectEventsImpl); - - Assert.NotNull(vsproject); - Assert.True(imports.Equals(vsproject.Imports)); - Assert.Equal(events, vsproject.Events); - } - - [Fact] - public void OutputTypeEx() + new Lazy(() => imports, IOrderPrecedenceMetadataViewFactory.Create("VisualBasic")) + }; + var events = Mock.Of(); + var vsProjectEventsImpl = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst, (UnconfiguredProject?)null) { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfigurationGeneralBrowseObject.SchemaName, ConfigurationGeneralBrowseObject.OutputTypeProperty, 4, setValues); + new Lazy(() => events, IOrderPrecedenceMetadataViewFactory.Create("VisualBasic")) + }; - var projectProperties = ProjectPropertiesFactory.Create(project, data); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var innerVSProjectMock = new Mock(); - var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); - Assert.Equal(prjOutputTypeEx.prjOutputTypeEx_AppContainerExe, vsLangProjectProperties.OutputTypeEx); + var unconfiguredProjectMock = new Mock(); + unconfiguredProjectMock.SetupGet(p => p.Capabilities) + .Returns((IProjectCapabilitiesScope?)null); - vsLangProjectProperties.OutputTypeEx = prjOutputTypeEx.prjOutputTypeEx_WinExe; - Assert.Equal(nameof(prjOutputTypeEx.prjOutputTypeEx_WinExe), setValues.Single().ToString()); - } + var vsproject = new VSProjectTestImpl( + innerVSProjectMock.Object, + threadingService: Mock.Of(), + projectProperties: Mock.Of>(), + project: unconfiguredProjectMock.Object, + buildManager: Mock.Of()); - [Fact] - public void OutputType() - { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfigurationGeneralBrowseObject.SchemaName, ConfigurationGeneralBrowseObject.OutputTypeProperty, 1, setValues); + vsproject.SetImportsImpl(importsImpl); + vsproject.SetVSProjectEventsImpl(vsProjectEventsImpl); - var projectProperties = ProjectPropertiesFactory.Create(project, data); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + Assert.NotNull(vsproject); + Assert.True(imports.Equals(vsproject.Imports)); + Assert.Equal(events, vsproject.Events); + } - var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); - Assert.Equal(prjOutputType.prjOutputTypeExe, vsLangProjectProperties.OutputType); + [Fact] + public void OutputTypeEx() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfigurationGeneralBrowseObject.SchemaName, ConfigurationGeneralBrowseObject.OutputTypeProperty, 4, setValues); - vsLangProjectProperties.OutputType = prjOutputType.prjOutputTypeLibrary; - Assert.Equal(prjOutputType.prjOutputTypeLibrary, setValues.Single()); - } + var projectProperties = ProjectPropertiesFactory.Create(project, data); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - [Fact] - public void AssemblyName() - { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.AssemblyNameProperty, "Blah", setValues); + var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); + Assert.Equal(prjOutputTypeEx.prjOutputTypeEx_AppContainerExe, vsLangProjectProperties.OutputTypeEx); - var projectProperties = ProjectPropertiesFactory.Create(project, data); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + vsLangProjectProperties.OutputTypeEx = prjOutputTypeEx.prjOutputTypeEx_WinExe; + Assert.Equal(nameof(prjOutputTypeEx.prjOutputTypeEx_WinExe), setValues.Single().ToString()); + } - var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); - Assert.Equal("Blah", vsLangProjectProperties.AssemblyName); + [Fact] + public void OutputType() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfigurationGeneralBrowseObject.SchemaName, ConfigurationGeneralBrowseObject.OutputTypeProperty, 1, setValues); - var testValue = "Testing"; - vsLangProjectProperties.AssemblyName = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var projectProperties = ProjectPropertiesFactory.Create(project, data); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - [Fact] - public void FullPath() - { - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.ProjectDirProperty, "somepath"); + var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); + Assert.Equal(prjOutputType.prjOutputTypeExe, vsLangProjectProperties.OutputType); - var projectProperties = ProjectPropertiesFactory.Create(project, data); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + vsLangProjectProperties.OutputType = prjOutputType.prjOutputTypeLibrary; + Assert.Equal(prjOutputType.prjOutputTypeLibrary, setValues.Single()); + } - var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); - Assert.Equal("somepath", vsLangProjectProperties.FullPath); - } + [Fact] + public void AssemblyName() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.AssemblyNameProperty, "Blah", setValues); - [Fact] - public void AbsoluteProjectDirectory() - { - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfigurationGeneralBrowseObject.SchemaName, ConfigurationGeneralBrowseObject.FullPathProperty, "testvalue"); + var projectProperties = ProjectPropertiesFactory.Create(project, data); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - var projectProperties = ProjectPropertiesFactory.Create(project, data); - var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); + Assert.Equal("Blah", vsLangProjectProperties.AssemblyName); - var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); - Assert.Equal("testvalue", vsLangProjectProperties.AbsoluteProjectDirectory); - } + var testValue = "Testing"; + vsLangProjectProperties.AssemblyName = testValue; + Assert.Equal(setValues.Single(), testValue); + } - [Fact] - public void ExtenderCATID() - { - var vsproject = CreateInstance( - Mock.Of(), - threadingService: Mock.Of(), - projectProperties: Mock.Of>(), - buildManager: Mock.Of()); - Assert.Null(vsproject.ExtenderCATID); - } + [Fact] + public void FullPath() + { + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.ProjectDirProperty, "somepath"); + + var projectProperties = ProjectPropertiesFactory.Create(project, data); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); + + var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); + Assert.Equal("somepath", vsLangProjectProperties.FullPath); + } + + [Fact] + public void AbsoluteProjectDirectory() + { + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfigurationGeneralBrowseObject.SchemaName, ConfigurationGeneralBrowseObject.FullPathProperty, "testvalue"); + + var projectProperties = ProjectPropertiesFactory.Create(project, data); + var activeConfiguredProject = IActiveConfiguredValueFactory.ImplementValue(() => projectProperties); - private static VSProject CreateInstance( - VSLangProj.VSProject vsproject, + var vsLangProjectProperties = CreateInstance(Mock.Of(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject); + Assert.Equal("testvalue", vsLangProjectProperties.AbsoluteProjectDirectory); + } + + [Fact] + public void ExtenderCATID() + { + var vsproject = CreateInstance( + Mock.Of(), + threadingService: Mock.Of(), + projectProperties: Mock.Of>(), + buildManager: Mock.Of()); + Assert.Null(vsproject.ExtenderCATID); + } + + private static VSProject CreateInstance( + VSLangProj.VSProject vsproject, + IProjectThreadingService threadingService, + IActiveConfiguredValue projectProperties, + UnconfiguredProject? project = null, + BuildManager? buildManager = null) + { + project ??= UnconfiguredProjectFactory.Create(); + + return new VSProject(vsproject, threadingService, projectProperties, project, buildManager!); + } + + internal class VSProjectTestImpl : VSProject + { + public VSProjectTestImpl( + VSLangProj.VSProject vsProject, IProjectThreadingService threadingService, IActiveConfiguredValue projectProperties, - UnconfiguredProject? project = null, - BuildManager? buildManager = null) + UnconfiguredProject project, + BuildManager buildManager) + : base(vsProject, threadingService, projectProperties, project, buildManager) { - project ??= UnconfiguredProjectFactory.Create(); + } - return new VSProject(vsproject, threadingService, projectProperties, project, buildManager!); + internal void SetImportsImpl(OrderPrecedenceImportCollection importsImpl) + { + ImportsImpl = importsImpl; } - internal class VSProjectTestImpl : VSProject + internal void SetVSProjectEventsImpl(OrderPrecedenceImportCollection vsProjectEventsImpl) { - public VSProjectTestImpl( - VSLangProj.VSProject vsProject, - IProjectThreadingService threadingService, - IActiveConfiguredValue projectProperties, - UnconfiguredProject project, - BuildManager buildManager) - : base(vsProject, threadingService, projectProperties, project, buildManager) - { - } - - internal void SetImportsImpl(OrderPrecedenceImportCollection importsImpl) - { - ImportsImpl = importsImpl; - } - - internal void SetVSProjectEventsImpl(OrderPrecedenceImportCollection vsProjectEventsImpl) - { - VSProjectEventsImpl = vsProjectEventsImpl; - } + VSProjectEventsImpl = vsProjectEventsImpl; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManagerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManagerTests.cs index daf46809b2..ba2673fc3f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManagerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/ImplicitlyTriggeredDebugBuildManagerTests.cs @@ -3,105 +3,104 @@ using Microsoft.VisualStudio.ProjectSystem.Build; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +public class ImplicitlyTriggeredDebugBuildManagerTests { - public class ImplicitlyTriggeredDebugBuildManagerTests + [Theory, CombinatorialData] + public async Task TestImplicitlySkipAnalyzersForDebugBuilds( + bool startDebuggingBuild, + bool startWithoutDebuggingBuild, + bool cancelBuild) { - [Theory, CombinatorialData] - public async Task TestImplicitlySkipAnalyzersForDebugBuilds( - bool startDebuggingBuild, - bool startWithoutDebuggingBuild, - bool cancelBuild) + var implicitBuildStartInvoked = false; + var implicitBuildEndOrCancelInvoked = false; + Action onImplicitBuildStart = () => implicitBuildStartInvoked = true; + Action> onImplicitBuildStartWithStartupPaths = startupPaths => implicitBuildStartInvoked = true; + Action onImplicitBuildEndOrCancel = () => implicitBuildEndOrCancelInvoked = true; + + var buildManager = await CreateInitializedInstanceAsync( + onImplicitBuildStart, + onImplicitBuildEndOrCancel, + onImplicitBuildStartWithStartupPaths, + startDebuggingBuild, + startWithoutDebuggingBuild); + + RunBuild(buildManager, cancelBuild); + + if ((startDebuggingBuild || startWithoutDebuggingBuild)) { - var implicitBuildStartInvoked = false; - var implicitBuildEndOrCancelInvoked = false; - Action onImplicitBuildStart = () => implicitBuildStartInvoked = true; - Action> onImplicitBuildStartWithStartupPaths = startupPaths => implicitBuildStartInvoked = true; - Action onImplicitBuildEndOrCancel = () => implicitBuildEndOrCancelInvoked = true; - - var buildManager = await CreateInitializedInstanceAsync( - onImplicitBuildStart, - onImplicitBuildEndOrCancel, - onImplicitBuildStartWithStartupPaths, - startDebuggingBuild, - startWithoutDebuggingBuild); - - RunBuild(buildManager, cancelBuild); - - if ((startDebuggingBuild || startWithoutDebuggingBuild)) - { - Assert.True(implicitBuildStartInvoked); - Assert.True(implicitBuildEndOrCancelInvoked); - } - else - { - Assert.False(implicitBuildStartInvoked); - Assert.False(implicitBuildEndOrCancelInvoked); - } + Assert.True(implicitBuildStartInvoked); + Assert.True(implicitBuildEndOrCancelInvoked); } - - [Fact] - public async Task StartupProjectsArePassedThrough() + else { - ImmutableArray startupProjectPaths = ImmutableArray.Empty; - Action> onImplicitBuildStartWithStartPaths = paths => startupProjectPaths = paths; + Assert.False(implicitBuildStartInvoked); + Assert.False(implicitBuildEndOrCancelInvoked); + } + } + + [Fact] + public async Task StartupProjectsArePassedThrough() + { + ImmutableArray startupProjectPaths = ImmutableArray.Empty; + Action> onImplicitBuildStartWithStartPaths = paths => startupProjectPaths = paths; - var buildManager = await CreateInitializedInstanceAsync( - onImplicitBuildStartWithStartupPaths: onImplicitBuildStartWithStartPaths, - startWithoutDebuggingBuild: true, - startupProjectFullPaths: ImmutableArray.Create(@"C:\alpha\beta.csproj", @"C:\alpha\gamma.csproj")); + var buildManager = await CreateInitializedInstanceAsync( + onImplicitBuildStartWithStartupPaths: onImplicitBuildStartWithStartPaths, + startWithoutDebuggingBuild: true, + startupProjectFullPaths: ImmutableArray.Create(@"C:\alpha\beta.csproj", @"C:\alpha\gamma.csproj")); - RunBuild(buildManager, cancelBuild: false); + RunBuild(buildManager, cancelBuild: false); + + Assert.Contains(@"C:\alpha\beta.csproj", startupProjectPaths); + Assert.Contains(@"C:\alpha\gamma.csproj", startupProjectPaths); + } - Assert.Contains(@"C:\alpha\beta.csproj", startupProjectPaths); - Assert.Contains(@"C:\alpha\gamma.csproj", startupProjectPaths); + private static async Task CreateInitializedInstanceAsync( + Action? onImplicitBuildStart = null, + Action? onImplicitBuildEndOrCancel = null, + Action>? onImplicitBuildStartWithStartupPaths = null, + bool startDebuggingBuild = false, + bool startWithoutDebuggingBuild = false, + ImmutableArray? startupProjectFullPaths = null) + { + var buildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_NONE; + if (startDebuggingBuild) + { + buildFlags |= VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG; } - private static async Task CreateInitializedInstanceAsync( - Action? onImplicitBuildStart = null, - Action? onImplicitBuildEndOrCancel = null, - Action>? onImplicitBuildStartWithStartupPaths = null, - bool startDebuggingBuild = false, - bool startWithoutDebuggingBuild = false, - ImmutableArray? startupProjectFullPaths = null) + if (startWithoutDebuggingBuild) { - var buildFlags = VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_NONE; - if (startDebuggingBuild) - { - buildFlags |= VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCHDEBUG; - } - - if (startWithoutDebuggingBuild) - { - buildFlags |= VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH; - } - - var solutionBuildManager = ISolutionBuildManagerFactory.ImplementBusy(buildFlags); - - var instance = new ImplicitlyTriggeredDebugBuildManager( - IProjectThreadingServiceFactory.Create(), - solutionBuildManager, - IImplicitlyTriggeredBuildManagerFactory.Create(onImplicitBuildStart, onImplicitBuildEndOrCancel, onImplicitBuildStartWithStartupPaths), - IStartupProjectHelperFactory.Create(startupProjectFullPaths ?? ImmutableArray.Empty)); - - await instance.LoadAsync(); - - return instance; + buildFlags |= VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_LAUNCH; } - private static void RunBuild(ImplicitlyTriggeredDebugBuildManager buildManager, bool cancelBuild) + var solutionBuildManager = ISolutionBuildManagerFactory.ImplementBusy(buildFlags); + + var instance = new ImplicitlyTriggeredDebugBuildManager( + IProjectThreadingServiceFactory.Create(), + solutionBuildManager, + IImplicitlyTriggeredBuildManagerFactory.Create(onImplicitBuildStart, onImplicitBuildEndOrCancel, onImplicitBuildStartWithStartupPaths), + IStartupProjectHelperFactory.Create(startupProjectFullPaths ?? ImmutableArray.Empty)); + + await instance.LoadAsync(); + + return instance; + } + + private static void RunBuild(ImplicitlyTriggeredDebugBuildManager buildManager, bool cancelBuild) + { + int discard = 0; + buildManager.UpdateSolution_Begin(ref discard); + + if (cancelBuild) + { + buildManager.UpdateSolution_Cancel(); + } + else { - int discard = 0; - buildManager.UpdateSolution_Begin(ref discard); - - if (cancelBuild) - { - buildManager.UpdateSolution_Cancel(); - } - else - { - buildManager.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny()); - } + buildManager.UpdateSolution_Done(It.IsAny(), It.IsAny(), It.IsAny()); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/LanguageServiceErrorListProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/LanguageServiceErrorListProviderTests.cs index 27ef400205..36f65bcf36 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/LanguageServiceErrorListProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Build/LanguageServiceErrorListProviderTests.cs @@ -4,361 +4,360 @@ using Microsoft.VisualStudio.ProjectSystem.LanguageServices; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Build +namespace Microsoft.VisualStudio.ProjectSystem.VS.Build; + +public class LanguageServiceErrorListProviderTests { - public class LanguageServiceErrorListProviderTests + [Fact] + public void SuspendRefresh_DoesNotThrow() { - [Fact] - public void SuspendRefresh_DoesNotThrow() - { - var provider = CreateInstance(); - provider.SuspendRefresh(); - } - - [Fact] - public void ResumeRefresh_DoesNotThrow() - { - var provider = CreateInstance(); - provider.ResumeRefresh(); - } + var provider = CreateInstance(); + provider.SuspendRefresh(); + } - [Fact] - public void ClearAllAsync_WhenNoErrorReporter_ReturnsCompletedTask() - { - var provider = CreateInstance(); + [Fact] + public void ResumeRefresh_DoesNotThrow() + { + var provider = CreateInstance(); + provider.ResumeRefresh(); + } - var result = provider.ClearAllAsync(); + [Fact] + public void ClearAllAsync_WhenNoErrorReporter_ReturnsCompletedTask() + { + var provider = CreateInstance(); - Assert.True(result.Status == TaskStatus.RanToCompletion); - } + var result = provider.ClearAllAsync(); - [Fact] - public void ClearMessageFromTargetAsync_WhenNoErrorReporter_ReturnsCompletedTask() - { - var provider = CreateInstance(); + Assert.True(result.Status == TaskStatus.RanToCompletion); + } - var result = provider.ClearMessageFromTargetAsync("targetName"); + [Fact] + public void ClearMessageFromTargetAsync_WhenNoErrorReporter_ReturnsCompletedTask() + { + var provider = CreateInstance(); - Assert.True(result.Status == TaskStatus.RanToCompletion); - } + var result = provider.ClearMessageFromTargetAsync("targetName"); - [Fact] - public async Task ClearAllAsync_WhenErrorReporter_CallsClearErrors() - { - int callCount = 0; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementClearErrors(() => { callCount++; return 0; }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var task = CreateDefaultTask(); - var provider = CreateInstance(host); + Assert.True(result.Status == TaskStatus.RanToCompletion); + } - await provider.AddMessageAsync(task); // Force initialization + [Fact] + public async Task ClearAllAsync_WhenErrorReporter_CallsClearErrors() + { + int callCount = 0; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementClearErrors(() => { callCount++; return 0; }); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var task = CreateDefaultTask(); + var provider = CreateInstance(host); - await provider.ClearAllAsync(); + await provider.AddMessageAsync(task); // Force initialization - Assert.Equal(1, callCount); - } + await provider.ClearAllAsync(); - [Fact] - public async Task AddMessageAsync_NullAsTask_ThrowsArgumentNull() - { - var provider = CreateInstance(); + Assert.Equal(1, callCount); + } - await Assert.ThrowsAsync("error", () => - { - return provider.AddMessageAsync(null!); - }); - } + [Fact] + public async Task AddMessageAsync_NullAsTask_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public async Task AddMessageAsync_WhenNoErrorReporter_ReturnsNotHandled() + await Assert.ThrowsAsync("error", () => { - var provider = CreateInstance(); + return provider.AddMessageAsync(null!); + }); + } - var task = new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender")); + [Fact] + public async Task AddMessageAsync_WhenNoErrorReporter_ReturnsNotHandled() + { + var provider = CreateInstance(); - var result = await provider.AddMessageAsync(task); + var task = new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender")); - Assert.Equal(AddMessageResult.NotHandled, result); - } + var result = await provider.AddMessageAsync(task); - [Fact] - public async Task AddMessageAsync_UnrecognizedArgsAsTaskBuildEventArgs_ReturnsNotHandled() - { - var provider = CreateInstance(); + Assert.Equal(AddMessageResult.NotHandled, result); + } - var task = new TargetGeneratedError("Test", new LazyFormattedBuildEventArgs("Message", "HelpKeyword", "SenderName")); + [Fact] + public async Task AddMessageAsync_UnrecognizedArgsAsTaskBuildEventArgs_ReturnsNotHandled() + { + var provider = CreateInstance(); - var result = await provider.AddMessageAsync(task); + var task = new TargetGeneratedError("Test", new LazyFormattedBuildEventArgs("Message", "HelpKeyword", "SenderName")); - Assert.Equal(AddMessageResult.NotHandled, result); - } + var result = await provider.AddMessageAsync(task); - [Fact] - public async Task AddMessageAsync_ArgsWithNoCodeAsTask_ReturnsNotHandled() - { - var provider = CreateInstance(); + Assert.Equal(AddMessageResult.NotHandled, result); + } - var task = new TargetGeneratedError("Test", new BuildErrorEventArgs(null, code: "", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender")); + [Fact] + public async Task AddMessageAsync_ArgsWithNoCodeAsTask_ReturnsNotHandled() + { + var provider = CreateInstance(); - var result = await provider.AddMessageAsync(task); + var task = new TargetGeneratedError("Test", new BuildErrorEventArgs(null, code: "", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender")); - Assert.Equal(AddMessageResult.NotHandled, result); - } + var result = await provider.AddMessageAsync(task); - [Fact] - public async Task AddMessageAsync_WhenErrorReporterThrowsNotImplemented_ReturnsNotHandled() + Assert.Equal(AddMessageResult.NotHandled, result); + } + + [Fact] + public async Task AddMessageAsync_WhenErrorReporterThrowsNotImplemented_ReturnsNotHandled() + { + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - throw new NotImplementedException(); - }); + throw new NotImplementedException(); + }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); - var task = CreateDefaultTask(); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host); + var task = CreateDefaultTask(); - var result = await provider.AddMessageAsync(task); + var result = await provider.AddMessageAsync(task); - Assert.Equal(AddMessageResult.NotHandled, result); - } + Assert.Equal(AddMessageResult.NotHandled, result); + } - [Fact] - public async Task AddMessageAsync_WhenErrorReporterThrows_Throws() + [Fact] + public async Task AddMessageAsync_WhenErrorReporterThrows_Throws() + { + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - throw new Exception(); - }); - - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); - var task = CreateDefaultTask(); - - await Assert.ThrowsAsync(() => - { - return provider.AddMessageAsync(task); - }); - } - - [Fact] - public async Task AddMessageAsync_ReturnsHandledAndStopProcessing() + throw new Exception(); + }); + + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host); + var task = CreateDefaultTask(); + + await Assert.ThrowsAsync(() => { - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); - var task = CreateDefaultTask(); + return provider.AddMessageAsync(task); + }); + } - var result = await provider.AddMessageAsync(task); + [Fact] + public async Task AddMessageAsync_ReturnsHandledAndStopProcessing() + { + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { }); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host); + var task = CreateDefaultTask(); + + var result = await provider.AddMessageAsync(task); - Assert.Equal(AddMessageResult.HandledAndStopProcessing, result); - } + Assert.Equal(AddMessageResult.HandledAndStopProcessing, result); + } - [Fact] - public async Task AddMessageAsync_WarningTaskAsTask_PassesTP_NORMALAsPriority() + [Fact] + public async Task AddMessageAsync_WarningTaskAsTask_PassesTP_NORMALAsPriority() + { + VSTASKPRIORITY? result = null; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - VSTASKPRIORITY? result = null; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - result = nPriority; - }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); + result = nPriority; + }); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host); - await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildWarningEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender"))); + await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildWarningEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender"))); - Assert.Equal(VSTASKPRIORITY.TP_NORMAL, result); - } + Assert.Equal(VSTASKPRIORITY.TP_NORMAL, result); + } - [Fact] - public async Task AddMessageAsync_ErrorTaskAsTask_PassesTP_HIGHAsPriority() + [Fact] + public async Task AddMessageAsync_ErrorTaskAsTask_PassesTP_HIGHAsPriority() + { + VSTASKPRIORITY? result = null; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - VSTASKPRIORITY? result = null; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - result = nPriority; - }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); + result = nPriority; + }); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host); - await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender"))); + await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender"))); - Assert.Equal(VSTASKPRIORITY.TP_HIGH, result); - } + Assert.Equal(VSTASKPRIORITY.TP_HIGH, result); + } - [Fact] - public async Task AddMessageAsync_CriticalBuildMessageTaskAsTask_PassesTP_LOWAsPriority() - { - VSTASKPRIORITY? result = null; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - result = nPriority; - }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); - - await provider.AddMessageAsync(new TargetGeneratedError("Test", new CriticalBuildMessageEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender"))); - - Assert.Equal(VSTASKPRIORITY.TP_LOW, result); - } - - // ErrorMessage Code - [Theory] - [InlineData(null, "A")] - [InlineData("", "0000")] - [InlineData(" ", "1000")] - [InlineData("This is an error message.", "CA1000")] - [InlineData("This is an error message\r\n", "CS1000")] - [InlineData("This is an error message.\r\n", "BC1000")] - [InlineData("This is an error message.\r\n.And another", "BC1000\r\n")] - public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingErrorMessageAndCode(string? errorMessage, string code) - { - string errorMessageResult = "NotSet"; - string errorIdResult = "NotSet"; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - errorMessageResult = bstrErrorMessage; - errorIdResult = bstrErrorId; - }); - - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - - var provider = CreateInstance(host); - await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, code, "File", 0, 0, 0, 0, errorMessage, "HelpKeyword", "Sender"))); - - Assert.Equal(errorMessage, errorMessageResult); - Assert.Equal(code, errorIdResult); - } - - // Line Column Expected Line Expected Column - [Theory] - [InlineData( 0, 0, 0, 0)] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/145 - [InlineData( 0, -1, 0, 0)] // Is this the right behavior? - [InlineData( -1, 0, 0, 0)] // Is this the right behavior? - [InlineData( 1, 0, 0, 0)] - [InlineData( 0, 1, 0, 0)] - [InlineData( 2, 2, 1, 1)] - [InlineData( 10, 100, 9, 99)] - [InlineData( 100, 10, 99, 9)] - [InlineData( 100, 100, 99, 99)] - public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingLineAndColumnAdjustingBy1(int lineNumber, int columnNumber, int expectedLineNumber, int expectedColumnNumber) - { - int? lineResult = null; - int? columnResult = null; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - lineResult = iLine; - columnResult = iColumn; - }); - - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - - var provider = CreateInstance(host); - await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", lineNumber, columnNumber, 0, 0, "ErrorMessage", "HelpKeyword", "Sender"))); - - Assert.Equal(expectedLineNumber, lineResult); - Assert.Equal(expectedColumnNumber, columnResult); - } - - // Line Column End Line End Column Expected End Line Expected End Column - [Theory] - [InlineData( 0, 0, 0, 0, 0, 0)] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/145 - [InlineData( 0, -1, 0, 0, 0, 0)] // Is this the right behavior? - [InlineData( -1, 0, 0, 0, 0, 0)] // Is this the right behavior? - [InlineData( 1, 0, 0, 0, 0, 0)] - [InlineData( 0, 1, 0, 0, 0, 0)] - [InlineData( 10, 100, 0, 0, 9, 99)] - [InlineData( 100, 10, 0, 0, 99, 9)] - [InlineData( 100, 100, 100, 100, 99, 99)] - [InlineData( 100, 100, 101, 102, 100, 101)] - [InlineData( 100, 101, 1, 1, 99, 100)] // Roslyn's ProjectExternalErrorReporter throws if end is less than start - public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingEndLineAndColumn(int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, int expectedEndLineNumber, int expectedEndColumnNumber) + [Fact] + public async Task AddMessageAsync_CriticalBuildMessageTaskAsTask_PassesTP_LOWAsPriority() + { + VSTASKPRIORITY? result = null; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - int? endLineResult = null; - int? endColumnResult = null; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - endLineResult = iEndLine; - endColumnResult = iEndColumn; - }); - - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - - var provider = CreateInstance(host); - await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", lineNumber, columnNumber, endLineNumber, endColumnNumber, "ErrorMessage", "HelpKeyword", "Sender"))); - - Assert.Equal(expectedEndLineNumber, endLineResult); - Assert.Equal(expectedEndColumnNumber, endColumnResult); - } - - // File ProjectFile ExpectedFileName - [Theory] - [InlineData(null, null, @"")] - [InlineData(@"", @"", @"")] - [InlineData(@"Foo.txt", @"", @"")] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/146 - [InlineData(@"C:\Foo.txt", @"", @"")] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/146 - [InlineData(@"C:\Foo.txt", @"C:\MyProject.csproj", @"C:\Foo.txt")] - [InlineData(@"Foo.txt", @"C:\MyProject.csproj", @"C:\Foo.txt")] - [InlineData(@"..\Foo.txt", @"C:\Bar\MyProject.csproj", @"C:\Foo.txt")] - [InlineData(@"..\Foo.txt", @"MyProject.csproj", @"")] - [InlineData(@"..\Foo.txt", @"<>", @"")] - [InlineData(@"<>", @"C:\MyProject.csproj", @"C:\<>")] - [InlineData(@"C:\MyProject.csproj", @"C:\MyProject.csproj", @"C:\MyProject.csproj")] - [InlineData(@"C:\Foo\..\MyProject.csproj", @"C:\MyProject.csproj", @"C:\MyProject.csproj")] - [InlineData(@"C:\Foo\Foo.txt", @"C:\Bar\MyProject.csproj", @"C:\Foo\Foo.txt")] - [InlineData(@"Foo.txt", @"C:\Bar\MyProject.csproj", @"C:\Bar\Foo.txt")] - public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingFileName(string? file, string? projectFile, string expectedFileName) + result = nPriority; + }); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host); + + await provider.AddMessageAsync(new TargetGeneratedError("Test", new CriticalBuildMessageEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender"))); + + Assert.Equal(VSTASKPRIORITY.TP_LOW, result); + } + + // ErrorMessage Code + [Theory] + [InlineData(null, "A")] + [InlineData("", "0000")] + [InlineData(" ", "1000")] + [InlineData("This is an error message.", "CA1000")] + [InlineData("This is an error message\r\n", "CS1000")] + [InlineData("This is an error message.\r\n", "BC1000")] + [InlineData("This is an error message.\r\n.And another", "BC1000\r\n")] + public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingErrorMessageAndCode(string? errorMessage, string code) + { + string errorMessageResult = "NotSet"; + string errorIdResult = "NotSet"; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - string fileNameResult = "NotSet"; - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => - { - fileNameResult = bstrFileName; - }); + errorMessageResult = bstrErrorMessage; + errorIdResult = bstrErrorId; + }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host); + var provider = CreateInstance(host); + await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, code, "File", 0, 0, 0, 0, errorMessage, "HelpKeyword", "Sender"))); - var args = new BuildErrorEventArgs(null, "Code", file, 0, 0, 0, 0, "ErrorMessage", "HelpKeyword", "Sender") - { - ProjectFile = projectFile - }; - await provider.AddMessageAsync(new TargetGeneratedError("Test", args)); + Assert.Equal(errorMessage, errorMessageResult); + Assert.Equal(code, errorIdResult); + } - Assert.Equal(expectedFileName, fileNameResult); - } + // Line Column Expected Line Expected Column + [Theory] + [InlineData( 0, 0, 0, 0)] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/145 + [InlineData( 0, -1, 0, 0)] // Is this the right behavior? + [InlineData( -1, 0, 0, 0)] // Is this the right behavior? + [InlineData( 1, 0, 0, 0)] + [InlineData( 0, 1, 0, 0)] + [InlineData( 2, 2, 1, 1)] + [InlineData( 10, 100, 9, 99)] + [InlineData( 100, 10, 99, 9)] + [InlineData( 100, 100, 99, 99)] + public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingLineAndColumnAdjustingBy1(int lineNumber, int columnNumber, int expectedLineNumber, int expectedColumnNumber) + { + int? lineResult = null; + int? columnResult = null; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => + { + lineResult = iLine; + columnResult = iColumn; + }); + + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + + var provider = CreateInstance(host); + await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", lineNumber, columnNumber, 0, 0, "ErrorMessage", "HelpKeyword", "Sender"))); + + Assert.Equal(expectedLineNumber, lineResult); + Assert.Equal(expectedColumnNumber, columnResult); + } - [Fact] - public async Task AddMessageAsync_WithLspPullDiagnosticsEnabled_ReturnsNotHandled() + // Line Column End Line End Column Expected End Line Expected End Column + [Theory] + [InlineData( 0, 0, 0, 0, 0, 0)] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/145 + [InlineData( 0, -1, 0, 0, 0, 0)] // Is this the right behavior? + [InlineData( -1, 0, 0, 0, 0, 0)] // Is this the right behavior? + [InlineData( 1, 0, 0, 0, 0, 0)] + [InlineData( 0, 1, 0, 0, 0, 0)] + [InlineData( 10, 100, 0, 0, 9, 99)] + [InlineData( 100, 10, 0, 0, 99, 9)] + [InlineData( 100, 100, 100, 100, 99, 99)] + [InlineData( 100, 100, 101, 102, 100, 101)] + [InlineData( 100, 101, 1, 1, 99, 100)] // Roslyn's ProjectExternalErrorReporter throws if end is less than start + public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingEndLineAndColumn(int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, int expectedEndLineNumber, int expectedEndColumnNumber) + { + int? endLineResult = null; + int? endColumnResult = null; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { }); - var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var provider = CreateInstance(host, lspPullDiagnosticsFeatureFlag: true); - var task = CreateDefaultTask(); + endLineResult = iEndLine; + endColumnResult = iEndColumn; + }); + + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - var result = await provider.AddMessageAsync(task); + var provider = CreateInstance(host); + await provider.AddMessageAsync(new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", lineNumber, columnNumber, endLineNumber, endColumnNumber, "ErrorMessage", "HelpKeyword", "Sender"))); - Assert.Equal(AddMessageResult.NotHandled, result); - } + Assert.Equal(expectedEndLineNumber, endLineResult); + Assert.Equal(expectedEndColumnNumber, endColumnResult); + } - private static TargetGeneratedError CreateDefaultTask() + // File ProjectFile ExpectedFileName + [Theory] + [InlineData(null, null, @"")] + [InlineData(@"", @"", @"")] + [InlineData(@"Foo.txt", @"", @"")] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/146 + [InlineData(@"C:\Foo.txt", @"", @"")] // Is this the right behavior? See https://github.com/dotnet/project-system/issues/146 + [InlineData(@"C:\Foo.txt", @"C:\MyProject.csproj", @"C:\Foo.txt")] + [InlineData(@"Foo.txt", @"C:\MyProject.csproj", @"C:\Foo.txt")] + [InlineData(@"..\Foo.txt", @"C:\Bar\MyProject.csproj", @"C:\Foo.txt")] + [InlineData(@"..\Foo.txt", @"MyProject.csproj", @"")] + [InlineData(@"..\Foo.txt", @"<>", @"")] + [InlineData(@"<>", @"C:\MyProject.csproj", @"C:\<>")] + [InlineData(@"C:\MyProject.csproj", @"C:\MyProject.csproj", @"C:\MyProject.csproj")] + [InlineData(@"C:\Foo\..\MyProject.csproj", @"C:\MyProject.csproj", @"C:\MyProject.csproj")] + [InlineData(@"C:\Foo\Foo.txt", @"C:\Bar\MyProject.csproj", @"C:\Foo\Foo.txt")] + [InlineData(@"Foo.txt", @"C:\Bar\MyProject.csproj", @"C:\Bar\Foo.txt")] + public async Task AddMessageAsync_BuildErrorAsTask_CallsReportErrorSettingFileName(string? file, string? projectFile, string expectedFileName) + { + string fileNameResult = "NotSet"; + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { - return new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender")); - } + fileNameResult = bstrFileName; + }); + + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); - private static LanguageServiceErrorListProvider CreateInstance( - IWorkspaceWriter? workspaceWriter = null, - bool lspPullDiagnosticsFeatureFlag = false) + var provider = CreateInstance(host); + + var args = new BuildErrorEventArgs(null, "Code", file, 0, 0, 0, 0, "ErrorMessage", "HelpKeyword", "Sender") { - workspaceWriter ??= IWorkspaceWriterFactory.Create(); + ProjectFile = projectFile + }; + await provider.AddMessageAsync(new TargetGeneratedError("Test", args)); + + Assert.Equal(expectedFileName, fileNameResult); + } + + [Fact] + public async Task AddMessageAsync_WithLspPullDiagnosticsEnabled_ReturnsNotHandled() + { + var reporter = IVsLanguageServiceBuildErrorReporter2Factory.ImplementReportError((string bstrErrorMessage, string bstrErrorId, VSTASKPRIORITY nPriority, int iLine, int iColumn, int iEndLine, int iEndColumn, string bstrFileName) => { }); + var host = IWorkspaceWriterFactory.ImplementErrorReporter(() => reporter); + var provider = CreateInstance(host, lspPullDiagnosticsFeatureFlag: true); + var task = CreateDefaultTask(); + + var result = await provider.AddMessageAsync(task); + + Assert.Equal(AddMessageResult.NotHandled, result); + } + + private static TargetGeneratedError CreateDefaultTask() + { + return new TargetGeneratedError("Test", new BuildErrorEventArgs(null, "Code", "File", 1, 1, 1, 1, "Message", "HelpKeyword", "Sender")); + } + + private static LanguageServiceErrorListProvider CreateInstance( + IWorkspaceWriter? workspaceWriter = null, + bool lspPullDiagnosticsFeatureFlag = false) + { + workspaceWriter ??= IWorkspaceWriterFactory.Create(); - var projectSystemOptions = new Mock(); - projectSystemOptions.Setup(m => m.IsLspPullDiagnosticsEnabledAsync(It.IsAny())).ReturnsAsync(lspPullDiagnosticsFeatureFlag); + var projectSystemOptions = new Mock(); + projectSystemOptions.Setup(m => m.IsLspPullDiagnosticsEnabledAsync(It.IsAny())).ReturnsAsync(lspPullDiagnosticsFeatureFlag); - var joinableTaskContext = IProjectThreadingServiceFactory.Create().JoinableTaskContext.Context; + var joinableTaskContext = IProjectThreadingServiceFactory.Create().JoinableTaskContext.Context; - var provider = new LanguageServiceErrorListProvider(UnconfiguredProjectFactory.Create(), workspaceWriter, projectSystemOptions.Object, joinableTaskContext); + var provider = new LanguageServiceErrorListProvider(UnconfiguredProjectFactory.Create(), workspaceWriter, projectSystemOptions.Object, joinableTaskContext); - return provider; - } + return provider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProviderTests.cs index f5dd58a320..e13844842f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectCompatibilityProviderTests.cs @@ -2,37 +2,36 @@ using Microsoft.Build.Construction; -namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp; + +public class CSharpProjectCompatibilityProviderTests { - public class CSharpProjectCompatibilityProviderTests + [Fact] + public async Task IsProjectCompatibleAsync_ReturnsTrue() { - [Fact] - public async Task IsProjectCompatibleAsync_ReturnsTrue() - { - var provider = CreateInstance(); + var provider = CreateInstance(); - var element = ProjectRootElement.Create(); + var element = ProjectRootElement.Create(); - var result = await provider.IsProjectCompatibleAsync(element); + var result = await provider.IsProjectCompatibleAsync(element); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task IsProjectNeedBeUpgradedAsync_ReturnsFalse() - { - var provider = CreateInstance(); + [Fact] + public async Task IsProjectNeedBeUpgradedAsync_ReturnsFalse() + { + var provider = CreateInstance(); - var element = ProjectRootElement.Create(); + var element = ProjectRootElement.Create(); - var result = await provider.IsProjectNeedBeUpgradedAsync(element); + var result = await provider.IsProjectNeedBeUpgradedAsync(element); - Assert.False(result); - } + Assert.False(result); + } - private static CSharpProjectCompatibilityProvider CreateInstance() - { - return new CSharpProjectCompatibilityProvider(); - } + private static CSharpProjectCompatibilityProvider CreateInstance() + { + return new CSharpProjectCompatibilityProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProviderTests.cs index 655b698f25..df19cad6db 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CSharp/CSharpProjectTypeGuidProviderTests.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.CSharp; + +public class CSharpProjectTypeGuidProviderTests { - public class CSharpProjectTypeGuidProviderTests + [Fact] + public void ProjectTypeGuid_ReturnsNonEmptyGuid() { - [Fact] - public void ProjectTypeGuid_ReturnsNonEmptyGuid() - { - var provider = CreateInstance(); + var provider = CreateInstance(); - // Handshake between the project system and factory around the actual guid value so we do not test - // for a specified guid, other than to confirm it's not empty - Assert.NotEqual(Guid.Empty, provider.ProjectTypeGuid); - } + // Handshake between the project system and factory around the actual guid value so we do not test + // for a specified guid, other than to confirm it's not empty + Assert.NotEqual(Guid.Empty, provider.ProjectTypeGuid); + } - private static CSharpProjectTypeGuidProvider CreateInstance() - { - return new CSharpProjectTypeGuidProvider(); - } + private static CSharpProjectTypeGuidProvider CreateInstance() + { + return new CSharpProjectTypeGuidProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ContractMetadata.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ContractMetadata.cs index 3bf995a7ae..36dcfed258 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ContractMetadata.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ContractMetadata.cs @@ -1,16 +1,15 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal partial class ComponentComposition { - internal partial class ComponentComposition + public class ContractMetadata { - public class ContractMetadata - { - public ProjectSystemContractProvider? Provider { get; set; } + public ProjectSystemContractProvider? Provider { get; set; } - public ProjectSystemContractScope? Scope { get; set; } + public ProjectSystemContractScope? Scope { get; set; } - public ImportCardinality? Cardinality { get; set; } - } + public ImportCardinality? Cardinality { get; set; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ScopeComponents.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ScopeComponents.cs index 7ab379fd87..5cf0b580d4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ScopeComponents.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.ScopeComponents.cs @@ -7,43 +7,42 @@ using SharedAttribute = System.Composition.SharedAttribute; using SharingBoundaryAttribute = System.Composition.SharingBoundaryAttribute; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal partial class ComponentComposition { - internal partial class ComponentComposition + // These components solely exist so that the MEF composition for + // these tests can see the "scopes" used within CPS. + + [Export] + private class GlobalScope + { + [Import] + [SharingBoundary(ExportContractNames.Scopes.ProjectService)] + private System.Composition.ExportFactory? ProjectServiceFactory { get; set; } + } + + [Export(typeof(IProjectService))] + [Shared(ExportContractNames.Scopes.ProjectService)] + private class ProjectServiceScope + { + [Import] + [SharingBoundary(ExportContractNames.Scopes.UnconfiguredProject)] + private ExportFactory? UnconfiguredProjectFactory { get; set; } + } + + [Export(typeof(UnconfiguredProject))] + [Shared(ExportContractNames.Scopes.UnconfiguredProject)] + private class UnconfiguredProjectScope + { + [Import] + [SharingBoundary(ExportContractNames.Scopes.ConfiguredProject)] + private ExportFactory? ConfiguredProjectFactory { get; set; } + } + + [Export(typeof(ConfiguredProject))] + [Shared(ExportContractNames.Scopes.ConfiguredProject)] + private class ConfiguredProjectScope { - // These components solely exist so that the MEF composition for - // these tests can see the "scopes" used within CPS. - - [Export] - private class GlobalScope - { - [Import] - [SharingBoundary(ExportContractNames.Scopes.ProjectService)] - private System.Composition.ExportFactory? ProjectServiceFactory { get; set; } - } - - [Export(typeof(IProjectService))] - [Shared(ExportContractNames.Scopes.ProjectService)] - private class ProjectServiceScope - { - [Import] - [SharingBoundary(ExportContractNames.Scopes.UnconfiguredProject)] - private ExportFactory? UnconfiguredProjectFactory { get; set; } - } - - [Export(typeof(UnconfiguredProject))] - [Shared(ExportContractNames.Scopes.UnconfiguredProject)] - private class UnconfiguredProjectScope - { - [Import] - [SharingBoundary(ExportContractNames.Scopes.ConfiguredProject)] - private ExportFactory? ConfiguredProjectFactory { get; set; } - } - - [Export(typeof(ConfiguredProject))] - [Shared(ExportContractNames.Scopes.ConfiguredProject)] - private class ConfiguredProjectScope - { - } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.cs index 044ea0a588..6ce0fc1349 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentComposition.cs @@ -4,257 +4,256 @@ using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +internal partial class ComponentComposition { - internal partial class ComponentComposition + /// + /// The list of assemblies that may contain exports. + /// + internal static readonly IReadOnlyList BuiltInAssemblies = new Assembly[] { - /// - /// The list of assemblies that may contain exports. - /// - internal static readonly IReadOnlyList BuiltInAssemblies = new Assembly[] - { - typeof(ConfiguredProjectImplicitActivationTracking).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed - typeof(VsContainedLanguageComponentsFactory).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed.VS - }; - - /// - /// The list of assemblies to scan for contracts. - /// - private static readonly IReadOnlyList ContractAssemblies = new Assembly[] - { - typeof(IProjectService).Assembly, // Microsoft.VisualStudio.ProjectSystem - typeof(IVsProjectServices).Assembly, // Microsoft.VisualStudio.ProjectSystem.VS - typeof(ConfiguredProjectImplicitActivationTracking).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed - typeof(VsContainedLanguageComponentsFactory).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed.VS - }; + typeof(ConfiguredProjectImplicitActivationTracking).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed + typeof(VsContainedLanguageComponentsFactory).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed.VS + }; + + /// + /// The list of assemblies to scan for contracts. + /// + private static readonly IReadOnlyList ContractAssemblies = new Assembly[] + { + typeof(IProjectService).Assembly, // Microsoft.VisualStudio.ProjectSystem + typeof(IVsProjectServices).Assembly, // Microsoft.VisualStudio.ProjectSystem.VS + typeof(ConfiguredProjectImplicitActivationTracking).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed + typeof(VsContainedLanguageComponentsFactory).Assembly, // Microsoft.VisualStudio.ProjectSystem.Managed.VS + }; - public static readonly ComponentComposition Instance = new(); + public static readonly ComponentComposition Instance = new(); - public ComponentComposition() - { - var discovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(Resolver.DefaultInstance), - new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true)); - - var parts = discovery.CreatePartsAsync(BuiltInAssemblies).GetAwaiter().GetResult(); - var scopeParts = discovery.CreatePartsAsync(typeof(UnconfiguredProjectScope), typeof(ConfiguredProjectScope), typeof(ProjectServiceScope), typeof(GlobalScope)).GetAwaiter().GetResult(); - - ComposableCatalog catalog = ComposableCatalog.Create(Resolver.DefaultInstance) - .AddParts(parts) - .AddParts(scopeParts) - .WithCompositionService(); - - // Prepare the self-host service and composition - Catalog = catalog; - Configuration = CompositionConfiguration.Create(catalog); - Contracts = CollectContractMetadata(ContractAssemblies.Union(BuiltInAssemblies)); - ContractsRequiringAppliesTo = CollectContractsRequiringAppliesTo(catalog); - InterfaceNames = CollectInterfaceNames(ContractAssemblies); - } + public ComponentComposition() + { + var discovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(Resolver.DefaultInstance), + new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true)); + + var parts = discovery.CreatePartsAsync(BuiltInAssemblies).GetAwaiter().GetResult(); + var scopeParts = discovery.CreatePartsAsync(typeof(UnconfiguredProjectScope), typeof(ConfiguredProjectScope), typeof(ProjectServiceScope), typeof(GlobalScope)).GetAwaiter().GetResult(); + + ComposableCatalog catalog = ComposableCatalog.Create(Resolver.DefaultInstance) + .AddParts(parts) + .AddParts(scopeParts) + .WithCompositionService(); + + // Prepare the self-host service and composition + Catalog = catalog; + Configuration = CompositionConfiguration.Create(catalog); + Contracts = CollectContractMetadata(ContractAssemblies.Union(BuiltInAssemblies)); + ContractsRequiringAppliesTo = CollectContractsRequiringAppliesTo(catalog); + InterfaceNames = CollectInterfaceNames(ContractAssemblies); + } - public ComposableCatalog Catalog { get; } + public ComposableCatalog Catalog { get; } - public CompositionConfiguration Configuration { get; } + public CompositionConfiguration Configuration { get; } - public IDictionary Contracts { get; } + public IDictionary Contracts { get; } - public IDictionary> ContractsRequiringAppliesTo { get; } + public IDictionary> ContractsRequiringAppliesTo { get; } - public ISet InterfaceNames { get; } + public ISet InterfaceNames { get; } - public ComposedPart? FindComposedPart(Type type) + public ComposedPart? FindComposedPart(Type type) + { + foreach (ComposedPart part in Configuration.Parts) { - foreach (ComposedPart part in Configuration.Parts) + if (type == part.Definition.Type) { - if (type == part.Definition.Type) - { - return part; - } + return part; } - - return null; } - public ComposablePartDefinition? FindComposablePartDefinition(Type type) + return null; + } + + public ComposablePartDefinition? FindComposablePartDefinition(Type type) + { + foreach (ComposablePartDefinition part in Catalog.Parts) { - foreach (ComposablePartDefinition part in Catalog.Parts) + if (type == part.Type) { - if (type == part.Type) - { - return part; - } + return part; } - - return null; } - private static IDictionary> CollectContractsRequiringAppliesTo(ComposableCatalog catalog) - { - var contractsRequiringAppliesTo = new Dictionary>(); + return null; + } - // First step, we scan all imports, and gather all places requiring "AppliesTo" metadata. - foreach (ComposablePartDefinition part in catalog.Parts) + private static IDictionary> CollectContractsRequiringAppliesTo(ComposableCatalog catalog) + { + var contractsRequiringAppliesTo = new Dictionary>(); + + // First step, we scan all imports, and gather all places requiring "AppliesTo" metadata. + foreach (ComposablePartDefinition part in catalog.Parts) + { + foreach (ImportDefinitionBinding import in part.ImportingMembers) { - foreach (ImportDefinitionBinding import in part.ImportingMembers) + if (IsAppliesToRequired(import)) { - if (IsAppliesToRequired(import)) + if (!contractsRequiringAppliesTo.TryGetValue(import.ImportDefinition.ContractName, out ISet contractTypes)) { - if (!contractsRequiringAppliesTo.TryGetValue(import.ImportDefinition.ContractName, out ISet contractTypes)) - { - contractTypes = new HashSet(); - contractsRequiringAppliesTo.Add(import.ImportDefinition.ContractName, contractTypes); - } - - if (import.ImportingSiteElementType is not null) - { - contractTypes.Add(import.ImportingSiteElementType); - } + contractTypes = new HashSet(); + contractsRequiringAppliesTo.Add(import.ImportDefinition.ContractName, contractTypes); + } + + if (import.ImportingSiteElementType is not null) + { + contractTypes.Add(import.ImportingSiteElementType); } } } - - return contractsRequiringAppliesTo; } - private static ISet CollectInterfaceNames(IEnumerable assemblies) + return contractsRequiringAppliesTo; + } + + private static ISet CollectInterfaceNames(IEnumerable assemblies) + { + var interfaceNames = new HashSet(); + foreach (Assembly assembly in assemblies) { - var interfaceNames = new HashSet(); - foreach (Assembly assembly in assemblies) + foreach (Type type in GetTypes(assembly)) { - foreach (Type type in GetTypes(assembly)) + if (type.IsPublic && type.IsInterface) { - if (type.IsPublic && type.IsInterface) - { - interfaceNames.Add(type.FullName); - } + interfaceNames.Add(type.FullName); } } - - return interfaceNames; } - private static Dictionary CollectContractMetadata(IEnumerable assemblies) - { - Requires.NotNull(assemblies); - var contracts = new Dictionary(StringComparer.Ordinal); - foreach (Assembly contractAssembly in assemblies) - { - ReadContractMetadata(contracts, contractAssembly); - } + return interfaceNames; + } - return contracts; + private static Dictionary CollectContractMetadata(IEnumerable assemblies) + { + Requires.NotNull(assemblies); + var contracts = new Dictionary(StringComparer.Ordinal); + foreach (Assembly contractAssembly in assemblies) + { + ReadContractMetadata(contracts, contractAssembly); } - private static void ReadContractMetadata(Dictionary contracts, Assembly contractAssembly) + return contracts; + } + + private static void ReadContractMetadata(Dictionary contracts, Assembly contractAssembly) + { + Requires.NotNull(contracts); + Requires.NotNull(contractAssembly); + foreach (ProjectSystemContractAttribute assemblyAttribute in contractAssembly.GetCustomAttributes()) { - Requires.NotNull(contracts); - Requires.NotNull(contractAssembly); - foreach (ProjectSystemContractAttribute assemblyAttribute in contractAssembly.GetCustomAttributes()) - { - var contractName = assemblyAttribute.ContractName; - var contractType = assemblyAttribute.ContractType; + var contractName = assemblyAttribute.ContractName; + var contractType = assemblyAttribute.ContractType; - if (contractName is not null || contractType is not null) - { - AddContractMetadata(contracts, contractName ?? contractType!.FullName, assemblyAttribute.Scope, assemblyAttribute.Provider, assemblyAttribute.Cardinality); - } + if (contractName is not null || contractType is not null) + { + AddContractMetadata(contracts, contractName ?? contractType!.FullName, assemblyAttribute.Scope, assemblyAttribute.Provider, assemblyAttribute.Cardinality); } + } - foreach (Type definedType in GetTypes(contractAssembly)) + foreach (Type definedType in GetTypes(contractAssembly)) + { + if (definedType.IsInterface || definedType.IsClass) { - if (definedType.IsInterface || definedType.IsClass) + foreach (ProjectSystemContractAttribute attribute in definedType.GetCustomAttributes()) { - foreach (ProjectSystemContractAttribute attribute in definedType.GetCustomAttributes()) - { - string name = attribute.ContractName ?? definedType.FullName; - AddContractMetadata(contracts, name, attribute.Scope, attribute.Provider, attribute.Cardinality); - } + string name = attribute.ContractName ?? definedType.FullName; + AddContractMetadata(contracts, name, attribute.Scope, attribute.Provider, attribute.Cardinality); } } } + } - private static Type[] GetTypes(Assembly assembly) + private static Type[] GetTypes(Assembly assembly) + { + try { - try - { - return assembly.GetTypes(); - } - catch (ReflectionTypeLoadException ex) - { - var exceptions = ex.LoaderExceptions.Where(e => !IsIgnorable(e)) - .ToArray(); - - if (exceptions.Length == 0) - return ex.Types.WhereNotNull().ToArray(); - - string message = ex.ToString(); + return assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + var exceptions = ex.LoaderExceptions.Where(e => !IsIgnorable(e)) + .ToArray(); - message += "\nLoaderExceptions:\n"; + if (exceptions.Length == 0) + return ex.Types.WhereNotNull().ToArray(); - for (int i = 0; i < ex.LoaderExceptions.Length; i++) - { - message += ex.LoaderExceptions[i].ToString(); - message += "\n"; - } + string message = ex.ToString(); - throw new Xunit.Sdk.XunitException(message); - } - } + message += "\nLoaderExceptions:\n"; - private static bool IsIgnorable(Exception exception) - { - if (exception is FileNotFoundException fileNotFound) + for (int i = 0; i < ex.LoaderExceptions.Length; i++) { - return fileNotFound.FileName.StartsWith("Microsoft.VisualStudio.ProjectServices,", StringComparison.Ordinal); + message += ex.LoaderExceptions[i].ToString(); + message += "\n"; } - return false; + throw new Xunit.Sdk.XunitException(message); } + } - private static void AddContractMetadata(Dictionary contracts, string name, ProjectSystemContractScope scope, ProjectSystemContractProvider provider, ImportCardinality cardinality) + private static bool IsIgnorable(Exception exception) + { + if (exception is FileNotFoundException fileNotFound) { - Requires.NotNull(contracts); - Requires.NotNullOrEmpty(name); + return fileNotFound.FileName.StartsWith("Microsoft.VisualStudio.ProjectServices,", StringComparison.Ordinal); + } - if (!contracts.TryGetValue(name, out ContractMetadata metadata)) + return false; + } + + private static void AddContractMetadata(Dictionary contracts, string name, ProjectSystemContractScope scope, ProjectSystemContractProvider provider, ImportCardinality cardinality) + { + Requires.NotNull(contracts); + Requires.NotNullOrEmpty(name); + + if (!contracts.TryGetValue(name, out ContractMetadata metadata)) + { + metadata = new ContractMetadata { - metadata = new ContractMetadata - { - Provider = provider, - Scope = scope, - Cardinality = cardinality - }; + Provider = provider, + Scope = scope, + Cardinality = cardinality + }; - contracts.Add(name, metadata); - } - else + contracts.Add(name, metadata); + } + else + { + // We don't support using the contract name with different interfaces, so we don't verify those contracts. + if (metadata.Scope != scope) { - // We don't support using the contract name with different interfaces, so we don't verify those contracts. - if (metadata.Scope != scope) - { - metadata.Scope = null; - } + metadata.Scope = null; + } - if (metadata.Provider != provider) - { - metadata.Provider = null; - } + if (metadata.Provider != provider) + { + metadata.Provider = null; + } - if (metadata.Cardinality != cardinality) - { - metadata.Cardinality = null; - } + if (metadata.Cardinality != cardinality) + { + metadata.Cardinality = null; } } + } - /// - /// Check whether the import requiring a component to have "AppliesTo" metadata. - /// If the imports ask metadata from the exports, and the metadata based on IAppliesToMetadataView, - /// the "AppliesTo" metadata is required. - /// - private static bool IsAppliesToRequired(ImportDefinitionBinding import) - { - Type appliesToView = typeof(IAppliesToMetadataView); - return import.MetadataType is not null && appliesToView.IsAssignableFrom(import.MetadataType); - } + /// + /// Check whether the import requiring a component to have "AppliesTo" metadata. + /// If the imports ask metadata from the exports, and the metadata based on IAppliesToMetadataView, + /// the "AppliesTo" metadata is required. + /// + private static bool IsAppliesToRequired(ImportDefinitionBinding import) + { + Type appliesToView = typeof(IAppliesToMetadataView); + return import.MetadataType is not null && appliesToView.IsAssignableFrom(import.MetadataType); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.AllExportsTestData.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.AllExportsTestData.cs index 66d969e69f..3f5bac62af 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.AllExportsTestData.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.AllExportsTestData.cs @@ -2,22 +2,21 @@ using System.Reflection; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public partial class ComponentVerificationTests { - public partial class ComponentVerificationTests + internal class AllExportsTestData : TheoryData { - internal class AllExportsTestData : TheoryData + public AllExportsTestData() { - public AllExportsTestData() + var types = from assembly in ComponentComposition.BuiltInAssemblies + from type in assembly.GetTypes() + where type.GetCustomAttributes().Any() + select type; + foreach (Type type in types) { - var types = from assembly in ComponentComposition.BuiltInAssemblies - from type in assembly.GetTypes() - where type.GetCustomAttributes().Any() - select type; - foreach (Type type in types) - { - Add(type); - } + Add(type); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposablePartDefinitionTestData.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposablePartDefinitionTestData.cs index cd0585ced1..9148c11e0b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposablePartDefinitionTestData.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposablePartDefinitionTestData.cs @@ -2,20 +2,19 @@ using Microsoft.VisualStudio.Composition; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public partial class ComponentVerificationTests { - public partial class ComponentVerificationTests + internal class ComposablePartDefinitionTestData : TheoryData { - internal class ComposablePartDefinitionTestData : TheoryData + public ComposablePartDefinitionTestData() { - public ComposablePartDefinitionTestData() - { - var catalog = ComponentComposition.Instance.Catalog; + var catalog = ComponentComposition.Instance.Catalog; - foreach (ComposablePartDefinition part in catalog.Parts) - { - Add(part.Type); - } + foreach (ComposablePartDefinition part in catalog.Parts) + { + Add(part.Type); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposedPartTestData.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposedPartTestData.cs index e21acd52eb..7fb08a8866 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposedPartTestData.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.ComposedPartTestData.cs @@ -2,21 +2,20 @@ using Microsoft.VisualStudio.Composition; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public partial class ComponentVerificationTests { - public partial class ComponentVerificationTests + // Produces inputs for theory based on the composed part + internal class ComposedPartTestData : TheoryData { - // Produces inputs for theory based on the composed part - internal class ComposedPartTestData : TheoryData + public ComposedPartTestData() { - public ComposedPartTestData() - { - var configuration = ComponentComposition.Instance.Configuration; + var configuration = ComponentComposition.Instance.Configuration; - foreach (ComposedPart part in configuration.Parts) - { - Add(part.Definition.Type); - } + foreach (ComposedPart part in configuration.Parts) + { + Add(part.Definition.Type); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.cs index 5148e9d18c..4f4c023b25 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ComponentVerificationTests.cs @@ -5,396 +5,395 @@ using Microsoft.VisualStudio.Composition.Reflection; using Microsoft.VisualStudio.Packaging; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public partial class ComponentVerificationTests { - public partial class ComponentVerificationTests + [Theory] + [ClassData(typeof(ComposedPartTestData))] + public void ImportsMustFilterBasedOnCapabilities(Type type) { - [Theory] - [ClassData(typeof(ComposedPartTestData))] - public void ImportsMustFilterBasedOnCapabilities(Type type) - { - // Imports should respect/filter the capabilities of the exports they receive + // Imports should respect/filter the capabilities of the exports they receive - var part = ComponentComposition.Instance.FindComposedPart(type); + var part = ComponentComposition.Instance.FindComposedPart(type); - Assert.NotNull(part); + Assert.NotNull(part); - foreach ((ImportDefinitionBinding import, IReadOnlyList exports) in part.SatisfyingExports) - { - // Only check properties. We don't verify ImportingConstructor. - if (import.ImportingMember is not PropertyInfo importingProperty) - return; + foreach ((ImportDefinitionBinding import, IReadOnlyList exports) in part.SatisfyingExports) + { + // Only check properties. We don't verify ImportingConstructor. + if (import.ImportingMember is not PropertyInfo importingProperty) + return; - Type memberType = importingProperty.PropertyType; + Type memberType = importingProperty.PropertyType; - // ImportMany, we want to use OrderPrecedenceImportCollection - if (import.ImportDefinition.Cardinality == ImportCardinality.ZeroOrMore) + // ImportMany, we want to use OrderPrecedenceImportCollection + if (import.ImportDefinition.Cardinality == ImportCardinality.ZeroOrMore) + { + if (exports.Any(b => !string.IsNullOrEmpty(GetAppliesToMetadata(b.ExportDefinition)))) { - if (exports.Any(b => !string.IsNullOrEmpty(GetAppliesToMetadata(b.ExportDefinition)))) + if (!IsSubclassOfGenericType(typeof(OrderPrecedenceImportCollection<,>), memberType)) { - if (!IsSubclassOfGenericType(typeof(OrderPrecedenceImportCollection<,>), memberType)) - { - Assert.Fail($"{part.Definition.Type.FullName}.{importingProperty.Name} needs to use OrderPrecedenceImportCollection to import components."); - } + Assert.Fail($"{part.Definition.Type.FullName}.{importingProperty.Name} needs to use OrderPrecedenceImportCollection to import components."); } - - return; } - // Single import - ExportDefinitionBinding exportBinding = exports.SingleOrDefault(); - if (exportBinding is not null) + return; + } + + // Single import + ExportDefinitionBinding exportBinding = exports.SingleOrDefault(); + if (exportBinding is not null) + { + string? appliesTo = GetAppliesToMetadata(exportBinding.ExportDefinition); + if (!string.IsNullOrEmpty(appliesTo) && !ContainsExpression(appliesTo)) { - string? appliesTo = GetAppliesToMetadata(exportBinding.ExportDefinition); - if (!string.IsNullOrEmpty(appliesTo) && !ContainsExpression(appliesTo)) + // If the consumer imports metadata, we assume it will be checked. + if (!IsSubclassOfGenericType(typeof(Lazy<,>), memberType)) { - // If the consumer imports metadata, we assume it will be checked. - if (!IsSubclassOfGenericType(typeof(Lazy<,>), memberType)) + // we require it to import the metadata, or the component requires the same capability, or the capability + // of the consumed component can be inferred from the capability of the consumer. + foreach (ExportDefinition exportDefinition in part.Definition.ExportDefinitions.Select(p => p.Value)) { - // we require it to import the metadata, or the component requires the same capability, or the capability - // of the consumed component can be inferred from the capability of the consumer. - foreach (ExportDefinition exportDefinition in part.Definition.ExportDefinitions.Select(p => p.Value)) + string? requiredAppliesTo = GetAppliesToMetadata(exportDefinition); + if (requiredAppliesTo is null || + !ContainsExpression(requiredAppliesTo)) { - string? requiredAppliesTo = GetAppliesToMetadata(exportDefinition); - if (requiredAppliesTo is null || - !ContainsExpression(requiredAppliesTo)) - { - Assert.Fail($"{part.Definition.Type.FullName}.{ importingProperty.Name} needs to check AppliesTo metadata of the imported component."); - } + Assert.Fail($"{part.Definition.Type.FullName}.{ importingProperty.Name} needs to check AppliesTo metadata of the imported component."); } } } } } } + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void ExportsFromSamePartMustApplyToSameCapabilities(Type type) - { - // Exports coming from a single part must apply to the same capabilities - var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void ExportsFromSamePartMustApplyToSameCapabilities(Type type) + { + // Exports coming from a single part must apply to the same capabilities + var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); - Assert.NotNull(definition); + Assert.NotNull(definition); - // Gather the appliesTo metadata from all exports of the same part. - var appliesToMetadata = new List(); - foreach ((MemberRef? memberRef, ExportDefinition exportDefinition) in definition.ExportDefinitions) - { - if (memberRef?.IsStatic == true) - continue; + // Gather the appliesTo metadata from all exports of the same part. + var appliesToMetadata = new List(); + foreach ((MemberRef? memberRef, ExportDefinition exportDefinition) in definition.ExportDefinitions) + { + if (memberRef?.IsStatic == true) + continue; - exportDefinition.Metadata.TryGetValue(nameof(AppliesToAttribute.AppliesTo), out object? metadata); - if (metadata is not null) - appliesToMetadata.Add((string)metadata); - } + exportDefinition.Metadata.TryGetValue(nameof(AppliesToAttribute.AppliesTo), out object? metadata); + if (metadata is not null) + appliesToMetadata.Add((string)metadata); + } - // Now check all of them should be the same. - if (appliesToMetadata.Distinct().Count() > 1) - { - Assert.Fail($"{definition.Type.FullName} exports multiple values with differing AppliesTo. All exports from a component must apply to the same capabilities."); - } + // Now check all of them should be the same. + if (appliesToMetadata.Distinct().Count() > 1) + { + Assert.Fail($"{definition.Type.FullName} exports multiple values with differing AppliesTo. All exports from a component must apply to the same capabilities."); } + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void CertainExportsMustNotBeMarkedWithDynamicCapabilities(Type type) + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void CertainExportsMustNotBeMarkedWithDynamicCapabilities(Type type) + { + string[] contractsWithFixedCapabilities = { - string[] contractsWithFixedCapabilities = - { - ExportContractNames.VsTypes.ProjectNodeComExtension, - "Microsoft.VisualStudio.ProjectSystem.ConfiguredProject.AutoLoad", - "Microsoft.VisualStudio.ProjectSystem.Project.AutoLoad", - }; + ExportContractNames.VsTypes.ProjectNodeComExtension, + "Microsoft.VisualStudio.ProjectSystem.ConfiguredProject.AutoLoad", + "Microsoft.VisualStudio.ProjectSystem.Project.AutoLoad", + }; - var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); + var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); - Assert.NotNull(definition); + Assert.NotNull(definition); - foreach (KeyValuePair exportDefinitionPair in definition.ExportDefinitions) + foreach (KeyValuePair exportDefinitionPair in definition.ExportDefinitions) + { + ExportDefinition export = exportDefinitionPair.Value; + if (contractsWithFixedCapabilities.Contains(export.ContractName) && + export.Metadata.TryGetValue(nameof(AppliesToAttribute.AppliesTo), out object? metadata) && + metadata is string appliesTo) { - ExportDefinition export = exportDefinitionPair.Value; - if (contractsWithFixedCapabilities.Contains(export.ContractName) && - export.Metadata.TryGetValue(nameof(AppliesToAttribute.AppliesTo), out object? metadata) && - metadata is string appliesTo) - { - IEnumerable capabilities = GetRawCapabilities(appliesTo); - IEnumerable fixedCapabilities = GetFixedCapabilities(); + IEnumerable capabilities = GetRawCapabilities(appliesTo); + IEnumerable fixedCapabilities = GetFixedCapabilities(); - var dynamicCapabilities = capabilities.Except(fixedCapabilities); + var dynamicCapabilities = capabilities.Except(fixedCapabilities); - Assert.False(dynamicCapabilities.Any(), @$"{definition.Type.FullName} exports {export.ContractName} based on a dynamic capabilities '{string.Join(";", dynamicCapabilities)}'. This contract is used during project initialization and must use a capability that doesn't change over the lifetime of a project. These capabilities are specified in src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\Packaging\ProjectTypeRegistration.cs"); - } + Assert.False(dynamicCapabilities.Any(), @$"{definition.Type.FullName} exports {export.ContractName} based on a dynamic capabilities '{string.Join(";", dynamicCapabilities)}'. This contract is used during project initialization and must use a capability that doesn't change over the lifetime of a project. These capabilities are specified in src\Microsoft.VisualStudio.ProjectSystem.Managed.VS\Packaging\ProjectTypeRegistration.cs"); } + } - static IEnumerable GetFixedCapabilities() - { - var fixedCapabilities = new HashSet(); - fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.Default)); - fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.CSharp)); - fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.VisualBasic)); - fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.FSharp)); + static IEnumerable GetFixedCapabilities() + { + var fixedCapabilities = new HashSet(); + fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.Default)); + fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.CSharp)); + fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.VisualBasic)); + fixedCapabilities.AddRange(GetCapabilitiesFromProjectType(ProjectTypeCapabilities.FSharp)); - return fixedCapabilities; - } + return fixedCapabilities; + } - static IEnumerable GetCapabilitiesFromProjectType(string capabilities) - { - return capabilities.Split(new char[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); - } + static IEnumerable GetCapabilitiesFromProjectType(string capabilities) + { + return capabilities.Split(new char[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); + } - static IEnumerable GetRawCapabilities(string appliesTo) - { - return appliesTo.Split(new char[] { '&', '|', '(', ')', ' ', '!', }, StringSplitOptions.RemoveEmptyEntries); - } + static IEnumerable GetRawCapabilities(string appliesTo) + { + return appliesTo.Split(new char[] { '&', '|', '(', ')', ' ', '!', }, StringSplitOptions.RemoveEmptyEntries); } + } - [Theory] - [ClassData(typeof(AllExportsTestData))] - public void ExportsMustBeConstructable(Type type) + [Theory] + [ClassData(typeof(AllExportsTestData))] + public void ExportsMustBeConstructable(Type type) + { + bool hasParameterlessConstructor = false; + int importingConstructors = 0; + foreach (var constructor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) { - bool hasParameterlessConstructor = false; - int importingConstructors = 0; - foreach (var constructor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) + if (constructor.GetParameters().Length == 0) { - if (constructor.GetParameters().Length == 0) - { - hasParameterlessConstructor = true; - } - else if (constructor.GetCustomAttribute() is not null) - { - importingConstructors++; - } + hasParameterlessConstructor = true; + } + else if (constructor.GetCustomAttribute() is not null) + { + importingConstructors++; } - Assert.True(importingConstructors <= 1, "MEF exports cannot have more than one constructor marked [ImportingConstructor]"); - Assert.True(hasParameterlessConstructor || importingConstructors == 1, "MEF exports must have a parameterless constructor and/or a single constructor marked with [ImportingConstructor]"); } + Assert.True(importingConstructors <= 1, "MEF exports cannot have more than one constructor marked [ImportingConstructor]"); + Assert.True(hasParameterlessConstructor || importingConstructors == 1, "MEF exports must have a parameterless constructor and/or a single constructor marked with [ImportingConstructor]"); + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void ExportsMustBeMarkedWithApplyToIfRequired(Type type) - { - // If a contract requires AppliesTo to be specified, then an export must specify it + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void ExportsMustBeMarkedWithApplyToIfRequired(Type type) + { + // If a contract requires AppliesTo to be specified, then an export must specify it + + var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); - var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); + Assert.NotNull(definition); - Assert.NotNull(definition); + foreach ((MemberRef? memberRef, ExportDefinition exportDefinition) in definition.ExportDefinitions) + { + var contractsRequiringMetadata = ComponentComposition.Instance.ContractsRequiringAppliesTo; - foreach ((MemberRef? memberRef, ExportDefinition exportDefinition) in definition.ExportDefinitions) + // If the exports has already had the metadata, it is good. + if (exportDefinition.Metadata.ContainsKey(nameof(AppliesToAttribute.AppliesTo))) { - var contractsRequiringMetadata = ComponentComposition.Instance.ContractsRequiringAppliesTo; + return; + } - // If the exports has already had the metadata, it is good. - if (exportDefinition.Metadata.ContainsKey(nameof(AppliesToAttribute.AppliesTo))) + // Check whether the export satisfy any contract required the appliesTo metadata. + // If it matches one of the contract, we will report an error, because it lacks the required metadata. + if (contractsRequiringMetadata.TryGetValue(exportDefinition.ContractName, out ISet contractTypes)) + { + Type exportType; + if (memberRef is null) { - return; + exportType = definition.Type; } - - // Check whether the export satisfy any contract required the appliesTo metadata. - // If it matches one of the contract, we will report an error, because it lacks the required metadata. - if (contractsRequiringMetadata.TryGetValue(exportDefinition.ContractName, out ISet contractTypes)) + else { - Type exportType; - if (memberRef is null) - { - exportType = definition.Type; - } - else - { - exportType = memberRef.DeclaringType.Resolve(); - } + exportType = memberRef.DeclaringType.Resolve(); + } - if (contractTypes.Any(t => t.IsAssignableFrom(exportType))) - { - Assert.Fail($"{definition.Type.FullName} must specify [AppliesTo] to its export of {exportType}."); - } + if (contractTypes.Any(t => t.IsAssignableFrom(exportType))) + { + Assert.Fail($"{definition.Type.FullName} must specify [AppliesTo] to its export of {exportType}."); } } } + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void ImportsMustRespectContractImportCardinality(Type type) - { - // Imports must respect import cardinality specified via [ProjectSystemContract] + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void ImportsMustRespectContractImportCardinality(Type type) + { + // Imports must respect import cardinality specified via [ProjectSystemContract] - var contracts = ComponentComposition.Instance.Contracts; - var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); + var contracts = ComponentComposition.Instance.Contracts; + var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); - Assert.NotNull(definition); + Assert.NotNull(definition); - foreach (ImportDefinitionBinding import in definition.Imports) + foreach (ImportDefinitionBinding import in definition.Imports) + { + ImportDefinition importDefinition = import.ImportDefinition; + if (contracts.TryGetValue(importDefinition.ContractName, out ComponentComposition.ContractMetadata contractMetadata)) { - ImportDefinition importDefinition = import.ImportDefinition; - if (contracts.TryGetValue(importDefinition.ContractName, out ComponentComposition.ContractMetadata contractMetadata)) + if (contractMetadata.Cardinality == ImportCardinality.ZeroOrMore && importDefinition.Cardinality != ImportCardinality.ZeroOrMore) { - if (contractMetadata.Cardinality == ImportCardinality.ZeroOrMore && importDefinition.Cardinality != ImportCardinality.ZeroOrMore) - { - Assert.Fail($"Must use [ImportMany] in {definition.Type.FullName} to import a contract {importDefinition.ContractName} which can be implemented by an extension."); - } + Assert.Fail($"Must use [ImportMany] in {definition.Type.FullName} to import a contract {importDefinition.ContractName} which can be implemented by an extension."); } } } + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void ImportsMustMatchExportScope(Type type) - { - // Imports cannot import something from a child scope, if the part comes from parent scope + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void ImportsMustMatchExportScope(Type type) + { + // Imports cannot import something from a child scope, if the part comes from parent scope - var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); + var definition = ComponentComposition.Instance.FindComposablePartDefinition(type); - Assert.NotNull(definition); + Assert.NotNull(definition); - foreach (ImportDefinitionBinding import in definition.Imports) - { - ImportDefinition importDefinition = import.ImportDefinition; - if (importDefinition.ExportFactorySharingBoundaries.Count > 0) - return; // You can import something from child if its the start of a scope + foreach (ImportDefinitionBinding import in definition.Imports) + { + ImportDefinition importDefinition = import.ImportDefinition; + if (importDefinition.ExportFactorySharingBoundaries.Count > 0) + return; // You can import something from child if its the start of a scope - var contracts = ComponentComposition.Instance.Contracts; + var contracts = ComponentComposition.Instance.Contracts; - if (contracts.TryGetValue(importDefinition.ContractName, out ComponentComposition.ContractMetadata importContractMetadata) && importContractMetadata.Scope != null) + if (contracts.TryGetValue(importDefinition.ContractName, out ComponentComposition.ContractMetadata importContractMetadata) && importContractMetadata.Scope != null) + { + foreach (KeyValuePair export in definition.ExportDefinitions) { - foreach (KeyValuePair export in definition.ExportDefinitions) + if (contracts.TryGetValue(export.Value.ContractName, out ComponentComposition.ContractMetadata exportContractMetadata) && exportContractMetadata.Scope != null) { - if (contracts.TryGetValue(export.Value.ContractName, out ComponentComposition.ContractMetadata exportContractMetadata) && exportContractMetadata.Scope != null) + // Do we import from a child scope but export to a parent scope? ie Importing ConfiguredProject, but exporting to an UnconfiguredProject service would be invalid + if (exportContractMetadata.Scope < importContractMetadata.Scope) { - // Do we import from a child scope but export to a parent scope? ie Importing ConfiguredProject, but exporting to an UnconfiguredProject service would be invalid - if (exportContractMetadata.Scope < importContractMetadata.Scope) - { - Assert.Fail($"{definition.Type.FullName} exports to the {exportContractMetadata.Scope.Value} scope, but it imports {importDefinition.ContractName} from {importContractMetadata.Scope} scope, which is a child of the preceeding scope."); - } + Assert.Fail($"{definition.Type.FullName} exports to the {exportContractMetadata.Scope.Value} scope, but it imports {importDefinition.ContractName} from {importContractMetadata.Scope} scope, which is a child of the preceeding scope."); } } } } } + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void ImportsMustImportContractsMarkedWithProjectSystemContract(Type type) - { - // Imports must import interfaces that are marked with [ProjectSystemContract] + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void ImportsMustImportContractsMarkedWithProjectSystemContract(Type type) + { + // Imports must import interfaces that are marked with [ProjectSystemContract] - var partDefinition = ComponentComposition.Instance.FindComposablePartDefinition(type); + var partDefinition = ComponentComposition.Instance.FindComposablePartDefinition(type); - Assert.NotNull(partDefinition); + Assert.NotNull(partDefinition); - foreach (ImportDefinitionBinding import in partDefinition.Imports) - { - ImportDefinition importDefinition = import.ImportDefinition; + foreach (ImportDefinitionBinding import in partDefinition.Imports) + { + ImportDefinition importDefinition = import.ImportDefinition; - string? typeName = importDefinition.ExportConstraints.OfType().FirstOrDefault()?.TypeIdentityName; - string contractName = GetContractName(importDefinition); + string? typeName = importDefinition.ExportConstraints.OfType().FirstOrDefault()?.TypeIdentityName; + string contractName = GetContractName(importDefinition); - if (!ValidateContractAnnotation(typeName, contractName, partDefinition)) - { - Assert.Fail($"{partDefinition.Type.FullName} imports type {typeName}, which is not applied with [ProjectSystemContract]"); - } + if (!ValidateContractAnnotation(typeName, contractName, partDefinition)) + { + Assert.Fail($"{partDefinition.Type.FullName} imports type {typeName}, which is not applied with [ProjectSystemContract]"); } } + } - [Theory] - [ClassData(typeof(ComposablePartDefinitionTestData))] - public void ExportsMustExportContractsMarkedWithProjectSystemContract(Type type) - { - // When a parts export types from our assemblies, those exported types must be annotated with [ProjectSystemContract] + [Theory] + [ClassData(typeof(ComposablePartDefinitionTestData))] + public void ExportsMustExportContractsMarkedWithProjectSystemContract(Type type) + { + // When a parts export types from our assemblies, those exported types must be annotated with [ProjectSystemContract] - var partDefinition = ComponentComposition.Instance.FindComposablePartDefinition(type); + var partDefinition = ComponentComposition.Instance.FindComposablePartDefinition(type); - Assert.NotNull(partDefinition); + Assert.NotNull(partDefinition); - foreach ((_, ExportDefinition exportDefinition) in partDefinition.ExportDefinitions) + foreach ((_, ExportDefinition exportDefinition) in partDefinition.ExportDefinitions) + { + if (exportDefinition.Metadata.TryGetValue("ExportTypeIdentity", out object? value) && value is string typeName) { - if (exportDefinition.Metadata.TryGetValue("ExportTypeIdentity", out object? value) && value is string typeName) + if (!ValidateContractAnnotation(typeName, exportDefinition.ContractName, partDefinition)) { - if (!ValidateContractAnnotation(typeName, exportDefinition.ContractName, partDefinition)) - { - Assert.Fail($"{partDefinition.Type.FullName} exports type {typeName}, which is not applied with [ProjectSystemContract]"); - } + Assert.Fail($"{partDefinition.Type.FullName} exports type {typeName}, which is not applied with [ProjectSystemContract]"); } } } + } - private bool ValidateContractAnnotation(string? typeName, string contractName, ComposablePartDefinition part) - { - Requires.NotNull(contractName); - - if (typeName is not null && ComponentComposition.Instance.Contracts.ContainsKey(typeName)) - { - // We have seen [ProjectSystemContract] on this type. - return true; - } - - if (ComponentComposition.Instance.Contracts.ContainsKey(contractName)) - { - // We have seen [ProjectSystemContract] exported with this contract name. - return true; - } - - if (ComponentComposition.Instance.InterfaceNames.Contains(contractName)) - { - // This type is from our assemblies, but is not annotated. - // If it was annotated, we would have returned true above. - // But here we are. - return false; - } + private bool ValidateContractAnnotation(string? typeName, string contractName, ComposablePartDefinition part) + { + Requires.NotNull(contractName); - // This type is outside of the assemblies we track, so we don't require validation here. + if (typeName is not null && ComponentComposition.Instance.Contracts.ContainsKey(typeName)) + { + // We have seen [ProjectSystemContract] on this type. return true; } - private static string GetContractName(ImportDefinition import) + if (ComponentComposition.Instance.Contracts.ContainsKey(contractName)) { - if (import.Metadata.TryGetValue("System.ComponentModel.Composition.GenericContractName", out var value) && value is not null) - { - return (string)value; - } - - return import.ContractName; + // We have seen [ProjectSystemContract] exported with this contract name. + return true; } - - /// - /// Check whether a capability is not a simple string, but a complex expression. - /// We don't have built-in logic to check whether one expression can infer another one today, so we don't do validation when an expression is being used. - /// - private static bool ContainsExpression(string? capability) + + if (ComponentComposition.Instance.InterfaceNames.Contains(contractName)) { - return capability?.IndexOfAny(new char[] { '&', '|', '!' }) >= 0; + // This type is from our assemblies, but is not annotated. + // If it was annotated, we would have returned true above. + // But here we are. + return false; } - /// - /// Check whether a type is a subclass of a generic type. - /// - private static bool IsSubclassOfGenericType(Type genericType, Type type) + // This type is outside of the assemblies we track, so we don't require validation here. + return true; + } + + private static string GetContractName(ImportDefinition import) + { + if (import.Metadata.TryGetValue("System.ComponentModel.Composition.GenericContractName", out var value) && value is not null) { - while (type is not null && type != typeof(object)) - { - Type currentType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; - if (genericType == currentType) - { - return true; - } + return (string)value; + } - type = type.BaseType; - } + return import.ContractName; + } - return false; - } + /// + /// Check whether a capability is not a simple string, but a complex expression. + /// We don't have built-in logic to check whether one expression can infer another one today, so we don't do validation when an expression is being used. + /// + private static bool ContainsExpression(string? capability) + { + return capability?.IndexOfAny(new char[] { '&', '|', '!' }) >= 0; + } - /// - /// Get AppliesTo metadata from an export. - /// - /// returns null if the metadata cannot be found. - private static string? GetAppliesToMetadata(ExportDefinition exportDefinition) + /// + /// Check whether a type is a subclass of a generic type. + /// + private static bool IsSubclassOfGenericType(Type genericType, Type type) + { + while (type is not null && type != typeof(object)) { - if (exportDefinition.Metadata.TryGetValue(nameof(AppliesToAttribute.AppliesTo), out object? appliesToMetadata) && appliesToMetadata is not null) + Type currentType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; + if (genericType == currentType) { - return (string)appliesToMetadata; + return true; } - return null; + type = type.BaseType; + } + + return false; + } + + /// + /// Get AppliesTo metadata from an export. + /// + /// returns null if the metadata cannot be found. + private static string? GetAppliesToMetadata(ExportDefinition exportDefinition) + { + if (exportDefinition.Metadata.TryGetValue(nameof(AppliesToAttribute.AppliesTo), out object? appliesToMetadata) && appliesToMetadata is not null) + { + return (string)appliesToMetadata; } + + return null; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CreateFileFromTemplateServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CreateFileFromTemplateServiceTests.cs index 7c33642596..1047ce1f56 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CreateFileFromTemplateServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/CreateFileFromTemplateServiceTests.cs @@ -4,102 +4,101 @@ using EnvDTE80; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public class CreateFileFromTemplateServiceTests { - public class CreateFileFromTemplateServiceTests + [Fact] + public async Task CreateFile_NullTemplateFile_ThrowsArgumentNull() { - [Fact] - public async Task CreateFile_NullTemplateFile_ThrowsArgumentNull() + var service = CreateInstance(); + + await Assert.ThrowsAsync("templateFile", () => { - var service = CreateInstance(); + return service.CreateFileAsync(null!, "Path"); + }); + } - await Assert.ThrowsAsync("templateFile", () => - { - return service.CreateFileAsync(null!, "Path"); - }); - } + [Fact] + public async Task CreateFile_NullAsPath_ThrowsArgumentNull() + { + var service = CreateInstance(); - [Fact] - public async Task CreateFile_NullAsPath_ThrowsArgumentNull() + await Assert.ThrowsAsync("path", () => { - var service = CreateInstance(); + return service.CreateFileAsync("SomeFile", null!); + }); + } - await Assert.ThrowsAsync("path", () => - { - return service.CreateFileAsync("SomeFile", null!); - }); - } + [Fact] + public async Task CreateFile_EmptyAsPath_ThrowsArgument() + { + var service = CreateInstance(); - [Fact] - public async Task CreateFile_EmptyAsPath_ThrowsArgument() + await Assert.ThrowsAsync("path", () => { - var service = CreateInstance(); - - await Assert.ThrowsAsync("path", () => - { - return service.CreateFileAsync("SomeFile", string.Empty); - }); - } - - [Theory] - [InlineData(@"C:\Path\To\TemplateFile", true)] - [InlineData(@"C:\Path\To\TemplateFile", false)] - [InlineData(null, false)] - public async Task CreateFile(string templateFilePath, bool expectedResult) - { - string templateName = "SettingsInternal.zip"; - string fileName = "Settings.settings"; + return service.CreateFileAsync("SomeFile", string.Empty); + }); + } - var hierarchy = IVsHierarchyFactory.Create(); - var solution = (Solution)SolutionFactory.CreateWithGetProjectItemTemplate((templateFile, language) => - { - Assert.Equal(templateName, templateFile); - return templateFilePath; - }); + [Theory] + [InlineData(@"C:\Path\To\TemplateFile", true)] + [InlineData(@"C:\Path\To\TemplateFile", false)] + [InlineData(null, false)] + public async Task CreateFile(string templateFilePath, bool expectedResult) + { + string templateName = "SettingsInternal.zip"; + string fileName = "Settings.settings"; - var vsProject = (IVsProject4)IVsHierarchyFactory.Create(); - vsProject.ImplementAddItemWithSpecific((itemId, itemOperation, itemName, cOpen, files, result) => - { - Assert.Equal(VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD, itemOperation); - Assert.Equal(fileName, itemName); - Assert.Equal((uint)1, cOpen); - Assert.Equal(new string[] { templateFilePath }, files); + var hierarchy = IVsHierarchyFactory.Create(); + var solution = (Solution)SolutionFactory.CreateWithGetProjectItemTemplate((templateFile, language) => + { + Assert.Equal(templateName, templateFile); + return templateFilePath; + }); - result[0] = expectedResult ? VSADDRESULT.ADDRESULT_Success : VSADDRESULT.ADDRESULT_Failure; + var vsProject = (IVsProject4)IVsHierarchyFactory.Create(); + vsProject.ImplementAddItemWithSpecific((itemId, itemOperation, itemName, cOpen, files, result) => + { + Assert.Equal(VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD, itemOperation); + Assert.Equal(fileName, itemName); + Assert.Equal((uint)1, cOpen); + Assert.Equal(new string[] { templateFilePath }, files); - return VSConstants.S_OK; - }); + result[0] = expectedResult ? VSADDRESULT.ADDRESULT_Success : VSADDRESULT.ADDRESULT_Failure; - var dte = DTEFactory.ImplementSolution(() => solution); + return VSConstants.S_OK; + }); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => vsProject); - var properties = CreateProperties(); - var service = CreateInstance(projectVsServices, dte, properties); + var dte = DTEFactory.ImplementSolution(() => solution); - bool result = await service.CreateFileAsync(templateName, Path.Combine("Moniker", fileName)); - Assert.Equal(expectedResult, result); - } + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => vsProject); + var properties = CreateProperties(); + var service = CreateInstance(projectVsServices, dte, properties); - private static CreateFileFromTemplateService CreateInstance() - { - return CreateInstance(null, null, null); - } + bool result = await service.CreateFileAsync(templateName, Path.Combine("Moniker", fileName)); + Assert.Equal(expectedResult, result); + } - private static CreateFileFromTemplateService CreateInstance(IUnconfiguredProjectVsServices? projectVsServices, DTE2? dte, ProjectProperties? properties) - { - projectVsServices ??= IUnconfiguredProjectVsServicesFactory.Create(); - dte ??= DTEFactory.Create(); - properties ??= ProjectPropertiesFactory.CreateEmpty(); + private static CreateFileFromTemplateService CreateInstance() + { + return CreateInstance(null, null, null); + } - return new CreateFileFromTemplateService(projectVsServices, IVsUIServiceFactory.Create(dte), properties); - } + private static CreateFileFromTemplateService CreateInstance(IUnconfiguredProjectVsServices? projectVsServices, DTE2? dte, ProjectProperties? properties) + { + projectVsServices ??= IUnconfiguredProjectVsServicesFactory.Create(); + dte ??= DTEFactory.Create(); + properties ??= ProjectPropertiesFactory.CreateEmpty(); - private static ProjectProperties CreateProperties() - { - var properties = ProjectPropertiesFactory.Create(UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TemplateLanguageProperty, "CSharp")); + return new CreateFileFromTemplateService(projectVsServices, IVsUIServiceFactory.Create(dte), properties); + } + + private static ProjectProperties CreateProperties() + { + var properties = ProjectPropertiesFactory.Create(UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.TemplateLanguageProperty, "CSharp")); - return properties; - } + return properties; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/DebugProfileEnumValuesGeneratorTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/DebugProfileEnumValuesGeneratorTests.cs index abcb4dcd06..172fe43f8f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/DebugProfileEnumValuesGeneratorTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/DebugProfileEnumValuesGeneratorTests.cs @@ -3,75 +3,74 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +public class DebugProfileEnumValuesGeneratorTests { - public class DebugProfileEnumValuesGeneratorTests + private readonly List _profiles = new() { - private readonly List _profiles = new() - { - new LaunchProfile("Profile1", null, launchBrowser: true), - new LaunchProfile("MyCommand", null), - new LaunchProfile("Foo", null), - new LaunchProfile("Bar", null), - new LaunchProfile("Foo & Bar", null) - }; + new LaunchProfile("Profile1", null, launchBrowser: true), + new LaunchProfile("MyCommand", null), + new LaunchProfile("Foo", null), + new LaunchProfile("Bar", null), + new LaunchProfile("Foo & Bar", null) + }; - [Fact] - public async Task DebugProfileEnumValuesGenerator_GetListsValuesAsyncTests() + [Fact] + public async Task DebugProfileEnumValuesGenerator_GetListsValuesAsyncTests() + { + var testProfiles = new Mock(); + testProfiles.Setup(m => m.ActiveProfile).Returns(() => { return _profiles[1]; }); + testProfiles.Setup(m => m.Profiles).Returns(() => { - var testProfiles = new Mock(); - testProfiles.Setup(m => m.ActiveProfile).Returns(() => { return _profiles[1]; }); - testProfiles.Setup(m => m.Profiles).Returns(() => - { - return _profiles.ToImmutableList(); - }); + return _profiles.ToImmutableList(); + }); - var moqProfileProvider = new Mock(); - moqProfileProvider.Setup(p => p.CurrentSnapshot).Returns(testProfiles.Object); - var threadingService = IProjectThreadingServiceFactory.Create(); + var moqProfileProvider = new Mock(); + moqProfileProvider.Setup(p => p.CurrentSnapshot).Returns(testProfiles.Object); + var threadingService = IProjectThreadingServiceFactory.Create(); - var generator = - new DebugProfileEnumValuesGenerator(moqProfileProvider.Object, threadingService); - ICollection results = await generator.GetListedValuesAsync(); - AssertEx.CollectionLength(results, 5); - Assert.True(results.ElementAt(0).Name == "Profile1" && results.ElementAt(0).DisplayName == "Profile1"); - Assert.True(results.ElementAt(1).Name == "MyCommand" && results.ElementAt(1).DisplayName == "MyCommand"); - Assert.True(results.ElementAt(2).Name == "Foo" && results.ElementAt(2).DisplayName == "Foo"); - Assert.True(results.ElementAt(3).Name == "Bar" && results.ElementAt(3).DisplayName == "Bar"); - Assert.True(results.ElementAt(4).Name == "Foo & Bar" && results.ElementAt(4).DisplayName == "Foo && Bar"); - } + var generator = + new DebugProfileEnumValuesGenerator(moqProfileProvider.Object, threadingService); + ICollection results = await generator.GetListedValuesAsync(); + AssertEx.CollectionLength(results, 5); + Assert.True(results.ElementAt(0).Name == "Profile1" && results.ElementAt(0).DisplayName == "Profile1"); + Assert.True(results.ElementAt(1).Name == "MyCommand" && results.ElementAt(1).DisplayName == "MyCommand"); + Assert.True(results.ElementAt(2).Name == "Foo" && results.ElementAt(2).DisplayName == "Foo"); + Assert.True(results.ElementAt(3).Name == "Bar" && results.ElementAt(3).DisplayName == "Bar"); + Assert.True(results.ElementAt(4).Name == "Foo & Bar" && results.ElementAt(4).DisplayName == "Foo && Bar"); + } - [Fact] - public async Task DebugProfileEnumValuesGenerator_TryCreateEnumValueAsyncTests() + [Fact] + public async Task DebugProfileEnumValuesGenerator_TryCreateEnumValueAsyncTests() + { + var testProfiles = new Mock(); + testProfiles.Setup(m => m.ActiveProfile).Returns(() => { return _profiles[1]; }); + testProfiles.Setup(m => m.Profiles).Returns(() => { - var testProfiles = new Mock(); - testProfiles.Setup(m => m.ActiveProfile).Returns(() => { return _profiles[1]; }); - testProfiles.Setup(m => m.Profiles).Returns(() => - { - return _profiles.ToImmutableList(); - }); + return _profiles.ToImmutableList(); + }); - var moqProfileProvider = new Mock(); - moqProfileProvider.Setup(p => p.CurrentSnapshot).Returns(testProfiles.Object); - var threadingService = IProjectThreadingServiceFactory.Create(); + var moqProfileProvider = new Mock(); + moqProfileProvider.Setup(p => p.CurrentSnapshot).Returns(testProfiles.Object); + var threadingService = IProjectThreadingServiceFactory.Create(); - var generator = - new DebugProfileEnumValuesGenerator(moqProfileProvider.Object, threadingService); + var generator = + new DebugProfileEnumValuesGenerator(moqProfileProvider.Object, threadingService); - Assert.False(generator.AllowCustomValues); - IEnumValue? result = await generator.TryCreateEnumValueAsync("Profile1"); - Assert.True(result!.Name == "Profile1" && result.DisplayName == "Profile1"); - result = await generator.TryCreateEnumValueAsync("MyCommand"); - Assert.True(result!.Name == "MyCommand" && result.DisplayName == "MyCommand"); + Assert.False(generator.AllowCustomValues); + IEnumValue? result = await generator.TryCreateEnumValueAsync("Profile1"); + Assert.True(result!.Name == "Profile1" && result.DisplayName == "Profile1"); + result = await generator.TryCreateEnumValueAsync("MyCommand"); + Assert.True(result!.Name == "MyCommand" && result.DisplayName == "MyCommand"); - // case sensitive check - result = await generator.TryCreateEnumValueAsync("mycommand"); - Assert.Null(result); + // case sensitive check + result = await generator.TryCreateEnumValueAsync("mycommand"); + Assert.Null(result); - result = await generator.TryCreateEnumValueAsync("Foo"); - Assert.True(result!.Name == "Foo" && result.DisplayName == "Foo"); - result = await generator.TryCreateEnumValueAsync("Bar"); - Assert.True(result!.Name == "Bar" && result.DisplayName == "Bar"); - } + result = await generator.TryCreateEnumValueAsync("Foo"); + Assert.True(result!.Name == "Foo" && result.DisplayName == "Foo"); + result = await generator.TryCreateEnumValueAsync("Bar"); + Assert.True(result!.Name == "Bar" && result.DisplayName == "Bar"); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProviderTests.cs index 919ba3c7e6..f481855488 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugLaunchProviderTests.cs @@ -2,112 +2,111 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +public class LaunchProfilesDebugLaunchProviderTests { - public class LaunchProfilesDebugLaunchProviderTests + private readonly Mock _mockWebProvider = new(); + private readonly Mock _mockDockerProvider = new(); + private readonly Mock _mockExeProvider = new(); + private readonly OrderPrecedenceImportCollection _launchProviders = + new(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst); + + private readonly Mock _configuredProjectMoq = new(); + private readonly Mock _launchSettingsProviderMoq = new(); + private readonly List _webProviderSettings = new(); + private readonly List _dockerProviderSettings = new(); + private readonly List _exeProviderSettings = new(); + + // Set this to have ILaunchSettingsProvider return this profile (null by default) + private ILaunchProfile? _activeProfile; + + public LaunchProfilesDebugLaunchProviderTests() + { + _mockWebProvider.Setup(x => x.SupportsProfile(It.IsAny())).Returns((p) => p.CommandName == "IISExpress"); + _mockWebProvider.Setup(x => x.QueryDebugTargetsAsync(It.IsAny(), It.IsAny())).Returns((o, p) => { return Task.FromResult((IReadOnlyList)_webProviderSettings); }); + _mockDockerProvider.Setup(x => x.SupportsProfile(It.IsAny())).Returns((p) => p.CommandName == "Docker"); + _mockDockerProvider.Setup(x => x.QueryDebugTargetsAsync(It.IsAny(), It.IsAny())).Returns((o, p) => { return Task.FromResult((IReadOnlyList)_dockerProviderSettings); }); + _mockExeProvider.Setup(x => x.SupportsProfile(It.IsAny())).Returns((p) => string.IsNullOrEmpty(p.CommandName) || p.CommandName == "Project"); + _mockExeProvider.Setup(x => x.QueryDebugTargetsAsync(It.IsAny(), It.IsAny())).Returns((o, p) => { return Task.FromResult((IReadOnlyList)_exeProviderSettings); }); + + var mockMetadata = new Mock(); + _launchProviders.Add(new Lazy(() => _mockWebProvider.Object, mockMetadata.Object)); + _launchProviders.Add(new Lazy(() => _mockDockerProvider.Object, mockMetadata.Object)); + _launchProviders.Add(new Lazy(() => _mockExeProvider.Object, mockMetadata.Object)); + + _launchSettingsProviderMoq.Setup(x => x.ActiveProfile).Returns(() => _activeProfile); + _launchSettingsProviderMoq.Setup(x => x.WaitForFirstSnapshot(It.IsAny())).Returns(() => + _activeProfile is not null + ? Task.FromResult((ILaunchSettings?)new LaunchSettings(new List { _activeProfile }, null, _activeProfile.Name)) + : Task.FromResult((ILaunchSettings?)LaunchSettings.Empty)); + } + + [Fact] + public async Task CanLaunchAsyncTests() + { + var provider = CreateInstance(); + + bool result = await provider.CanLaunchAsync(DebugLaunchOptions.NoDebug); + Assert.True(result); + result = await provider.CanLaunchAsync(0); + Assert.True(result); + } + + [Fact] + public void GetLaunchTargetsProviderForProfileTestsAsync() + { + var provider = CreateInstance(); + Assert.Equal(_mockWebProvider.Object, provider.GetLaunchTargetsProvider(new LaunchProfile("test", "IISExpress"))); + Assert.Equal(_mockDockerProvider.Object, provider.GetLaunchTargetsProvider(new LaunchProfile("test", "Docker"))); + Assert.Equal(_mockExeProvider.Object, provider.GetLaunchTargetsProvider(new LaunchProfile("test", "Project"))); + Assert.Null(provider.GetLaunchTargetsProvider(new LaunchProfile("test", "IIS"))); + } + + [Fact] + public async Task QueryDebugTargetsAsyncCorrectProvider() { - private readonly Mock _mockWebProvider = new(); - private readonly Mock _mockDockerProvider = new(); - private readonly Mock _mockExeProvider = new(); - private readonly OrderPrecedenceImportCollection _launchProviders = - new(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst); - - private readonly Mock _configuredProjectMoq = new(); - private readonly Mock _launchSettingsProviderMoq = new(); - private readonly List _webProviderSettings = new(); - private readonly List _dockerProviderSettings = new(); - private readonly List _exeProviderSettings = new(); - - // Set this to have ILaunchSettingsProvider return this profile (null by default) - private ILaunchProfile? _activeProfile; - - public LaunchProfilesDebugLaunchProviderTests() - { - _mockWebProvider.Setup(x => x.SupportsProfile(It.IsAny())).Returns((p) => p.CommandName == "IISExpress"); - _mockWebProvider.Setup(x => x.QueryDebugTargetsAsync(It.IsAny(), It.IsAny())).Returns((o, p) => { return Task.FromResult((IReadOnlyList)_webProviderSettings); }); - _mockDockerProvider.Setup(x => x.SupportsProfile(It.IsAny())).Returns((p) => p.CommandName == "Docker"); - _mockDockerProvider.Setup(x => x.QueryDebugTargetsAsync(It.IsAny(), It.IsAny())).Returns((o, p) => { return Task.FromResult((IReadOnlyList)_dockerProviderSettings); }); - _mockExeProvider.Setup(x => x.SupportsProfile(It.IsAny())).Returns((p) => string.IsNullOrEmpty(p.CommandName) || p.CommandName == "Project"); - _mockExeProvider.Setup(x => x.QueryDebugTargetsAsync(It.IsAny(), It.IsAny())).Returns((o, p) => { return Task.FromResult((IReadOnlyList)_exeProviderSettings); }); - - var mockMetadata = new Mock(); - _launchProviders.Add(new Lazy(() => _mockWebProvider.Object, mockMetadata.Object)); - _launchProviders.Add(new Lazy(() => _mockDockerProvider.Object, mockMetadata.Object)); - _launchProviders.Add(new Lazy(() => _mockExeProvider.Object, mockMetadata.Object)); - - _launchSettingsProviderMoq.Setup(x => x.ActiveProfile).Returns(() => _activeProfile); - _launchSettingsProviderMoq.Setup(x => x.WaitForFirstSnapshot(It.IsAny())).Returns(() => - _activeProfile is not null - ? Task.FromResult((ILaunchSettings?)new LaunchSettings(new List { _activeProfile }, null, _activeProfile.Name)) - : Task.FromResult((ILaunchSettings?)LaunchSettings.Empty)); - } - - [Fact] - public async Task CanLaunchAsyncTests() - { - var provider = CreateInstance(); - - bool result = await provider.CanLaunchAsync(DebugLaunchOptions.NoDebug); - Assert.True(result); - result = await provider.CanLaunchAsync(0); - Assert.True(result); - } - - [Fact] - public void GetLaunchTargetsProviderForProfileTestsAsync() - { - var provider = CreateInstance(); - Assert.Equal(_mockWebProvider.Object, provider.GetLaunchTargetsProvider(new LaunchProfile("test", "IISExpress"))); - Assert.Equal(_mockDockerProvider.Object, provider.GetLaunchTargetsProvider(new LaunchProfile("test", "Docker"))); - Assert.Equal(_mockExeProvider.Object, provider.GetLaunchTargetsProvider(new LaunchProfile("test", "Project"))); - Assert.Null(provider.GetLaunchTargetsProvider(new LaunchProfile("test", "IIS"))); - } - - [Fact] - public async Task QueryDebugTargetsAsyncCorrectProvider() - { - var provider = CreateInstance(); - - _activeProfile = new LaunchProfile("test", "IISExpress"); - var result = await provider.QueryDebugTargetsAsync(0); - Assert.Equal(_webProviderSettings, result); - - _activeProfile = new LaunchProfile("test", "Docker"); - result = await provider.QueryDebugTargetsAsync(0); - Assert.Equal(_dockerProviderSettings, result); - - _activeProfile = new LaunchProfile("test", "Project"); - result = await provider.QueryDebugTargetsAsync(0); - Assert.Equal(_exeProviderSettings, result); - } - - [Fact] - public async Task QueryDebugTargetsAsync_WhenNoLaunchProfile_Throws() - { - var provider = CreateInstance(); - _activeProfile = null; - - await Assert.ThrowsAsync(() => provider.QueryDebugTargetsAsync(0)); - } - - [Fact] - public async Task QueryDebugTargetsAsync_WhenNoInstalledProvider_Throws() - { - var provider = CreateInstance(); - _activeProfile = new LaunchProfile("NoActionProfile", "SomeOtherExtension"); - - await Assert.ThrowsAsync(() => provider.QueryDebugTargetsAsync(0)); - } - - private LaunchProfilesDebugLaunchProvider CreateInstance() - { - var provider = new LaunchProfilesDebugLaunchProvider(_configuredProjectMoq.Object, _launchSettingsProviderMoq.Object, vsDebuggerService: null!); - - provider.LaunchTargetsProviders.Add(_mockWebProvider.Object); - provider.LaunchTargetsProviders.Add(_mockDockerProvider.Object); - provider.LaunchTargetsProviders.Add(_mockExeProvider.Object); - - return provider; - } + var provider = CreateInstance(); + + _activeProfile = new LaunchProfile("test", "IISExpress"); + var result = await provider.QueryDebugTargetsAsync(0); + Assert.Equal(_webProviderSettings, result); + + _activeProfile = new LaunchProfile("test", "Docker"); + result = await provider.QueryDebugTargetsAsync(0); + Assert.Equal(_dockerProviderSettings, result); + + _activeProfile = new LaunchProfile("test", "Project"); + result = await provider.QueryDebugTargetsAsync(0); + Assert.Equal(_exeProviderSettings, result); + } + + [Fact] + public async Task QueryDebugTargetsAsync_WhenNoLaunchProfile_Throws() + { + var provider = CreateInstance(); + _activeProfile = null; + + await Assert.ThrowsAsync(() => provider.QueryDebugTargetsAsync(0)); + } + + [Fact] + public async Task QueryDebugTargetsAsync_WhenNoInstalledProvider_Throws() + { + var provider = CreateInstance(); + _activeProfile = new LaunchProfile("NoActionProfile", "SomeOtherExtension"); + + await Assert.ThrowsAsync(() => provider.QueryDebugTargetsAsync(0)); + } + + private LaunchProfilesDebugLaunchProvider CreateInstance() + { + var provider = new LaunchProfilesDebugLaunchProvider(_configuredProjectMoq.Object, _launchSettingsProviderMoq.Object, vsDebuggerService: null!); + + provider.LaunchTargetsProviders.Add(_mockWebProvider.Object); + provider.LaunchTargetsProviders.Add(_mockDockerProvider.Object); + provider.LaunchTargetsProviders.Add(_mockExeProvider.Object); + + return provider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProviderTests.cs index 3acf7a00f8..879df6ac86 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/LaunchProfilesDebugPageGuidProviderTests.cs @@ -1,14 +1,13 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +public class LaunchProfilesDebugPageGuidProviderTests { - public class LaunchProfilesDebugPageGuidProviderTests + [Fact] + public async Task LaunchProfilesDebugPageGuidProvider_CheckGuid() { - [Fact] - public async Task LaunchProfilesDebugPageGuidProvider_CheckGuid() - { - var guid = new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"); - Assert.True(await new LaunchProfilesDebugPageGuidProvider().GetDebugPropertyPageGuidAsync() == guid); - } + var guid = new Guid("{0273C280-1882-4ED0-9308-52914672E3AA}"); + Assert.True(await new LaunchProfilesDebugPageGuidProvider().GetDebugPropertyPageGuidAsync() == guid); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/ProjectLaunchTargetsProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/ProjectLaunchTargetsProviderTests.cs index 3b65492680..a49e6ef3b8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/ProjectLaunchTargetsProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Debug/ProjectLaunchTargetsProviderTests.cs @@ -7,780 +7,779 @@ using Microsoft.VisualStudio.ProjectSystem.VS.HotReload; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +public class ProjectLaunchTargetsProviderTests { - public class ProjectLaunchTargetsProviderTests + private readonly string _ProjectFile = @"c:\test\project\project.csproj"; + private readonly string _Path = @"c:\program files\dotnet;c:\program files\SomeDirectory"; + private readonly IFileSystemMock _mockFS = new(); + + [Fact] + public void GetExeAndArguments() { - private readonly string _ProjectFile = @"c:\test\project\project.csproj"; - private readonly string _Path = @"c:\program files\dotnet;c:\program files\SomeDirectory"; - private readonly IFileSystemMock _mockFS = new(); + string exeIn = @"c:\foo\bar.exe"; + string argsIn = "/foo /bar"; + string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); - [Fact] - public void GetExeAndArguments() - { - string exeIn = @"c:\foo\bar.exe"; - string argsIn = "/foo /bar"; - string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); + ProjectLaunchTargetsProvider.GetExeAndArguments(false, exeIn, argsIn, out string? finalExePath, out string? finalArguments); + Assert.Equal(finalExePath, exeIn); + Assert.Equal(finalArguments, argsIn); - ProjectLaunchTargetsProvider.GetExeAndArguments(false, exeIn, argsIn, out string? finalExePath, out string? finalArguments); - Assert.Equal(finalExePath, exeIn); - Assert.Equal(finalArguments, argsIn); + ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, argsIn, out finalExePath, out finalArguments); + Assert.Equal(cmdExePath, finalExePath); + Assert.Equal("/c \"\"c:\\foo\\bar.exe\" /foo /bar & pause\"", finalArguments); + } - ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, argsIn, out finalExePath, out finalArguments); - Assert.Equal(cmdExePath, finalExePath); - Assert.Equal("/c \"\"c:\\foo\\bar.exe\" /foo /bar & pause\"", finalArguments); - } + [Fact] + public void GetExeAndArgumentsWithEscapedArgs() + { + string exeIn = @"c:\foo\bar.exe"; + string argsInWithEscapes = "/foo /bar ^ < > &"; + string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); - [Fact] - public void GetExeAndArgumentsWithEscapedArgs() - { - string exeIn = @"c:\foo\bar.exe"; - string argsInWithEscapes = "/foo /bar ^ < > &"; - string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); + ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, argsInWithEscapes, out string? finalExePath, out string? finalArguments); + Assert.Equal(cmdExePath, finalExePath); + Assert.Equal("/c \"\"c:\\foo\\bar.exe\" /foo /bar ^^ ^< ^> ^& & pause\"", finalArguments); - ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, argsInWithEscapes, out string? finalExePath, out string? finalArguments); - Assert.Equal(cmdExePath, finalExePath); - Assert.Equal("/c \"\"c:\\foo\\bar.exe\" /foo /bar ^^ ^< ^> ^& & pause\"", finalArguments); + ProjectLaunchTargetsProvider.GetExeAndArguments(false, exeIn, argsInWithEscapes, out finalExePath, out finalArguments); + Assert.Equal(exeIn, finalExePath); + Assert.Equal(argsInWithEscapes, finalArguments); + } - ProjectLaunchTargetsProvider.GetExeAndArguments(false, exeIn, argsInWithEscapes, out finalExePath, out finalArguments); - Assert.Equal(exeIn, finalExePath); - Assert.Equal(argsInWithEscapes, finalArguments); - } + [Fact] + public void GetExeAndArgumentsWithNullArgs() + { + string exeIn = @"c:\foo\bar.exe"; + string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); - [Fact] - public void GetExeAndArgumentsWithNullArgs() - { - string exeIn = @"c:\foo\bar.exe"; - string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); + ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, "", out string? finalExePath, out string? finalArguments); + Assert.Equal(cmdExePath, finalExePath); + Assert.Equal("/c \"\"c:\\foo\\bar.exe\" & pause\"", finalArguments); + } - ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, "", out string? finalExePath, out string? finalArguments); - Assert.Equal(cmdExePath, finalExePath); - Assert.Equal("/c \"\"c:\\foo\\bar.exe\" & pause\"", finalArguments); - } + [Fact] + public void GetExeAndArgumentsWithEmptyArgs() + { + string exeIn = @"c:\foo\bar.exe"; + string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); - [Fact] - public void GetExeAndArgumentsWithEmptyArgs() - { - string exeIn = @"c:\foo\bar.exe"; - string cmdExePath = Path.Combine(Environment.SystemDirectory, "cmd.exe"); + // empty string args + ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, "", out string? finalExePath, out string? finalArguments); + Assert.Equal(cmdExePath, finalExePath); + Assert.Equal("/c \"\"c:\\foo\\bar.exe\" & pause\"", finalArguments); + } - // empty string args - ProjectLaunchTargetsProvider.GetExeAndArguments(true, exeIn, "", out string? finalExePath, out string? finalArguments); - Assert.Equal(cmdExePath, finalExePath); - Assert.Equal("/c \"\"c:\\foo\\bar.exe\" & pause\"", finalArguments); - } + [Fact] + public async Task QueryDebugTargetsAsync_ProjectProfileAsyncF5() + { + var debugger = GetDebugTargetsProvider(); + + await _mockFS.WriteAllTextAsync(@"c:\program files\dotnet\dotnet.exe", ""); + _mockFS.CreateDirectory(@"c:\test\project"); + + var activeProfile = new LaunchProfile("MyApplication", "Project", commandLineArgs: "--someArgs"); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"c:\program files\dotnet\dotnet.exe", targets[0].Executable); + Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); + Assert.Equal(0, targets[0].AdditionalDebugEngines.Count); + Assert.Equal("exec \"c:\\test\\project\\bin\\project.dll\" --someArgs", targets[0].Arguments); + } - [Fact] - public async Task QueryDebugTargetsAsync_ProjectProfileAsyncF5() - { - var debugger = GetDebugTargetsProvider(); - - await _mockFS.WriteAllTextAsync(@"c:\program files\dotnet\dotnet.exe", ""); - _mockFS.CreateDirectory(@"c:\test\project"); - - var activeProfile = new LaunchProfile("MyApplication", "Project", commandLineArgs: "--someArgs"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"c:\program files\dotnet\dotnet.exe", targets[0].Executable); - Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); - Assert.Equal(0, targets[0].AdditionalDebugEngines.Count); - Assert.Equal("exec \"c:\\test\\project\\bin\\project.dll\" --someArgs", targets[0].Arguments); - } + [Fact] + public async Task QueryDebugTargetsAsync_ProjectProfileAsyncF5_NativeDebugging() + { + var debugger = GetDebugTargetsProvider(); + + await _mockFS.WriteAllTextAsync(@"c:\program files\dotnet\dotnet.exe", ""); + _mockFS.CreateDirectory(@"c:\test\project"); + + var activeProfile = new LaunchProfile( + name: "MyApplication", + commandName: "Project", + commandLineArgs: "--someArgs", + otherSettings: ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.NativeDebuggingProperty, (object)true))); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"c:\program files\dotnet\dotnet.exe", targets[0].Executable); + Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); + Assert.Single(targets[0].AdditionalDebugEngines); + Assert.Equal(DebuggerEngines.NativeOnlyEngine, targets[0].AdditionalDebugEngines[0]); + Assert.Equal("exec \"c:\\test\\project\\bin\\project.dll\" --someArgs", targets[0].Arguments); + } - [Fact] - public async Task QueryDebugTargetsAsync_ProjectProfileAsyncF5_NativeDebugging() - { - var debugger = GetDebugTargetsProvider(); - - await _mockFS.WriteAllTextAsync(@"c:\program files\dotnet\dotnet.exe", ""); - _mockFS.CreateDirectory(@"c:\test\project"); - - var activeProfile = new LaunchProfile( - name: "MyApplication", - commandName: "Project", - commandLineArgs: "--someArgs", - otherSettings: ImmutableArray.Create<(string, object)>((LaunchProfileExtensions.NativeDebuggingProperty, (object)true))); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"c:\program files\dotnet\dotnet.exe", targets[0].Executable); - Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); - Assert.Single(targets[0].AdditionalDebugEngines); - Assert.Equal(DebuggerEngines.NativeOnlyEngine, targets[0].AdditionalDebugEngines[0]); - Assert.Equal("exec \"c:\\test\\project\\bin\\project.dll\" --someArgs", targets[0].Arguments); - } + [Fact] + public async Task QueryDebugTargetsAsync_ProjectProfileAsyncCtrlF5() + { + var debugger = GetDebugTargetsProvider(); + + var activeProfile = new LaunchProfile( + name: "MyApplication", + commandName: "Project", + commandLineArgs: "--someArgs", + environmentVariables: ImmutableArray.Create(("var1", "Value1"))); + + var targets = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); + Assert.Single(targets); + Assert.EndsWith(@"\cmd.exe", targets[0].Executable, StringComparison.OrdinalIgnoreCase); + Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); + Assert.Equal(DebugLaunchOptions.NoDebug | DebugLaunchOptions.MergeEnvironment, targets[0].LaunchOptions); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); + Assert.True(targets[0].Environment.ContainsKey("var1")); + Assert.Equal("/c \"\"c:\\program files\\dotnet\\dotnet.exe\" exec \"c:\\test\\project\\bin\\project.dll\" --someArgs & pause\"", targets[0].Arguments); + } - [Fact] - public async Task QueryDebugTargetsAsync_ProjectProfileAsyncCtrlF5() - { - var debugger = GetDebugTargetsProvider(); - - var activeProfile = new LaunchProfile( - name: "MyApplication", - commandName: "Project", - commandLineArgs: "--someArgs", - environmentVariables: ImmutableArray.Create(("var1", "Value1"))); - - var targets = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); - Assert.Single(targets); - Assert.EndsWith(@"\cmd.exe", targets[0].Executable, StringComparison.OrdinalIgnoreCase); - Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); - Assert.Equal(DebugLaunchOptions.NoDebug | DebugLaunchOptions.MergeEnvironment, targets[0].LaunchOptions); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); - Assert.True(targets[0].Environment.ContainsKey("var1")); - Assert.Equal("/c \"\"c:\\program files\\dotnet\\dotnet.exe\" exec \"c:\\test\\project\\bin\\project.dll\" --someArgs & pause\"", targets[0].Arguments); - } + [Fact] + public async Task QueryDebugTargetsAsync_ProjectProfileAsyncProfile() + { + var debugger = GetDebugTargetsProvider(); - [Fact] - public async Task QueryDebugTargetsAsync_ProjectProfileAsyncProfile() - { - var debugger = GetDebugTargetsProvider(); + var activeProfile = new LaunchProfile("MyApplication", "Project", commandLineArgs: "--someArgs"); - var activeProfile = new LaunchProfile("MyApplication", "Project", commandLineArgs: "--someArgs"); + // Validate that when the DLO_Profiling is set we don't run the cmd.exe + var targets = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug | DebugLaunchOptions.Profiling, activeProfile); + Assert.Single(targets); + Assert.Equal("c:\\program files\\dotnet\\dotnet.exe", targets[0].Executable); + Assert.Equal(DebugLaunchOptions.NoDebug | DebugLaunchOptions.Profiling, targets[0].LaunchOptions); + } - // Validate that when the DLO_Profiling is set we don't run the cmd.exe - var targets = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug | DebugLaunchOptions.Profiling, activeProfile); - Assert.Single(targets); - Assert.Equal("c:\\program files\\dotnet\\dotnet.exe", targets[0].Executable); - Assert.Equal(DebugLaunchOptions.NoDebug | DebugLaunchOptions.Profiling, targets[0].LaunchOptions); - } + [Fact] + public async Task QueryDebugTargetsAsync_ExeProfileAsyncF5() + { + var debugger = GetDebugTargetsProvider(); + + var activeProfile = new LaunchProfile( + "MyApplication", + commandName: null, + commandLineArgs: "--someArgs", + executablePath: @"c:\test\Project\someapp.exe"); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(activeProfile.ExecutablePath, targets[0].Executable); + Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); + Assert.Equal("--someArgs", targets[0].Arguments); + } - [Fact] - public async Task QueryDebugTargetsAsync_ExeProfileAsyncF5() - { - var debugger = GetDebugTargetsProvider(); - - var activeProfile = new LaunchProfile( - "MyApplication", - commandName: null, - commandLineArgs: "--someArgs", - executablePath: @"c:\test\Project\someapp.exe"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(activeProfile.ExecutablePath, targets[0].Executable); - Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); - Assert.Equal("--someArgs", targets[0].Arguments); - } + [Fact] + public async Task QueryDebugTargetsAsync_ExeProfileAsyncCtrlF5() + { + var debugger = GetDebugTargetsProvider(); + + var activeProfile = new LaunchProfile( + name: "MyApplication", + commandName: null, + commandLineArgs: "--someArgs", + executablePath: @"c:\test\Project\someapp.exe", + environmentVariables: ImmutableArray.Create(("var1", "Value1"))); + var targets = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); + Assert.Single(targets); + Assert.Equal(activeProfile.ExecutablePath, targets[0].Executable); + Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); + Assert.Equal(DebugLaunchOptions.NoDebug | DebugLaunchOptions.MergeEnvironment, targets[0].LaunchOptions); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); + Assert.Equal("--someArgs", targets[0].Arguments); + } - [Fact] - public async Task QueryDebugTargetsAsync_ExeProfileAsyncCtrlF5() + [Theory] + [InlineData(@"c:\test\project\bin\")] + [InlineData(@"bin\")] + [InlineData(@"doesntExist\")] + [InlineData(null)] + public async Task QueryDebugTargetsAsync_ExeProfileAsyncExeRelativeNoWorkingDir(string outdir) + { + var properties = new Dictionary { - var debugger = GetDebugTargetsProvider(); - - var activeProfile = new LaunchProfile( - name: "MyApplication", - commandName: null, - commandLineArgs: "--someArgs", - executablePath: @"c:\test\Project\someapp.exe", - environmentVariables: ImmutableArray.Create(("var1", "Value1"))); - var targets = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); - Assert.Single(targets); - Assert.Equal(activeProfile.ExecutablePath, targets[0].Executable); - Assert.Equal(DebugLaunchOperation.CreateProcess, targets[0].LaunchOperation); - Assert.Equal(DebugLaunchOptions.NoDebug | DebugLaunchOptions.MergeEnvironment, targets[0].LaunchOptions); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, targets[0].LaunchDebugEngineGuid); - Assert.Equal("--someArgs", targets[0].Arguments); - } + { "RunCommand", @"dotnet" }, + { "RunArguments", "exec " + "\"" + @"c:\test\project\bin\project.dll"+ "\"" }, + { "RunWorkingDirectory", @"bin\" }, + { "TargetFrameworkIdentifier", @".NetCoreApp" }, + { "OutDir", outdir } + }; - [Theory] - [InlineData(@"c:\test\project\bin\")] - [InlineData(@"bin\")] - [InlineData(@"doesntExist\")] - [InlineData(null)] - public async Task QueryDebugTargetsAsync_ExeProfileAsyncExeRelativeNoWorkingDir(string outdir) - { - var properties = new Dictionary - { - { "RunCommand", @"dotnet" }, - { "RunArguments", "exec " + "\"" + @"c:\test\project\bin\project.dll"+ "\"" }, - { "RunWorkingDirectory", @"bin\" }, - { "TargetFrameworkIdentifier", @".NetCoreApp" }, - { "OutDir", outdir } - }; - - var debugger = GetDebugTargetsProvider(ProjectOutputType.Console, properties); - - // Exe relative, no working dir - await _mockFS.WriteAllTextAsync(@"c:\test\project\bin\test.exe", string.Empty); - await _mockFS.WriteAllTextAsync(@"c:\test\project\test.exe", string.Empty); - var activeProfile = new LaunchProfile("run", null, executablePath: ".\\test.exe"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - if (outdir is null || outdir == @"doesntExist\") - { - Assert.Equal(@"c:\test\project\test.exe", targets[0].Executable); - Assert.Equal(@"c:\test\project\", targets[0].CurrentDirectory); - } - else - { - Assert.Equal(@"c:\test\project\bin\test.exe", targets[0].Executable); - Assert.Equal(@"c:\test\project\bin\", targets[0].CurrentDirectory); - } - } + var debugger = GetDebugTargetsProvider(ProjectOutputType.Console, properties); - [Theory] - [InlineData(@"c:\WorkingDir")] - [InlineData(@"\WorkingDir")] - public async Task QueryDebugTargetsAsync_ExeProfileAsyncExeRelativeToWorkingDir(string workingDir) + // Exe relative, no working dir + await _mockFS.WriteAllTextAsync(@"c:\test\project\bin\test.exe", string.Empty); + await _mockFS.WriteAllTextAsync(@"c:\test\project\test.exe", string.Empty); + var activeProfile = new LaunchProfile("run", null, executablePath: ".\\test.exe"); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + if (outdir is null || outdir == @"doesntExist\") { - var debugger = GetDebugTargetsProvider(); - - // Exe relative to full working dir - await _mockFS.WriteAllTextAsync(@"c:\WorkingDir\mytest.exe", string.Empty); - _mockFS.SetCurrentDirectory(@"c:\Test"); - _mockFS.CreateDirectory(@"c:\WorkingDir"); - var activeProfile = new LaunchProfile("run", null, executablePath: ".\\mytest.exe", workingDirectory: workingDir); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"c:\WorkingDir\mytest.exe", targets[0].Executable); - Assert.Equal(@"c:\WorkingDir", targets[0].CurrentDirectory); + Assert.Equal(@"c:\test\project\test.exe", targets[0].Executable); + Assert.Equal(@"c:\test\project\", targets[0].CurrentDirectory); } - - [Fact] - public async Task QueryDebugTargetsAsync_ExeProfileAsyncExeRelativeToWorkingDir_AlternateSlash() + else { - var debugger = GetDebugTargetsProvider(); - - // Exe relative to full working dir - await _mockFS.WriteAllTextAsync(@"c:\WorkingDir\mytest.exe", string.Empty); - _mockFS.CreateDirectory(@"c:\WorkingDir"); - var activeProfile = new LaunchProfile("run", null, executablePath: "./mytest.exe", workingDirectory: @"c:/WorkingDir"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"c:\WorkingDir\mytest.exe", targets[0].Executable); - Assert.Equal(@"c:\WorkingDir", targets[0].CurrentDirectory); + Assert.Equal(@"c:\test\project\bin\test.exe", targets[0].Executable); + Assert.Equal(@"c:\test\project\bin\", targets[0].CurrentDirectory); } + } - [Fact] - public async Task QueryDebugTargetsAsync_SetsProject() - { - var project = new LaunchProfile("run", null, executablePath: "dotnet.exe"); + [Theory] + [InlineData(@"c:\WorkingDir")] + [InlineData(@"\WorkingDir")] + public async Task QueryDebugTargetsAsync_ExeProfileAsyncExeRelativeToWorkingDir(string workingDir) + { + var debugger = GetDebugTargetsProvider(); + + // Exe relative to full working dir + await _mockFS.WriteAllTextAsync(@"c:\WorkingDir\mytest.exe", string.Empty); + _mockFS.SetCurrentDirectory(@"c:\Test"); + _mockFS.CreateDirectory(@"c:\WorkingDir"); + var activeProfile = new LaunchProfile("run", null, executablePath: ".\\mytest.exe", workingDirectory: workingDir); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"c:\WorkingDir\mytest.exe", targets[0].Executable); + Assert.Equal(@"c:\WorkingDir", targets[0].CurrentDirectory); + } - var debugger = GetDebugTargetsProvider(); - var targets = await debugger.QueryDebugTargetsAsync(0, project); + [Fact] + public async Task QueryDebugTargetsAsync_ExeProfileAsyncExeRelativeToWorkingDir_AlternateSlash() + { + var debugger = GetDebugTargetsProvider(); + + // Exe relative to full working dir + await _mockFS.WriteAllTextAsync(@"c:\WorkingDir\mytest.exe", string.Empty); + _mockFS.CreateDirectory(@"c:\WorkingDir"); + var activeProfile = new LaunchProfile("run", null, executablePath: "./mytest.exe", workingDirectory: @"c:/WorkingDir"); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"c:\WorkingDir\mytest.exe", targets[0].Executable); + Assert.Equal(@"c:\WorkingDir", targets[0].CurrentDirectory); + } - Assert.Single(targets); - Assert.NotNull(targets[0].Project); - } + [Fact] + public async Task QueryDebugTargetsAsync_SetsProject() + { + var project = new LaunchProfile("run", null, executablePath: "dotnet.exe"); - [Theory] - [InlineData("dotnet")] - [InlineData("dotnet.exe")] - public async Task QueryDebugTargetsAsync_ExeProfileExeRelativeToPath(string exeName) - { - var debugger = GetDebugTargetsProvider(); + var debugger = GetDebugTargetsProvider(); + var targets = await debugger.QueryDebugTargetsAsync(0, project); - // Exe relative to path - var activeProfile = new LaunchProfile("run", null, executablePath: exeName); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"c:\program files\dotnet\dotnet.exe", targets[0].Executable); - } + Assert.Single(targets); + Assert.NotNull(targets[0].Project); + } - [Theory] - [InlineData("myexe")] - [InlineData("myexe.exe")] - public async Task QueryDebugTargetsAsync_ExeProfileExeRelativeToCurrentDirectory(string exeName) - { - var debugger = GetDebugTargetsProvider(); - await _mockFS.WriteAllTextAsync(@"c:\CurrentDirectory\myexe.exe", string.Empty); - _mockFS.SetCurrentDirectory(@"c:\CurrentDirectory"); - - // Exe relative to path - var activeProfile = new LaunchProfile("run", null, executablePath: exeName); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"c:\CurrentDirectory\myexe.exe", targets[0].Executable); - } + [Theory] + [InlineData("dotnet")] + [InlineData("dotnet.exe")] + public async Task QueryDebugTargetsAsync_ExeProfileExeRelativeToPath(string exeName) + { + var debugger = GetDebugTargetsProvider(); - [Fact] - public async Task QueryDebugTargetsAsync_ExeProfileExeIsRootedWithNoDrive() - { - var debugger = GetDebugTargetsProvider(); - await _mockFS.WriteAllTextAsync(@"e:\myexe.exe", string.Empty); - _mockFS.SetCurrentDirectory(@"e:\CurrentDirectory"); - - // Exe relative to path - var activeProfile = new LaunchProfile("run", null, executablePath: @"\myexe.exe"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"e:\myexe.exe", targets[0].Executable); - } + // Exe relative to path + var activeProfile = new LaunchProfile("run", null, executablePath: exeName); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"c:\program files\dotnet\dotnet.exe", targets[0].Executable); + } + + [Theory] + [InlineData("myexe")] + [InlineData("myexe.exe")] + public async Task QueryDebugTargetsAsync_ExeProfileExeRelativeToCurrentDirectory(string exeName) + { + var debugger = GetDebugTargetsProvider(); + await _mockFS.WriteAllTextAsync(@"c:\CurrentDirectory\myexe.exe", string.Empty); + _mockFS.SetCurrentDirectory(@"c:\CurrentDirectory"); + + // Exe relative to path + var activeProfile = new LaunchProfile("run", null, executablePath: exeName); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"c:\CurrentDirectory\myexe.exe", targets[0].Executable); + } - [Fact] - public async Task QueryDebugTargetsAsync_WhenLibraryWithRunCommand_ReturnsRunCommand() + [Fact] + public async Task QueryDebugTargetsAsync_ExeProfileExeIsRootedWithNoDrive() + { + var debugger = GetDebugTargetsProvider(); + await _mockFS.WriteAllTextAsync(@"e:\myexe.exe", string.Empty); + _mockFS.SetCurrentDirectory(@"e:\CurrentDirectory"); + + // Exe relative to path + var activeProfile = new LaunchProfile("run", null, executablePath: @"\myexe.exe"); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + Assert.Single(targets); + Assert.Equal(@"e:\myexe.exe", targets[0].Executable); + } + + [Fact] + public async Task QueryDebugTargetsAsync_WhenLibraryWithRunCommand_ReturnsRunCommand() + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"RunCommand", @"C:\dotnet.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"RunCommand", @"C:\dotnet.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); + var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"C:\dotnet.exe", targets[0].Executable); - } + Assert.Single(targets); + Assert.Equal(@"C:\dotnet.exe", targets[0].Executable); + } - [Fact] - public async Task QueryDebugTargetsAsync_WhenLibraryWithoutRunCommand_ReturnsTargetPath() + [Fact] + public async Task QueryDebugTargetsAsync_WhenLibraryWithoutRunCommand_ReturnsTargetPath() + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"TargetPath", @"C:\library.dll"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\library.dll"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); + var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"C:\library.dll", targets[0].Executable); - } + Assert.Single(targets); + Assert.Equal(@"C:\library.dll", targets[0].Executable); + } - [Fact] - public async Task QueryDebugTargetsAsync_WhenLibraryWithoutRunCommand_DoesNotManipulateTargetPath() + [Fact] + public async Task QueryDebugTargetsAsync_WhenLibraryWithoutRunCommand_DoesNotManipulateTargetPath() + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"TargetPath", @"library.dll"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"library.dll"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); + var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"library.dll", targets[0].Executable); - } + Assert.Single(targets); + Assert.Equal(@"library.dll", targets[0].Executable); + } - [Fact] - public async Task QueryDebugTargetsForDebugLaunchAsync_WhenLibraryAndNoRunCommandSpecified_Throws() + [Fact] + public async Task QueryDebugTargetsForDebugLaunchAsync_WhenLibraryAndNoRunCommandSpecified_Throws() + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"TargetPath", @"C:\library.dll"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\library.dll"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - await _mockFS.WriteAllTextAsync(@"C:\library.dll", string.Empty); + await _mockFS.WriteAllTextAsync(@"C:\library.dll", string.Empty); - var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); + var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - await Assert.ThrowsAsync(() => debugger.QueryDebugTargetsForDebugLaunchAsync(0, activeProfile)); - } + await Assert.ThrowsAsync(() => debugger.QueryDebugTargetsForDebugLaunchAsync(0, activeProfile)); + } - [Fact] - public async Task QueryDebugTargetsForDebugLaunchAsync_WhenLibraryAndRunCommandSpecified_ReturnsRunCommand() + [Fact] + public async Task QueryDebugTargetsForDebugLaunchAsync_WhenLibraryAndRunCommandSpecified_ReturnsRunCommand() + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"RunCommand", @"C:\dotnet.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"RunCommand", @"C:\dotnet.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - await _mockFS.WriteAllTextAsync(@"C:\library.dll", string.Empty); + await _mockFS.WriteAllTextAsync(@"C:\library.dll", string.Empty); - var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); + var debugger = GetDebugTargetsProvider(ProjectOutputType.Library, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); + var targets = await debugger.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(targets); - Assert.Equal(@"C:\dotnet.exe", targets[0].Executable); - } + Assert.Single(targets); + Assert.Equal(@"C:\dotnet.exe", targets[0].Executable); + } - [Fact] - public async Task QueryDebugTargetsAsync_ConsoleAppLaunchWithNoDebugger_WrapsInCmd() + [Fact] + public async Task QueryDebugTargetsAsync_ConsoleAppLaunchWithNoDebugger_WrapsInCmd() + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"TargetPath", @"C:\ConsoleApp.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\ConsoleApp.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var debugger = GetDebugTargetsProvider(ProjectOutputType.Console, properties); + var debugger = GetDebugTargetsProvider(ProjectOutputType.Console, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var result = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); + var result = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); - Assert.Single(result); - Assert.Contains("cmd.exe", result[0].Executable); - } + Assert.Single(result); + Assert.Contains("cmd.exe", result[0].Executable); + } - [Fact] - public async Task QueryDebugTargetsAsync_ConsoleAppLaunchWithNoDebuggerWithIntegratedConsoleEnabled_DoesNotWrapInCmd() + [Fact] + public async Task QueryDebugTargetsAsync_ConsoleAppLaunchWithNoDebuggerWithIntegratedConsoleEnabled_DoesNotWrapInCmd() + { + var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); + var properties = new Dictionary { - var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); - var properties = new Dictionary - { - {"TargetPath", @"C:\ConsoleApp.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\ConsoleApp.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var scope = IProjectCapabilitiesScopeFactory.Create(capabilities: new string[] { ProjectCapabilities.IntegratedConsoleDebugging }); + var scope = IProjectCapabilitiesScopeFactory.Create(capabilities: new string[] { ProjectCapabilities.IntegratedConsoleDebugging }); - var provider = GetDebugTargetsProvider(ProjectOutputType.Console, properties, debugger, scope); + var provider = GetDebugTargetsProvider(ProjectOutputType.Console, properties, debugger, scope); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var result = await provider.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); + var result = await provider.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); - Assert.Single(result); - Assert.DoesNotContain("cmd.exe", result[0].Executable); - } + Assert.Single(result); + Assert.DoesNotContain("cmd.exe", result[0].Executable); + } - [Theory] - [InlineData((DebugLaunchOptions)0)] - [InlineData(DebugLaunchOptions.NoDebug)] - public async Task QueryDebugTargetsAsync_ConsoleAppLaunch_IncludesIntegratedConsoleInLaunchOptions(DebugLaunchOptions launchOptions) + [Theory] + [InlineData((DebugLaunchOptions)0)] + [InlineData(DebugLaunchOptions.NoDebug)] + public async Task QueryDebugTargetsAsync_ConsoleAppLaunch_IncludesIntegratedConsoleInLaunchOptions(DebugLaunchOptions launchOptions) + { + var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); + var properties = new Dictionary { - var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); - var properties = new Dictionary - { - {"TargetPath", @"C:\ConsoleApp.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\ConsoleApp.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var scope = IProjectCapabilitiesScopeFactory.Create(capabilities: new string[] { ProjectCapabilities.IntegratedConsoleDebugging }); + var scope = IProjectCapabilitiesScopeFactory.Create(capabilities: new string[] { ProjectCapabilities.IntegratedConsoleDebugging }); - var provider = GetDebugTargetsProvider(ProjectOutputType.Console, properties, debugger, scope); + var provider = GetDebugTargetsProvider(ProjectOutputType.Console, properties, debugger, scope); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var result = await provider.QueryDebugTargetsAsync(launchOptions, activeProfile); + var result = await provider.QueryDebugTargetsAsync(launchOptions, activeProfile); - Assert.Single(result); - Assert.True((result[0].LaunchOptions & DebugLaunchOptions.IntegratedConsole) == DebugLaunchOptions.IntegratedConsole); - } + Assert.Single(result); + Assert.True((result[0].LaunchOptions & DebugLaunchOptions.IntegratedConsole) == DebugLaunchOptions.IntegratedConsole); + } - [Theory] - [InlineData((DebugLaunchOptions)0)] - [InlineData(DebugLaunchOptions.NoDebug)] - public async Task QueryDebugTargetsAsync_NonIntegratedConsoleCapability_DoesNotIncludeIntegrationConsoleInLaunchOptions(DebugLaunchOptions launchOptions) + [Theory] + [InlineData((DebugLaunchOptions)0)] + [InlineData(DebugLaunchOptions.NoDebug)] + public async Task QueryDebugTargetsAsync_NonIntegratedConsoleCapability_DoesNotIncludeIntegrationConsoleInLaunchOptions(DebugLaunchOptions launchOptions) + { + var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); + var properties = new Dictionary { - var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); - var properties = new Dictionary - { - {"TargetPath", @"C:\ConsoleApp.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\ConsoleApp.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var provider = GetDebugTargetsProvider(ProjectOutputType.Console, properties, debugger); + var provider = GetDebugTargetsProvider(ProjectOutputType.Console, properties, debugger); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var result = await provider.QueryDebugTargetsAsync(launchOptions, activeProfile); + var result = await provider.QueryDebugTargetsAsync(launchOptions, activeProfile); - Assert.Single(result); - Assert.True((result[0].LaunchOptions & DebugLaunchOptions.IntegratedConsole) != DebugLaunchOptions.IntegratedConsole); - } + Assert.Single(result); + Assert.True((result[0].LaunchOptions & DebugLaunchOptions.IntegratedConsole) != DebugLaunchOptions.IntegratedConsole); + } - [Theory] - [InlineData(ProjectOutputType.Library)] - [InlineData(ProjectOutputType.Other)] - public async Task QueryDebugTargetsAsync_NonConsoleAppLaunch_DoesNotIncludeIntegrationConsoleInLaunchOptions(ProjectOutputType outputType) + [Theory] + [InlineData(ProjectOutputType.Library)] + [InlineData(ProjectOutputType.Other)] + public async Task QueryDebugTargetsAsync_NonConsoleAppLaunch_DoesNotIncludeIntegrationConsoleInLaunchOptions(ProjectOutputType outputType) + { + var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); + var properties = new Dictionary { - var debugger = IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: true); - var properties = new Dictionary - { - {"TargetPath", @"C:\ConsoleApp.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\ConsoleApp.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var provider = GetDebugTargetsProvider(outputType, properties, debugger); + var provider = GetDebugTargetsProvider(outputType, properties, debugger); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var result = await provider.QueryDebugTargetsAsync(0, activeProfile); + var result = await provider.QueryDebugTargetsAsync(0, activeProfile); - Assert.Single(result); - Assert.True((result[0].LaunchOptions & DebugLaunchOptions.IntegratedConsole) != DebugLaunchOptions.IntegratedConsole); - } + Assert.Single(result); + Assert.True((result[0].LaunchOptions & DebugLaunchOptions.IntegratedConsole) != DebugLaunchOptions.IntegratedConsole); + } - [Theory] - [InlineData(ProjectOutputType.Library)] - [InlineData(ProjectOutputType.Other)] - public async Task QueryDebugTargetsAsync_NonConsoleAppLaunchWithNoDebugger_DoesNotWrapInCmd(ProjectOutputType outputType) + [Theory] + [InlineData(ProjectOutputType.Library)] + [InlineData(ProjectOutputType.Other)] + public async Task QueryDebugTargetsAsync_NonConsoleAppLaunchWithNoDebugger_DoesNotWrapInCmd(ProjectOutputType outputType) + { + var properties = new Dictionary { - var properties = new Dictionary - { - {"TargetPath", @"C:\ConsoleApp.exe"}, - {"TargetFrameworkIdentifier", @".NETFramework"} - }; + {"TargetPath", @"C:\ConsoleApp.exe"}, + {"TargetFrameworkIdentifier", @".NETFramework"} + }; - var debugger = GetDebugTargetsProvider(outputType, properties); + var debugger = GetDebugTargetsProvider(outputType, properties); - var activeProfile = new LaunchProfile("Name", "Project"); + var activeProfile = new LaunchProfile("Name", "Project"); - var result = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); + var result = await debugger.QueryDebugTargetsAsync(DebugLaunchOptions.NoDebug, activeProfile); - Assert.Single(result); - Assert.Equal(@"C:\ConsoleApp.exe", result[0].Executable); - } + Assert.Single(result); + Assert.Equal(@"C:\ConsoleApp.exe", result[0].Executable); + } - [Fact] - public void ValidateSettings_WhenNoExe_Throws() - { - string? executable = null; - string? workingDir = null; - var debugger = GetDebugTargetsProvider(); - var profileName = "run"; - - Assert.ThrowsAny(() => - { - debugger.ValidateSettings(executable!, workingDir!, profileName); - }); - } + [Fact] + public void ValidateSettings_WhenNoExe_Throws() + { + string? executable = null; + string? workingDir = null; + var debugger = GetDebugTargetsProvider(); + var profileName = "run"; - [Fact] - public void ValidateSettings_WhenExeNotFoundThrows() + Assert.ThrowsAny(() => { - string executable = @"c:\foo\bar.exe"; - string? workingDir = null; - var debugger = GetDebugTargetsProvider(); - var profileName = "run"; - - Assert.ThrowsAny(() => - { - debugger.ValidateSettings(executable, workingDir!, profileName); - }); - } + debugger.ValidateSettings(executable!, workingDir!, profileName); + }); + } - [Fact] - public void ValidateSettings_WhenExeFound_DoesNotThrow() - { - string executable = @"c:\foo\bar.exe"; - string? workingDir = null; - var debugger = GetDebugTargetsProvider(); - var profileName = "run"; - _mockFS.Create(executable); + [Fact] + public void ValidateSettings_WhenExeNotFoundThrows() + { + string executable = @"c:\foo\bar.exe"; + string? workingDir = null; + var debugger = GetDebugTargetsProvider(); + var profileName = "run"; + Assert.ThrowsAny(() => + { debugger.ValidateSettings(executable, workingDir!, profileName); - Assert.True(true); - } + }); + } - [Fact] - public void ValidateSettings_WhenWorkingDirNotFound_Throws() - { - string executable = "bar.exe"; - string workingDir = @"c:\foo"; - var debugger = GetDebugTargetsProvider(); - var profileName = "run"; - - Assert.ThrowsAny(() => - { - debugger.ValidateSettings(executable, workingDir, profileName); - }); - } + [Fact] + public void ValidateSettings_WhenExeFound_DoesNotThrow() + { + string executable = @"c:\foo\bar.exe"; + string? workingDir = null; + var debugger = GetDebugTargetsProvider(); + var profileName = "run"; + _mockFS.Create(executable); + + debugger.ValidateSettings(executable, workingDir!, profileName); + Assert.True(true); + } - [Fact] - public async Task CommandLineArgNewLines_AreStripped() + [Fact] + public void ValidateSettings_WhenWorkingDirNotFound_Throws() + { + string executable = "bar.exe"; + string workingDir = @"c:\foo"; + var debugger = GetDebugTargetsProvider(); + var profileName = "run"; + + Assert.ThrowsAny(() => { - var provider = GetDebugTargetsProvider( - outputType: ProjectOutputType.Library, - properties: new Dictionary(), - debugger: null, - scope: null); + debugger.ValidateSettings(executable, workingDir, profileName); + }); + } - var activeProfile = new LaunchProfile("Name", "Executable", commandLineArgs: "-arg1\r\n-arg2 -arg3\n -arg4\r\n\\r\\n"); - var launchSettings = await provider.GetConsoleTargetForProfileAsync(activeProfile, DebugLaunchOptions.NoDebug, false); + [Fact] + public async Task CommandLineArgNewLines_AreStripped() + { + var provider = GetDebugTargetsProvider( + outputType: ProjectOutputType.Library, + properties: new Dictionary(), + debugger: null, + scope: null); - Assert.Equal("-arg1 -arg2 -arg3 -arg4 \\r\\n", launchSettings?.Arguments); + var activeProfile = new LaunchProfile("Name", "Executable", commandLineArgs: "-arg1\r\n-arg2 -arg3\n -arg4\r\n\\r\\n"); + var launchSettings = await provider.GetConsoleTargetForProfileAsync(activeProfile, DebugLaunchOptions.NoDebug, false); - } + Assert.Equal("-arg1 -arg2 -arg3 -arg4 \\r\\n", launchSettings?.Arguments); - [Fact] - public void ValidateSettings_WhenWorkingDirFound_DoesNotThrow() - { - string executable = "bar.exe"; - string workingDir = @"c:\foo"; - var debugger = GetDebugTargetsProvider(); - var profileName = "run"; + } - _mockFS.AddFolder(workingDir); + [Fact] + public void ValidateSettings_WhenWorkingDirFound_DoesNotThrow() + { + string executable = "bar.exe"; + string workingDir = @"c:\foo"; + var debugger = GetDebugTargetsProvider(); + var profileName = "run"; - debugger.ValidateSettings(executable, workingDir, profileName); - Assert.True(true); - } + _mockFS.AddFolder(workingDir); - [Theory] - [InlineData("exec \"C:\\temp\\test.dll\"", "exec \"C:\\temp\\test.dll\"")] - [InlineData("exec ^<>\"C:\\temp&^\\test.dll\"&", "exec ^^^<^>\"C:\\temp&^\\test.dll\"^&")] - public void ConsoleDebugTargetsProvider_EscapeString_WorksCorrectly(string input, string expected) - { - Assert.Equal(expected, ProjectLaunchTargetsProvider.EscapeString(input, new[] { '^', '<', '>', '&' })); - } + debugger.ValidateSettings(executable, workingDir, profileName); + Assert.True(true); + } - [Fact] - public void GetDebugEngineForFrameworkTests() - { - Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetStandardApp")); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetStandard")); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetCore")); - Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetCoreApp")); - Assert.Equal(DebuggerEngines.ManagedOnlyEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NETFramework")); - } + [Theory] + [InlineData("exec \"C:\\temp\\test.dll\"", "exec \"C:\\temp\\test.dll\"")] + [InlineData("exec ^<>\"C:\\temp&^\\test.dll\"&", "exec ^^^<^>\"C:\\temp&^\\test.dll\"^&")] + public void ConsoleDebugTargetsProvider_EscapeString_WorksCorrectly(string input, string expected) + { + Assert.Equal(expected, ProjectLaunchTargetsProvider.EscapeString(input, new[] { '^', '<', '>', '&' })); + } - [Fact] - public async Task CanBeStartupProject_WhenUsingExecutableCommand_AlwaysTrue() - { - var provider = GetDebugTargetsProvider( - outputType: ProjectOutputType.Library, - properties: new Dictionary(), - debugger: null, - scope: null); + [Fact] + public void GetDebugEngineForFrameworkTests() + { + Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetStandardApp")); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetStandard")); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetCore")); + Assert.Equal(DebuggerEngines.ManagedCoreEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NetCoreApp")); + Assert.Equal(DebuggerEngines.ManagedOnlyEngine, ProjectLaunchTargetsProvider.GetManagedDebugEngineForFramework(".NETFramework")); + } - var activeProfile = new LaunchProfile("Name", "Executable"); - bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); + [Fact] + public async Task CanBeStartupProject_WhenUsingExecutableCommand_AlwaysTrue() + { + var provider = GetDebugTargetsProvider( + outputType: ProjectOutputType.Library, + properties: new Dictionary(), + debugger: null, + scope: null); - Assert.True(canBeStartupProject); - } + var activeProfile = new LaunchProfile("Name", "Executable"); + bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); - [Fact] - public async Task CanBeStartupProject_WhenUsingProjectCommand_TrueIfRunCommandPropertySpecified() - { - var provider = GetDebugTargetsProvider( - properties: new Dictionary() { { "RunCommand", @"C:\alpha\beta\gamma.exe" } }); + Assert.True(canBeStartupProject); + } - var activeProfile = new LaunchProfile("Name", "Project"); - bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); + [Fact] + public async Task CanBeStartupProject_WhenUsingProjectCommand_TrueIfRunCommandPropertySpecified() + { + var provider = GetDebugTargetsProvider( + properties: new Dictionary() { { "RunCommand", @"C:\alpha\beta\gamma.exe" } }); - Assert.True(canBeStartupProject); - } + var activeProfile = new LaunchProfile("Name", "Project"); + bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); - [Fact] - public async Task CanBeStartupProject_WhenUsingProjectCommand_TrueIfTargetPathPropertySpecified() - { - var provider = GetDebugTargetsProvider( - properties: new Dictionary() { { "TargetPath", @"C:\alpha\beta\gamma.exe" } }); + Assert.True(canBeStartupProject); + } - var activeProfile = new LaunchProfile("Name", "Project"); - bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); + [Fact] + public async Task CanBeStartupProject_WhenUsingProjectCommand_TrueIfTargetPathPropertySpecified() + { + var provider = GetDebugTargetsProvider( + properties: new Dictionary() { { "TargetPath", @"C:\alpha\beta\gamma.exe" } }); - Assert.True(canBeStartupProject); - } + var activeProfile = new LaunchProfile("Name", "Project"); + bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); - [Fact] - public async Task CanBeStartupProject_WhenUsingProjectCommand_FalseIfRunCommandAndTargetPathNotSpecified() - { - var provider = GetDebugTargetsProvider( - outputType: ProjectOutputType.Library, - properties: new Dictionary(), - debugger: null, - scope: null); + Assert.True(canBeStartupProject); + } + + [Fact] + public async Task CanBeStartupProject_WhenUsingProjectCommand_FalseIfRunCommandAndTargetPathNotSpecified() + { + var provider = GetDebugTargetsProvider( + outputType: ProjectOutputType.Library, + properties: new Dictionary(), + debugger: null, + scope: null); - var activeProfile = new LaunchProfile("Name", "Project"); - bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); + var activeProfile = new LaunchProfile("Name", "Project"); + bool canBeStartupProject = await provider.CanBeStartupProjectAsync(DebugLaunchOptions.NoDebug, activeProfile); - Assert.False(canBeStartupProject); - } + Assert.False(canBeStartupProject); + } - private ProjectLaunchTargetsProvider GetDebugTargetsProvider(ProjectOutputType outputType = ProjectOutputType.Console, Dictionary? properties = null, IVsDebugger10? debugger = null, IProjectCapabilitiesScope? scope = null) - { - _mockFS.Create(@"c:\test\Project\someapp.exe"); - _mockFS.CreateDirectory(@"c:\test\Project"); - _mockFS.CreateDirectory(@"c:\test\Project\bin\"); - _mockFS.Create(@"c:\program files\dotnet\dotnet.exe"); - - var project = UnconfiguredProjectFactory.Create(fullPath: _ProjectFile); - - var outputTypeChecker = outputType switch - { - ProjectOutputType.Console => IOutputTypeCheckerFactory.Create(isLibrary: false, isConsole: true), - ProjectOutputType.Library => IOutputTypeCheckerFactory.Create(isLibrary: true, isConsole: false), - ProjectOutputType.Other => IOutputTypeCheckerFactory.Create(isLibrary: false, isConsole: false), - _ => throw new InvalidOperationException($"Unexpected {nameof(ProjectOutputType)}: {outputType}") - }; - - properties ??= new Dictionary - { - {"RunCommand", @"dotnet"}, - {"RunArguments", "exec " + "\"" + @"c:\test\project\bin\project.dll" + "\""}, - {"RunWorkingDirectory", @"bin\"}, - {"TargetFrameworkIdentifier", @".NetCoreApp"}, - {"OutDir", @"c:\test\project\bin\"} - }; - - var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues(properties); - - var delegateProvider = IProjectPropertiesProviderFactory.Create(null, delegatePropertiesMock.Object); - - var configuredProjectServices = Mock.Of(o => - o.ProjectPropertiesProvider == delegateProvider); - - var capabilitiesScope = scope ?? IProjectCapabilitiesScopeFactory.Create(capabilities: Enumerable.Empty()); - - var configuredProject = Mock.Of(o => - o.UnconfiguredProject == project && - o.Services == configuredProjectServices && - o.Capabilities == capabilitiesScope); - var environment = IEnvironmentHelperFactory.ImplementGetEnvironmentVariable(_Path); - - return CreateInstance(configuredProject: configuredProject, fileSystem: _mockFS, typeChecker: outputTypeChecker, environment: environment, debugger: debugger); - } + private ProjectLaunchTargetsProvider GetDebugTargetsProvider(ProjectOutputType outputType = ProjectOutputType.Console, Dictionary? properties = null, IVsDebugger10? debugger = null, IProjectCapabilitiesScope? scope = null) + { + _mockFS.Create(@"c:\test\Project\someapp.exe"); + _mockFS.CreateDirectory(@"c:\test\Project"); + _mockFS.CreateDirectory(@"c:\test\Project\bin\"); + _mockFS.Create(@"c:\program files\dotnet\dotnet.exe"); - private static ProjectLaunchTargetsProvider CreateInstance( - ConfiguredProject? configuredProject = null, - IDebugTokenReplacer? tokenReplacer = null, - IFileSystem? fileSystem = null, - IEnvironmentHelper? environment = null, - IActiveDebugFrameworkServices? activeDebugFramework = null, - IOutputTypeChecker? typeChecker = null, - IProjectThreadingService? threadingService = null, - IVsDebugger10? debugger = null, - IHotReloadOptionService? hotReloadSettings = null) + var project = UnconfiguredProjectFactory.Create(fullPath: _ProjectFile); + + var outputTypeChecker = outputType switch { - environment ??= Mock.Of(); - tokenReplacer ??= IDebugTokenReplacerFactory.Create(); - activeDebugFramework ??= IActiveDebugFrameworkServicesFactory.ImplementGetConfiguredProjectForActiveFrameworkAsync(configuredProject); - threadingService ??= IProjectThreadingServiceFactory.Create(); - debugger ??= IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: false); - typeChecker ??= IOutputTypeCheckerFactory.Create(); - - IUnconfiguredProjectVsServices unconfiguredProjectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(IVsHierarchyFactory.Create); - - IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService = Mock.Of(); - - return new ProjectLaunchTargetsProvider( - unconfiguredProjectVsServices, - configuredProject!, - tokenReplacer, - fileSystem!, - environment, - activeDebugFramework, - typeChecker, - threadingService, - IVsUIServiceFactory.Create(debugger), - remoteDebuggerAuthenticationService, - new Lazy(IProjectHotReloadSessionManagerFactory.Create), - new Lazy(() => hotReloadSettings ?? IHotReloadOptionServiceFactory.Create())); - } + ProjectOutputType.Console => IOutputTypeCheckerFactory.Create(isLibrary: false, isConsole: true), + ProjectOutputType.Library => IOutputTypeCheckerFactory.Create(isLibrary: true, isConsole: false), + ProjectOutputType.Other => IOutputTypeCheckerFactory.Create(isLibrary: false, isConsole: false), + _ => throw new InvalidOperationException($"Unexpected {nameof(ProjectOutputType)}: {outputType}") + }; - /// - /// Indicates if the project is a library, console app, or something else. - /// - public enum ProjectOutputType + properties ??= new Dictionary { - Library, - Console, - Other - } + {"RunCommand", @"dotnet"}, + {"RunArguments", "exec " + "\"" + @"c:\test\project\bin\project.dll" + "\""}, + {"RunWorkingDirectory", @"bin\"}, + {"TargetFrameworkIdentifier", @".NetCoreApp"}, + {"OutDir", @"c:\test\project\bin\"} + }; + + var delegatePropertiesMock = IProjectPropertiesFactory.MockWithPropertiesAndValues(properties); + + var delegateProvider = IProjectPropertiesProviderFactory.Create(null, delegatePropertiesMock.Object); + + var configuredProjectServices = Mock.Of(o => + o.ProjectPropertiesProvider == delegateProvider); + + var capabilitiesScope = scope ?? IProjectCapabilitiesScopeFactory.Create(capabilities: Enumerable.Empty()); + + var configuredProject = Mock.Of(o => + o.UnconfiguredProject == project && + o.Services == configuredProjectServices && + o.Capabilities == capabilitiesScope); + var environment = IEnvironmentHelperFactory.ImplementGetEnvironmentVariable(_Path); + + return CreateInstance(configuredProject: configuredProject, fileSystem: _mockFS, typeChecker: outputTypeChecker, environment: environment, debugger: debugger); + } + + private static ProjectLaunchTargetsProvider CreateInstance( + ConfiguredProject? configuredProject = null, + IDebugTokenReplacer? tokenReplacer = null, + IFileSystem? fileSystem = null, + IEnvironmentHelper? environment = null, + IActiveDebugFrameworkServices? activeDebugFramework = null, + IOutputTypeChecker? typeChecker = null, + IProjectThreadingService? threadingService = null, + IVsDebugger10? debugger = null, + IHotReloadOptionService? hotReloadSettings = null) + { + environment ??= Mock.Of(); + tokenReplacer ??= IDebugTokenReplacerFactory.Create(); + activeDebugFramework ??= IActiveDebugFrameworkServicesFactory.ImplementGetConfiguredProjectForActiveFrameworkAsync(configuredProject); + threadingService ??= IProjectThreadingServiceFactory.Create(); + debugger ??= IVsDebugger10Factory.ImplementIsIntegratedConsoleEnabled(enabled: false); + typeChecker ??= IOutputTypeCheckerFactory.Create(); + + IUnconfiguredProjectVsServices unconfiguredProjectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(IVsHierarchyFactory.Create); + + IRemoteDebuggerAuthenticationService remoteDebuggerAuthenticationService = Mock.Of(); + + return new ProjectLaunchTargetsProvider( + unconfiguredProjectVsServices, + configuredProject!, + tokenReplacer, + fileSystem!, + environment, + activeDebugFramework, + typeChecker, + threadingService, + IVsUIServiceFactory.Create(debugger), + remoteDebuggerAuthenticationService, + new Lazy(IProjectHotReloadSessionManagerFactory.Create), + new Lazy(() => hotReloadSettings ?? IHotReloadOptionServiceFactory.Create())); + } + + /// + /// Indicates if the project is a library, console app, or something else. + /// + public enum ProjectOutputType + { + Library, + Console, + Other } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/DebugTests/StartupProjectRegistrarTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/DebugTests/StartupProjectRegistrarTests.cs index b61b00ab3f..f32da954e3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/DebugTests/StartupProjectRegistrarTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/DebugTests/StartupProjectRegistrarTests.cs @@ -3,200 +3,199 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug +namespace Microsoft.VisualStudio.ProjectSystem.VS.Debug; + +public class StartupProjectRegistrarTests { - public class StartupProjectRegistrarTests + [Fact] + public async Task DisposeAsync_WhenNotInitialized_DoesNotThrow() { - [Fact] - public async Task DisposeAsync_WhenNotInitialized_DoesNotThrow() - { - var registrar = CreateInstance(); + var registrar = CreateInstance(); - await registrar.DisposeAsync(); + await registrar.DisposeAsync(); - Assert.True(registrar.IsDisposed); - } + Assert.True(registrar.IsDisposed); + } - [Fact] - public async Task DisposeAsync_WhenInitialized_DoesNotThrow() - { - var registrar = await CreateInitializedInstanceAsync(); + [Fact] + public async Task DisposeAsync_WhenInitialized_DoesNotThrow() + { + var registrar = await CreateInitializedInstanceAsync(); - await registrar.DisposeAsync(); + await registrar.DisposeAsync(); - Assert.True(registrar.IsDisposed); - } + Assert.True(registrar.IsDisposed); + } - [Fact] - public async Task OnProjectChanged_WhenNotDebuggable_ProjectNotRegistered() - { - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); - var registrar = await CreateInitializedInstanceAsync(vsStartupProjectsListService, debugProvider); + [Fact] + public async Task OnProjectChanged_WhenNotDebuggable_ProjectNotRegistered() + { + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); + var registrar = await CreateInitializedInstanceAsync(vsStartupProjectsListService, debugProvider); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Null(vsStartupProjectsListService.ProjectGuid); - } + Assert.Null(vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_WhenDebuggable_ProjectRegistered() - { - var projectGuid = Guid.NewGuid(); - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => true); - var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); + [Fact] + public async Task OnProjectChanged_WhenDebuggable_ProjectRegistered() + { + var projectGuid = Guid.NewGuid(); + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => true); + var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); - } + Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_WhenProjectAlreadyRegisteredAndDebuggable_RemainsRegistered() - { - var projectGuid = Guid.NewGuid(); - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => true); - var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); + [Fact] + public async Task OnProjectChanged_WhenProjectAlreadyRegisteredAndDebuggable_RemainsRegistered() + { + var projectGuid = Guid.NewGuid(); + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => true); + var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); + Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); - } + Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_WhenProjectNotRegisteredAndNotDebuggable_RemainsUnregistered() - { - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); - var registrar = await CreateInitializedInstanceAsync(vsStartupProjectsListService, debugProvider); + [Fact] + public async Task OnProjectChanged_WhenProjectNotRegisteredAndNotDebuggable_RemainsUnregistered() + { + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); + var registrar = await CreateInitializedInstanceAsync(vsStartupProjectsListService, debugProvider); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Null(vsStartupProjectsListService.ProjectGuid); + Assert.Null(vsStartupProjectsListService.ProjectGuid); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Null(vsStartupProjectsListService.ProjectGuid); - } + Assert.Null(vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_WhenProjectAlreadyRegisteredAndNotDebuggable_ProjectUnregistered() - { - bool isDebuggable = true; - var projectGuid = Guid.NewGuid(); - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => isDebuggable); - var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); + [Fact] + public async Task OnProjectChanged_WhenProjectAlreadyRegisteredAndNotDebuggable_ProjectUnregistered() + { + bool isDebuggable = true; + var projectGuid = Guid.NewGuid(); + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => isDebuggable); + var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - isDebuggable = false; + isDebuggable = false; - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Null(vsStartupProjectsListService.ProjectGuid); - } + Assert.Null(vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_WhenProjectNotRegisteredAndDebuggable_ProjectRegistered() - { - bool isDebuggable = false; - var projectGuid = Guid.NewGuid(); - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => isDebuggable); - var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); + [Fact] + public async Task OnProjectChanged_WhenProjectNotRegisteredAndDebuggable_ProjectRegistered() + { + bool isDebuggable = false; + var projectGuid = Guid.NewGuid(); + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => isDebuggable); + var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - isDebuggable = true; + isDebuggable = true; - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); - } + Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_ConsultsAllDebugProviders() - { - var projectGuid = Guid.NewGuid(); - var vsStartupProjectsListService = new VsStartupProjectsListService(); - var debugProvider1 = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); - var debugProvider2 = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => true); + [Fact] + public async Task OnProjectChanged_ConsultsAllDebugProviders() + { + var projectGuid = Guid.NewGuid(); + var vsStartupProjectsListService = new VsStartupProjectsListService(); + var debugProvider1 = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); + var debugProvider2 = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => true); - var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider1, debugProvider2); + var registrar = await CreateInitializedInstanceAsync(projectGuid, vsStartupProjectsListService, debugProvider1, debugProvider2); - await registrar.OnProjectChangedAsync(); + await registrar.OnProjectChangedAsync(); - Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); - } + Assert.Equal(projectGuid, vsStartupProjectsListService.ProjectGuid); + } - [Fact] - public async Task OnProjectChanged_VsStartupProjectsListServiceIsNull_DoesNotThrow() - { - var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); - var registrar = await CreateInitializedInstanceAsync(vsStartupProjectsListService: null, debugProvider); + [Fact] + public async Task OnProjectChanged_VsStartupProjectsListServiceIsNull_DoesNotThrow() + { + var debugProvider = IDebugLaunchProviderFactory.ImplementIsProjectDebuggableAsync(() => false); + var registrar = await CreateInitializedInstanceAsync(vsStartupProjectsListService: null, debugProvider); - await registrar.OnProjectChangedAsync(); - } + await registrar.OnProjectChangedAsync(); + } - private static Task CreateInitializedInstanceAsync(IVsStartupProjectsListService? vsStartupProjectsListService, params IDebugLaunchProvider[] launchProviders) - { - return CreateInitializedInstanceAsync(Guid.NewGuid(), vsStartupProjectsListService, launchProviders); - } + private static Task CreateInitializedInstanceAsync(IVsStartupProjectsListService? vsStartupProjectsListService, params IDebugLaunchProvider[] launchProviders) + { + return CreateInitializedInstanceAsync(Guid.NewGuid(), vsStartupProjectsListService, launchProviders); + } + + private static Task CreateInitializedInstanceAsync(Guid projectGuid, IVsStartupProjectsListService? vsStartupProjectsListService, params IDebugLaunchProvider[] launchProviders) + { + var projectGuidService = ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(projectGuid); + var debuggerLaunchProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst); - private static Task CreateInitializedInstanceAsync(Guid projectGuid, IVsStartupProjectsListService? vsStartupProjectsListService, params IDebugLaunchProvider[] launchProviders) + int orderPrecedence = 0; + foreach (IDebugLaunchProvider launchProvider in launchProviders) { - var projectGuidService = ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(projectGuid); - var debuggerLaunchProviders = new OrderPrecedenceImportCollection(ImportOrderPrecedenceComparer.PreferenceOrder.PreferredComesFirst); - - int orderPrecedence = 0; - foreach (IDebugLaunchProvider launchProvider in launchProviders) - { - debuggerLaunchProviders.Add(launchProvider, orderPrecedence: orderPrecedence++); - } - - return CreateInitializedInstanceAsync(vsStartupProjectsListService: vsStartupProjectsListService, - projectGuidService: projectGuidService, - launchProviders: IActiveConfiguredValuesFactory.ImplementValues(() => debuggerLaunchProviders)); + debuggerLaunchProviders.Add(launchProvider, orderPrecedence: orderPrecedence++); } - private static async Task CreateInitializedInstanceAsync( - IVsStartupProjectsListService? vsStartupProjectsListService = null, - IProjectThreadingService? threadingService = null, - ISafeProjectGuidService? projectGuidService = null, - IActiveConfiguredProjectSubscriptionService? projectSubscriptionService = null, - IActiveConfiguredValues? launchProviders = null) - { - var instance = CreateInstance(vsStartupProjectsListService, threadingService, projectGuidService, projectSubscriptionService, launchProviders); - await instance.InitializeAsync(); + return CreateInitializedInstanceAsync(vsStartupProjectsListService: vsStartupProjectsListService, + projectGuidService: projectGuidService, + launchProviders: IActiveConfiguredValuesFactory.ImplementValues(() => debuggerLaunchProviders)); + } - return instance; - } + private static async Task CreateInitializedInstanceAsync( + IVsStartupProjectsListService? vsStartupProjectsListService = null, + IProjectThreadingService? threadingService = null, + ISafeProjectGuidService? projectGuidService = null, + IActiveConfiguredProjectSubscriptionService? projectSubscriptionService = null, + IActiveConfiguredValues? launchProviders = null) + { + var instance = CreateInstance(vsStartupProjectsListService, threadingService, projectGuidService, projectSubscriptionService, launchProviders); + await instance.InitializeAsync(); - private static StartupProjectRegistrar CreateInstance( - IVsStartupProjectsListService? vsStartupProjectsListService = null, - IProjectThreadingService? threadingService = null, - ISafeProjectGuidService? projectGuidService = null, - IActiveConfiguredProjectSubscriptionService? projectSubscriptionService = null, - IActiveConfiguredValues? launchProviders = null) - { - var project = UnconfiguredProjectFactory.Create(); - var instance = new StartupProjectRegistrar( - project, - IUnconfiguredProjectTasksServiceFactory.Create(), - IVsServiceFactory.Create(vsStartupProjectsListService!), - threadingService ?? IProjectThreadingServiceFactory.Create(), - projectGuidService ?? ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(Guid.NewGuid()), - projectSubscriptionService ?? IActiveConfiguredProjectSubscriptionServiceFactory.Create(), - launchProviders!); - - return instance; - } + return instance; + } + + private static StartupProjectRegistrar CreateInstance( + IVsStartupProjectsListService? vsStartupProjectsListService = null, + IProjectThreadingService? threadingService = null, + ISafeProjectGuidService? projectGuidService = null, + IActiveConfiguredProjectSubscriptionService? projectSubscriptionService = null, + IActiveConfiguredValues? launchProviders = null) + { + var project = UnconfiguredProjectFactory.Create(); + var instance = new StartupProjectRegistrar( + project, + IUnconfiguredProjectTasksServiceFactory.Create(), + IVsServiceFactory.Create(vsStartupProjectsListService!), + threadingService ?? IProjectThreadingServiceFactory.Create(), + projectGuidService ?? ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(Guid.NewGuid()), + projectSubscriptionService ?? IActiveConfiguredProjectSubscriptionServiceFactory.Create(), + launchProviders!); + + return instance; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/FSharp/FSharpProjectSelectorTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/FSharp/FSharpProjectSelectorTests.cs index b4924daa18..6bb5df872c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/FSharp/FSharpProjectSelectorTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/FSharp/FSharpProjectSelectorTests.cs @@ -2,26 +2,25 @@ using System.Xml.Linq; -namespace Microsoft.VisualStudio.ProjectSystem.VS.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.FSharp; + +public class FSharpProjectSelectorTests { - public class FSharpProjectSelectorTests - { - [Theory] - [InlineData(@" ", ProjectType.FSharp)] - [InlineData(@" ", ProjectType.LegacyFSharp)] - [InlineData(@" ", ProjectType.FSharp)] - [InlineData(@" ", ProjectType.LegacyFSharp)] - [InlineData(@" ", ProjectType.FSharp)] - [InlineData(@" ", ProjectType.LegacyFSharp)] - [InlineData(@" ", ProjectType.FSharp)] - [InlineData(@" ", ProjectType.LegacyFSharp)] + [Theory] + [InlineData(@" ", ProjectType.FSharp)] + [InlineData(@" ", ProjectType.LegacyFSharp)] + [InlineData(@" ", ProjectType.FSharp)] + [InlineData(@" ", ProjectType.LegacyFSharp)] + [InlineData(@" ", ProjectType.FSharp)] + [InlineData(@" ", ProjectType.LegacyFSharp)] + [InlineData(@" ", ProjectType.FSharp)] + [InlineData(@" ", ProjectType.LegacyFSharp)] - public void GetProjectFactoryGuid(string projectFile, string expectedGuid) - { - var doc = XDocument.Parse(projectFile); - FSharpProjectSelector.GetProjectFactoryGuid(doc, out var resultGuid); + public void GetProjectFactoryGuid(string projectFile, string expectedGuid) + { + var doc = XDocument.Parse(projectFile); + FSharpProjectSelector.GetProjectFactoryGuid(doc, out var resultGuid); - Assert.Equal(expectedGuid, resultGuid.ToString(), ignoreCase: true); - } + Assert.Equal(expectedGuid, resultGuid.ToString(), ignoreCase: true); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManagerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManagerTests.cs index ea7df5aebd..ce54373859 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManagerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/HotReload/ProjectHotReloadSessionManagerTests.cs @@ -3,196 +3,195 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload +namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload; + +public class ProjectHotReloadSessionManagerTests { - public class ProjectHotReloadSessionManagerTests + [Fact] + public async Task WhenActiveFrameworkMeetsRequirements_APendingSessionIsCreated() { - [Fact] - public async Task WhenActiveFrameworkMeetsRequirements_APendingSessionIsCreated() - { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" }, - { "DebugSymbols", "true" }, - // Note: "Optimize" is not included here. The compilers do not optimize by default; - // so if the property isn't set that's OK. - }; - - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); - - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - - Assert.True(sessionCreated); - } - - [Fact] - public async Task WhenTheSupportsHotReloadCapabilityIsMissing_APendingSessionIsNotCreated() + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var capabilities = new[] { "ARandomCapabilityUnrelatedToHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" }, - { "DebugSymbols", "true" } - }; + { "TargetFrameworkVersion", "v6.0" }, + { "DebugSymbols", "true" }, + // Note: "Optimize" is not included here. The compilers do not optimize by default; + // so if the property isn't set that's OK. + }; - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - Assert.False(sessionCreated); - } + Assert.True(sessionCreated); + } - [Fact] - public async Task WhenTheTargetFrameworkVersionIsNotDefined_APendingSessionIsNotCreated() + [Fact] + public async Task WhenTheSupportsHotReloadCapabilityIsMissing_APendingSessionIsNotCreated() + { + var capabilities = new[] { "ARandomCapabilityUnrelatedToHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "ARandomProperty", "WithARandomValue" }, - { "DebugSymbols", "true" } - }; + { "TargetFrameworkVersion", "v6.0" }, + { "DebugSymbols", "true" } + }; - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - Assert.False(sessionCreated); - } + Assert.False(sessionCreated); + } - [Fact] - public async Task WhenStartupHooksAreDisabled_APendingSessionIsNotCreated() - { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" }, - { "StartupHookSupport", "false" }, - { "DebugSymbols", "true" } - }; - - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - bool outputServiceCalled = false; - void OutputServiceCallback() => outputServiceCalled = true; - var manager = CreateHotReloadSessionManager(activeConfiguredProject, OutputServiceCallback); - - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - - Assert.False(sessionCreated); - Assert.True(outputServiceCalled); - } - - [Fact] - public async Task WhenOptimizationIsEnabled_APendingSessionIsNotCreated() + [Fact] + public async Task WhenTheTargetFrameworkVersionIsNotDefined_APendingSessionIsNotCreated() + { + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" }, - { "DebugSymbols", "true" }, - { "Optimize", "true" } - }; + { "ARandomProperty", "WithARandomValue" }, + { "DebugSymbols", "true" } + }; - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - Assert.False(sessionCreated); - } + Assert.False(sessionCreated); + } - [Fact] - public async Task WhenDebugSymbolsIsNotSpecified_APendingSessionIsNotCreated() + [Fact] + public async Task WhenStartupHooksAreDisabled_APendingSessionIsNotCreated() + { + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" } - }; + { "TargetFrameworkVersion", "v6.0" }, + { "StartupHookSupport", "false" }, + { "DebugSymbols", "true" } + }; - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + bool outputServiceCalled = false; + void OutputServiceCallback() => outputServiceCalled = true; + var manager = CreateHotReloadSessionManager(activeConfiguredProject, OutputServiceCallback); - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - Assert.False(sessionCreated); - } + Assert.False(sessionCreated); + Assert.True(outputServiceCalled); + } - [Fact] - public async Task WhenDebugSymbolsIsFalse_APendingSessionIsNotCreated() + [Fact] + public async Task WhenOptimizationIsEnabled_APendingSessionIsNotCreated() + { + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" }, - { "DebugSymbols", "false" } - }; + { "TargetFrameworkVersion", "v6.0" }, + { "DebugSymbols", "true" }, + { "Optimize", "true" } + }; - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); - var environmentVariables = new Dictionary(); - var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); - Assert.False(sessionCreated); - } + Assert.False(sessionCreated); + } - [Fact] - public void WhenNoActiveSession_HasSessionsReturnsFalse() + [Fact] + public async Task WhenDebugSymbolsIsNotSpecified_APendingSessionIsNotCreated() + { + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var capabilities = new[] { "SupportsHotReload" }; - var propertyNamesAndValues = new Dictionary() - { - { "TargetFrameworkVersion", "v6.0" }, - { "DebugSymbols", "true" }, - // Note: "Optimize" is not included here. The compilers do not optimize by default; - // so if the property isn't set that's OK. - }; - - var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); - var manager = CreateHotReloadSessionManager(activeConfiguredProject); - - Assert.False(manager.HasActiveHotReloadSessions); - } - - private static ProjectHotReloadSessionManager CreateHotReloadSessionManager(ConfiguredProject activeConfiguredProject, Action? outputServiceCallback = null) + { "TargetFrameworkVersion", "v6.0" } + }; + + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); + + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + + Assert.False(sessionCreated); + } + + [Fact] + public async Task WhenDebugSymbolsIsFalse_APendingSessionIsNotCreated() + { + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - var activeDebugFrameworkServices = new IActiveDebugFrameworkServicesMock() - .ImplementGetConfiguredProjectForActiveFrameworkAsync(activeConfiguredProject) - .Object; - - var iVSSolutionBuildManagerServiceMock = new Mock>(); - - var manager = new ProjectHotReloadSessionManager( - UnconfiguredProjectFactory.Create(), - IProjectThreadingServiceFactory.Create(), - IProjectFaultHandlerServiceFactory.Create(), - activeDebugFrameworkServices, - new Lazy(() => IProjectHotReloadAgentFactory.Create()), - new Lazy(() => IHotReloadDiagnosticOutputServiceFactory.Create(outputServiceCallback)), - new Lazy(() => IProjectHotReloadNotificationServiceFactory.Create()), - iVSSolutionBuildManagerServiceMock.Object); - - return manager; - } - - private static ConfiguredProject CreateConfiguredProject(string[] capabilities, Dictionary propertyNamesAndValues) + { "TargetFrameworkVersion", "v6.0" }, + { "DebugSymbols", "false" } + }; + + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); + + var environmentVariables = new Dictionary(); + var sessionCreated = await manager.TryCreatePendingSessionAsync(environmentVariables); + + Assert.False(sessionCreated); + } + + [Fact] + public void WhenNoActiveSession_HasSessionsReturnsFalse() + { + var capabilities = new[] { "SupportsHotReload" }; + var propertyNamesAndValues = new Dictionary() { - return ConfiguredProjectFactory.Create( - IProjectCapabilitiesScopeFactory.Create(capabilities), - services: ConfiguredProjectServicesFactory.Create( - projectPropertiesProvider: IProjectPropertiesProviderFactory.Create( - commonProps: IProjectPropertiesFactory.CreateWithPropertiesAndValues( - propertyNamesAndValues)))); - } + { "TargetFrameworkVersion", "v6.0" }, + { "DebugSymbols", "true" }, + // Note: "Optimize" is not included here. The compilers do not optimize by default; + // so if the property isn't set that's OK. + }; + + var activeConfiguredProject = CreateConfiguredProject(capabilities, propertyNamesAndValues); + var manager = CreateHotReloadSessionManager(activeConfiguredProject); + + Assert.False(manager.HasActiveHotReloadSessions); + } + + private static ProjectHotReloadSessionManager CreateHotReloadSessionManager(ConfiguredProject activeConfiguredProject, Action? outputServiceCallback = null) + { + var activeDebugFrameworkServices = new IActiveDebugFrameworkServicesMock() + .ImplementGetConfiguredProjectForActiveFrameworkAsync(activeConfiguredProject) + .Object; + + var iVSSolutionBuildManagerServiceMock = new Mock>(); + + var manager = new ProjectHotReloadSessionManager( + UnconfiguredProjectFactory.Create(), + IProjectThreadingServiceFactory.Create(), + IProjectFaultHandlerServiceFactory.Create(), + activeDebugFrameworkServices, + new Lazy(() => IProjectHotReloadAgentFactory.Create()), + new Lazy(() => IHotReloadDiagnosticOutputServiceFactory.Create(outputServiceCallback)), + new Lazy(() => IProjectHotReloadNotificationServiceFactory.Create()), + iVSSolutionBuildManagerServiceMock.Object); + + return manager; + } + + private static ConfiguredProject CreateConfiguredProject(string[] capabilities, Dictionary propertyNamesAndValues) + { + return ConfiguredProjectFactory.Create( + IProjectCapabilitiesScopeFactory.Create(capabilities), + services: ConfiguredProjectServicesFactory.Create( + projectPropertiesProvider: IProjectPropertiesProviderFactory.Create( + commonProps: IProjectPropertiesFactory.CreateWithPropertiesAndValues( + propertyNamesAndValues)))); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandlerTests.cs index 3f741ebb6b..01b2188038 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractAddItemCommandHandlerTests.cs @@ -3,202 +3,201 @@ using Microsoft.VisualStudio.ProjectSystem.VS.UI; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class AbstractAddItemCommandHandlerTests { - public class AbstractAddItemCommandHandlerTests + [Fact] + public void GetCommandStatusAsync_NullAsNodes_ThrowsArgumentNull() { - [Fact] - public void GetCommandStatusAsync_NullAsNodes_ThrowsArgumentNull() + var command = CreateInstance(); + + Assert.ThrowsAsync("nodes", () => { - var command = CreateInstance(); + return command.GetCommandStatusAsync(null!, TestAddItemCommand.CommandId, true, "commandText", CommandStatus.Enabled); + }); + } - Assert.ThrowsAsync("nodes", () => - { - return command.GetCommandStatusAsync(null!, TestAddItemCommand.CommandId, true, "commandText", CommandStatus.Enabled); - }); - } + [Fact] + public async Task GetCommandStatusAsync_UnrecognizedCommandIdAsCommandId_ReturnsUnhandled() + { + var command = CreateInstance(); - [Fact] - public async Task GetCommandStatusAsync_UnrecognizedCommandIdAsCommandId_ReturnsUnhandled() - { - var command = CreateInstance(); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + """); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - """); + var nodes = ImmutableHashSet.Create(tree); - var nodes = ImmutableHashSet.Create(tree); + var result = await command.GetCommandStatusAsync(nodes, 1, true, "commandText", CommandStatus.Enabled); - var result = await command.GetCommandStatusAsync(nodes, 1, true, "commandText", CommandStatus.Enabled); + Assert.False(result.Handled); + } - Assert.False(result.Handled); - } + [Fact] + public async Task TryHandleCommandAsync_NonMatchingCapability_ReturnsFalse() + { + var command = CreateInstance(capability: "IncorrectCapability"); - [Fact] - public async Task TryHandleCommandAsync_NonMatchingCapability_ReturnsFalse() - { - var command = CreateInstance(capability: "IncorrectCapability"); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + """); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - """); + var nodes = ImmutableHashSet.Create(tree); - var nodes = ImmutableHashSet.Create(tree); + var result = await command.GetCommandStatusAsync(nodes, TestAddItemCommand.CommandId, true, "commandText", CommandStatus.Enabled); - var result = await command.GetCommandStatusAsync(nodes, TestAddItemCommand.CommandId, true, "commandText", CommandStatus.Enabled); + Assert.False(result.Handled); + } - Assert.False(result.Handled); - } + [Fact] + public async Task TryHandleCommandAsync_MoreThanOneNodeAsNodes_ReturnsFalse() + { + var command = CreateInstance(); - [Fact] - public async Task TryHandleCommandAsync_MoreThanOneNodeAsNodes_ReturnsFalse() - { - var command = CreateInstance(); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + var nodes = ImmutableHashSet.Create(tree, tree.Children[0]); - var nodes = ImmutableHashSet.Create(tree, tree.Children[0]); + var result = await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); - var result = await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); + Assert.False(result); + } - Assert.False(result); - } + [Fact] + public async Task GetCommandStatusAsync_FolderAsNodes_ReturnsHandled() + { + var command = CreateInstance(); - [Fact] - public async Task GetCommandStatusAsync_FolderAsNodes_ReturnsHandled() - { - var command = CreateInstance(); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Code (flags: {Folder}) + """); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Code (flags: {Folder}) - """); + var nodes = ImmutableHashSet.Create(tree.Children[0]); - var nodes = ImmutableHashSet.Create(tree.Children[0]); + var result = await command.GetCommandStatusAsync(nodes, TestAddItemCommand.CommandId, true, "commandText", 0); - var result = await command.GetCommandStatusAsync(nodes, TestAddItemCommand.CommandId, true, "commandText", 0); + Assert.True(result.Handled); + Assert.Equal("commandText", result.CommandText); + Assert.Equal(CommandStatus.Enabled | CommandStatus.Supported, result.Status); + } - Assert.True(result.Handled); - Assert.Equal("commandText", result.CommandText); - Assert.Equal(CommandStatus.Enabled | CommandStatus.Supported, result.Status); - } + [Fact] + public async Task TryHandleCommandAsync_FolderAsNodes_ReturnsTrue() + { + var command = CreateInstance(); - [Fact] - public async Task TryHandleCommandAsync_FolderAsNodes_ReturnsTrue() - { - var command = CreateInstance(); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Code (flags: {Folder}) + """); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Code (flags: {Folder}) - """); + var nodes = ImmutableHashSet.Create(tree.Children[0]); - var nodes = ImmutableHashSet.Create(tree.Children[0]); + var result = await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); - var result = await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task TryHandleCommandAsync_FolderAsNodes_CallsShowAddProjectItemDlgAsyncWithFilter() + { + int callCount = 0; + string localizedDirectoryNameResult = ""; + string localizedTemplateNameResult = ""; + IProjectTree? nodeResult = null; - [Fact] - public async Task TryHandleCommandAsync_FolderAsNodes_CallsShowAddProjectItemDlgAsyncWithFilter() + var addItemDialogService = IAddItemDialogServiceFactory.ImplementShowAddNewItemDialogAsync((node, localizedDirectoryName, localizedTemplateName) => { - int callCount = 0; - string localizedDirectoryNameResult = ""; - string localizedTemplateNameResult = ""; - IProjectTree? nodeResult = null; - - var addItemDialogService = IAddItemDialogServiceFactory.ImplementShowAddNewItemDialogAsync((node, localizedDirectoryName, localizedTemplateName) => - { - callCount++; - nodeResult = node; - localizedDirectoryNameResult = localizedDirectoryName; - localizedTemplateNameResult = localizedTemplateName; - return true; - }); - - var command = CreateInstance(addItemDialogService: addItemDialogService); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); - - var nodes = ImmutableHashSet.Create(tree.Children[0]); - - await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); - - Assert.Equal(1, callCount); - Assert.Equal(nameof(TestAddItemCommand.ResourceIds.DirName), localizedDirectoryNameResult); - Assert.Equal(nameof(TestAddItemCommand.ResourceIds.TemplateName), localizedTemplateNameResult); - Assert.Same(tree.Children[0], nodeResult); - } + callCount++; + nodeResult = node; + localizedDirectoryNameResult = localizedDirectoryName; + localizedTemplateNameResult = localizedTemplateName; + return true; + }); - [Fact] - public async Task TryHandleCommand_FolderAsNodes_ReturnsTrueWhenUserClicksCancel() - { - var addItemDialogService = IAddItemDialogServiceFactory.ImplementShowAddNewItemDialogAsync((node, localizedDirectoryName, localizedTemplateName) => false); + var command = CreateInstance(addItemDialogService: addItemDialogService); - var command = CreateInstance(addItemDialogService); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Code (flags: {Folder}) - """); + var nodes = ImmutableHashSet.Create(tree.Children[0]); - var nodes = ImmutableHashSet.Create(tree.Children[0]); + await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); - var result = await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); + Assert.Equal(1, callCount); + Assert.Equal(nameof(TestAddItemCommand.ResourceIds.DirName), localizedDirectoryNameResult); + Assert.Equal(nameof(TestAddItemCommand.ResourceIds.TemplateName), localizedTemplateNameResult); + Assert.Same(tree.Children[0], nodeResult); + } - Assert.True(result); - } + [Fact] + public async Task TryHandleCommand_FolderAsNodes_ReturnsTrueWhenUserClicksCancel() + { + var addItemDialogService = IAddItemDialogServiceFactory.ImplementShowAddNewItemDialogAsync((node, localizedDirectoryName, localizedTemplateName) => false); - internal static AbstractAddItemCommandHandler CreateInstance( - IAddItemDialogService? addItemDialogService = null, - string? capability = null) - { - var configuredProject = ConfiguredProjectFactory.Create(IProjectCapabilitiesScopeFactory.Create(new string[] { capability ?? TestAddItemCommand.Capability })); - addItemDialogService ??= IAddItemDialogServiceFactory.Create(); + var command = CreateInstance(addItemDialogService); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Code (flags: {Folder}) + """); + + var nodes = ImmutableHashSet.Create(tree.Children[0]); - string result = "DirName"; - var vsShellMock = new Mock(); - vsShellMock.Setup(x => x.LoadPackageString(ref It.Ref.IsAny, (uint)TestAddItemCommand.ResourceIds.DirName, out result)).Returns(0); - result = "TemplateName"; - vsShellMock.Setup(x => x.LoadPackageString(ref It.Ref.IsAny, (uint)TestAddItemCommand.ResourceIds.TemplateName, out result)).Returns(0); + var result = await command.TryHandleCommandAsync(nodes, TestAddItemCommand.CommandId, true, 0, IntPtr.Zero, IntPtr.Zero); - var vsShellService = IVsUIServiceFactory.Create(vsShellMock.Object); + Assert.True(result); + } + + internal static AbstractAddItemCommandHandler CreateInstance( + IAddItemDialogService? addItemDialogService = null, + string? capability = null) + { + var configuredProject = ConfiguredProjectFactory.Create(IProjectCapabilitiesScopeFactory.Create(new string[] { capability ?? TestAddItemCommand.Capability })); + addItemDialogService ??= IAddItemDialogServiceFactory.Create(); - return new TestAddItemCommand(configuredProject, addItemDialogService, vsShellService); + string result = "DirName"; + var vsShellMock = new Mock(); + vsShellMock.Setup(x => x.LoadPackageString(ref It.Ref.IsAny, (uint)TestAddItemCommand.ResourceIds.DirName, out result)).Returns(0); + result = "TemplateName"; + vsShellMock.Setup(x => x.LoadPackageString(ref It.Ref.IsAny, (uint)TestAddItemCommand.ResourceIds.TemplateName, out result)).Returns(0); + + var vsShellService = IVsUIServiceFactory.Create(vsShellMock.Object); + + return new TestAddItemCommand(configuredProject, addItemDialogService, vsShellService); + } + + private class TestAddItemCommand : AbstractAddItemCommandHandler + { + public const long CommandId = 150; + public const string Capability = "Capability"; + + public enum ResourceIds + { + DirName = 523, + TemplateName = 2014 } - private class TestAddItemCommand : AbstractAddItemCommandHandler + public TestAddItemCommand(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) + : base(configuredProject, addItemDialogService, vsShell) { - public const long CommandId = 150; - public const string Capability = "Capability"; - - public enum ResourceIds - { - DirName = 523, - TemplateName = 2014 - } - - public TestAddItemCommand(ConfiguredProject configuredProject, IAddItemDialogService addItemDialogService, IVsUIService vsShell) - : base(configuredProject, addItemDialogService, vsShell) - { - } - - protected override ImmutableDictionary> TemplatesByCommandId => ImmutableDictionary>.Empty - .CreateTemplateDetails(CommandId, Capability, Guid.Empty, ResourceIds.DirName, ResourceIds.TemplateName); } + + protected override ImmutableDictionary> TemplatesByCommandId => ImmutableDictionary>.Empty + .CreateTemplateDetails(CommandId, Capability, Guid.Empty, ResourceIds.DirName, ResourceIds.TemplateName); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommandTests.cs index c6ca56033d..95d83cf6a6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractGenerateNuGetPackageCommandTests.cs @@ -4,155 +4,154 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Build; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public abstract class AbstractGenerateNuGetPackageCommandTests { - public abstract class AbstractGenerateNuGetPackageCommandTests + [Fact] + public void Constructor_NullAsUnconfiguredProject_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstanceCore(null!, IProjectThreadingServiceFactory.Create(), ISolutionBuildManagerFactory.Create(), CreateGeneratePackageOnBuildPropertyProvider())); + } + + [Fact] + public void Constructor_NullAsProjectThreadingServiceFactory_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstanceCore(UnconfiguredProjectFactory.Create(), null!, ISolutionBuildManagerFactory.Create(), CreateGeneratePackageOnBuildPropertyProvider())); + } + + [Fact] + public void Constructor_NullAsSVsServiceProvider_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstanceCore(UnconfiguredProjectFactory.Create(), IProjectThreadingServiceFactory.Create(), null!, CreateGeneratePackageOnBuildPropertyProvider())); + } + + [Fact] + public void Constructor_NullAsGeneratePackageOnBuildPropertyProvider_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstanceCore(UnconfiguredProjectFactory.Create(), IProjectThreadingServiceFactory.Create(), ISolutionBuildManagerFactory.Create(), null!)); + } + + [Fact] + public async Task TryHandleCommandAsync_InvokesBuild() + { + bool buildStarted = false, buildCancelled = false, buildCompleted = false; + + void onUpdateSolutionBegin() => buildStarted = true; + void onUpdateSolutionCancel() => buildCancelled = true; + void onUpdateSolutionDone() => buildCompleted = true; + + var solutionEventsListener = IVsUpdateSolutionEventsFactory.Create(onUpdateSolutionBegin, onUpdateSolutionCancel, onUpdateSolutionDone); + var command = CreateInstance(solutionEventsListener: solutionEventsListener); + + var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); + + var nodes = ImmutableHashSet.Create(tree.Root); + + var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + + Assert.True(result); + Assert.True(buildStarted); + Assert.True(buildCompleted); + Assert.False(buildCancelled); + } + + [Fact] + public async Task TryHandleCommandAsync_OnBuildCancelled() + { + bool buildStarted = false, buildCancelled = false, buildCompleted = false; + + void onUpdateSolutionBegin() => buildStarted = true; + void onUpdateSolutionCancel() => buildCancelled = true; + void onUpdateSolutionDone() => buildCompleted = true; + + var solutionEventsListener = IVsUpdateSolutionEventsFactory.Create(onUpdateSolutionBegin, onUpdateSolutionCancel, onUpdateSolutionDone); + var command = CreateInstance(solutionEventsListener: solutionEventsListener, cancelBuild: true); + + var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); + + var nodes = ImmutableHashSet.Create(tree.Root); + + var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + + Assert.True(result); + Assert.True(buildStarted); + Assert.False(buildCompleted); + Assert.True(buildCancelled); + } + + [Fact] + public async Task GetCommandStatusAsync_BuildInProgress() + { + var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); + + var nodes = ImmutableHashSet.Create(tree.Root); + + // Command is enabled if there is no build in progress. + var command = CreateInstance(isBuilding: false); + var results = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + Assert.True(results.Handled); + Assert.Equal(CommandStatus.Enabled | CommandStatus.Supported, results.Status); + + // Command is disabled if there is build in progress. + command = CreateInstance(isBuilding: true); + results = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + Assert.True(results.Handled); + Assert.Equal(CommandStatus.Supported, results.Status); + } + + [Fact] + public async Task TryHandleCommandAsync_BuildInProgress() { - [Fact] - public void Constructor_NullAsUnconfiguredProject_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstanceCore(null!, IProjectThreadingServiceFactory.Create(), ISolutionBuildManagerFactory.Create(), CreateGeneratePackageOnBuildPropertyProvider())); - } + var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); + + var nodes = ImmutableHashSet.Create(tree.Root); + + bool buildStarted = false, buildCancelled = false, buildCompleted = false; + + void onUpdateSolutionBegin() => buildStarted = true; + void onUpdateSolutionCancel() => buildCancelled = true; + void onUpdateSolutionDone() => buildCompleted = true; + + var solutionEventsListener = IVsUpdateSolutionEventsFactory.Create(onUpdateSolutionBegin, onUpdateSolutionCancel, onUpdateSolutionDone); + + var command = CreateInstance(solutionEventsListener: solutionEventsListener, isBuilding: true); - [Fact] - public void Constructor_NullAsProjectThreadingServiceFactory_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstanceCore(UnconfiguredProjectFactory.Create(), null!, ISolutionBuildManagerFactory.Create(), CreateGeneratePackageOnBuildPropertyProvider())); - } + // Ensure we handle the command, but don't invoke build as there is a build already in progress. + var handled = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + Assert.True(handled); + Assert.False(buildStarted); + Assert.False(buildCompleted); + Assert.False(buildCancelled); + } + + internal abstract long GetCommandId(); - [Fact] - public void Constructor_NullAsSVsServiceProvider_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstanceCore(UnconfiguredProjectFactory.Create(), IProjectThreadingServiceFactory.Create(), null!, CreateGeneratePackageOnBuildPropertyProvider())); - } + internal AbstractGenerateNuGetPackageCommand CreateInstance( + GeneratePackageOnBuildPropertyProvider? generatePackageOnBuildPropertyProvider = null, + ISolutionBuildManager? solutionBuildManager = null, + IVsUpdateSolutionEvents? solutionEventsListener = null, + bool isBuilding = false, + bool cancelBuild = false) + { + var hierarchy = IVsHierarchyFactory.Create(); + var project = UnconfiguredProjectFactory.Create(hierarchy); + var threadingService = IProjectThreadingServiceFactory.Create(); + solutionBuildManager ??= ISolutionBuildManagerFactory.Create(solutionEventsListener, hierarchy, isBuilding, cancelBuild); + generatePackageOnBuildPropertyProvider ??= CreateGeneratePackageOnBuildPropertyProvider(); - [Fact] - public void Constructor_NullAsGeneratePackageOnBuildPropertyProvider_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstanceCore(UnconfiguredProjectFactory.Create(), IProjectThreadingServiceFactory.Create(), ISolutionBuildManagerFactory.Create(), null!)); - } - - [Fact] - public async Task TryHandleCommandAsync_InvokesBuild() - { - bool buildStarted = false, buildCancelled = false, buildCompleted = false; - - void onUpdateSolutionBegin() => buildStarted = true; - void onUpdateSolutionCancel() => buildCancelled = true; - void onUpdateSolutionDone() => buildCompleted = true; - - var solutionEventsListener = IVsUpdateSolutionEventsFactory.Create(onUpdateSolutionBegin, onUpdateSolutionCancel, onUpdateSolutionDone); - var command = CreateInstance(solutionEventsListener: solutionEventsListener); - - var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); - - var nodes = ImmutableHashSet.Create(tree.Root); - - var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - - Assert.True(result); - Assert.True(buildStarted); - Assert.True(buildCompleted); - Assert.False(buildCancelled); - } - - [Fact] - public async Task TryHandleCommandAsync_OnBuildCancelled() - { - bool buildStarted = false, buildCancelled = false, buildCompleted = false; - - void onUpdateSolutionBegin() => buildStarted = true; - void onUpdateSolutionCancel() => buildCancelled = true; - void onUpdateSolutionDone() => buildCompleted = true; - - var solutionEventsListener = IVsUpdateSolutionEventsFactory.Create(onUpdateSolutionBegin, onUpdateSolutionCancel, onUpdateSolutionDone); - var command = CreateInstance(solutionEventsListener: solutionEventsListener, cancelBuild: true); - - var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); - - var nodes = ImmutableHashSet.Create(tree.Root); - - var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - - Assert.True(result); - Assert.True(buildStarted); - Assert.False(buildCompleted); - Assert.True(buildCancelled); - } - - [Fact] - public async Task GetCommandStatusAsync_BuildInProgress() - { - var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); - - var nodes = ImmutableHashSet.Create(tree.Root); - - // Command is enabled if there is no build in progress. - var command = CreateInstance(isBuilding: false); - var results = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - Assert.True(results.Handled); - Assert.Equal(CommandStatus.Enabled | CommandStatus.Supported, results.Status); - - // Command is disabled if there is build in progress. - command = CreateInstance(isBuilding: true); - results = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - Assert.True(results.Handled); - Assert.Equal(CommandStatus.Supported, results.Status); - } - - [Fact] - public async Task TryHandleCommandAsync_BuildInProgress() - { - var tree = ProjectTreeParser.Parse("Root (flags: {ProjectRoot})"); - - var nodes = ImmutableHashSet.Create(tree.Root); - - bool buildStarted = false, buildCancelled = false, buildCompleted = false; - - void onUpdateSolutionBegin() => buildStarted = true; - void onUpdateSolutionCancel() => buildCancelled = true; - void onUpdateSolutionDone() => buildCompleted = true; - - var solutionEventsListener = IVsUpdateSolutionEventsFactory.Create(onUpdateSolutionBegin, onUpdateSolutionCancel, onUpdateSolutionDone); - - var command = CreateInstance(solutionEventsListener: solutionEventsListener, isBuilding: true); - - // Ensure we handle the command, but don't invoke build as there is a build already in progress. - var handled = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - Assert.True(handled); - Assert.False(buildStarted); - Assert.False(buildCompleted); - Assert.False(buildCancelled); - } - - internal abstract long GetCommandId(); - - internal AbstractGenerateNuGetPackageCommand CreateInstance( - GeneratePackageOnBuildPropertyProvider? generatePackageOnBuildPropertyProvider = null, - ISolutionBuildManager? solutionBuildManager = null, - IVsUpdateSolutionEvents? solutionEventsListener = null, - bool isBuilding = false, - bool cancelBuild = false) - { - var hierarchy = IVsHierarchyFactory.Create(); - var project = UnconfiguredProjectFactory.Create(hierarchy); - var threadingService = IProjectThreadingServiceFactory.Create(); - solutionBuildManager ??= ISolutionBuildManagerFactory.Create(solutionEventsListener, hierarchy, isBuilding, cancelBuild); - generatePackageOnBuildPropertyProvider ??= CreateGeneratePackageOnBuildPropertyProvider(); + return CreateInstanceCore(project, threadingService, solutionBuildManager, generatePackageOnBuildPropertyProvider); + } - return CreateInstanceCore(project, threadingService, solutionBuildManager, generatePackageOnBuildPropertyProvider); - } - - private static GeneratePackageOnBuildPropertyProvider CreateGeneratePackageOnBuildPropertyProvider(IProjectService? projectService = null) - { - projectService ??= IProjectServiceFactory.Create(); - return new GeneratePackageOnBuildPropertyProvider(projectService); - } - - internal abstract AbstractGenerateNuGetPackageCommand CreateInstanceCore( - UnconfiguredProject project, - IProjectThreadingService threadingService, - ISolutionBuildManager solutionBuildManager, - GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider); + private static GeneratePackageOnBuildPropertyProvider CreateGeneratePackageOnBuildPropertyProvider(IProjectService? projectService = null) + { + projectService ??= IProjectServiceFactory.Create(); + return new GeneratePackageOnBuildPropertyProvider(projectService); } + + internal abstract AbstractGenerateNuGetPackageCommand CreateInstanceCore( + UnconfiguredProject project, + IProjectThreadingService threadingService, + ISolutionBuildManager solutionBuildManager, + GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommandTests.cs index 562f4d3e9d..5ab3d33024 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/AbstractOpenProjectDesignerCommandTests.cs @@ -2,190 +2,189 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public abstract class AbstractOpenProjectDesignerCommandTests { - public abstract class AbstractOpenProjectDesignerCommandTests + [Fact] + public void GetCommandStatusAsync_NullAsNodes_ThrowsArgumentNull() { - [Fact] - public void GetCommandStatusAsync_NullAsNodes_ThrowsArgumentNull() - { - var command = CreateInstance(); + var command = CreateInstance(); - Assert.ThrowsAsync("nodes", () => - { - return command.GetCommandStatusAsync(null!, GetCommandId(), true, "commandText", CommandStatus.Enabled); - }); - } - - [Fact] - public async Task GetCommandStatusAsync_UnrecognizedCommandIdAsCommandId_ReturnsUnhandled() + Assert.ThrowsAsync("nodes", () => { - var command = CreateInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + return command.GetCommandStatusAsync(null!, GetCommandId(), true, "commandText", CommandStatus.Enabled); + }); + } - var nodes = ImmutableHashSet.Create(tree.Children[0]); + [Fact] + public async Task GetCommandStatusAsync_UnrecognizedCommandIdAsCommandId_ReturnsUnhandled() + { + var command = CreateInstance(); - var result = await command.GetCommandStatusAsync(nodes, 1, true, "commandText", CommandStatus.Enabled); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.False(result.Handled); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - [Fact] - public async Task TryHandleCommandAsync_UnrecognizedCommandIdAsCommandId_ReturnsFalse() - { - var command = CreateInstance(); + var result = await command.GetCommandStatusAsync(nodes, 1, true, "commandText", CommandStatus.Enabled); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + Assert.False(result.Handled); + } - var nodes = ImmutableHashSet.Create(tree.Children[0]); + [Fact] + public async Task TryHandleCommandAsync_UnrecognizedCommandIdAsCommandId_ReturnsFalse() + { + var command = CreateInstance(); - var result = await command.TryHandleCommandAsync(nodes, 1, true, 0, IntPtr.Zero, IntPtr.Zero); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.False(result); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - [Fact] - public async Task GetCommandStatusAsync_MoreThanOneNodeAsNodes_ReturnsUnhandled() - { - var command = CreateInstance(); + var result = await command.TryHandleCommandAsync(nodes, 1, true, 0, IntPtr.Zero, IntPtr.Zero); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + Assert.False(result); + } - var nodes = ImmutableHashSet.Create(tree, tree.Children[0]); + [Fact] + public async Task GetCommandStatusAsync_MoreThanOneNodeAsNodes_ReturnsUnhandled() + { + var command = CreateInstance(); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.False(result.Handled); - } + var nodes = ImmutableHashSet.Create(tree, tree.Children[0]); - [Fact] - public async Task TryHandleCommandAsync_MoreThanOneNodeAsNodes_ReturnsFalse() - { - var command = CreateInstance(); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + Assert.False(result.Handled); + } - var nodes = ImmutableHashSet.Create(tree, tree.Children[0]); + [Fact] + public async Task TryHandleCommandAsync_MoreThanOneNodeAsNodes_ReturnsFalse() + { + var command = CreateInstance(); - var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.False(result); - } + var nodes = ImmutableHashSet.Create(tree, tree.Children[0]); - [Fact] - public async Task GetCommandStatusAsync_NonAppDesignerFolderAsNodes_ReturnsUnhandled() - { - var command = CreateInstance(); + var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - """); + Assert.False(result); + } - var nodes = ImmutableHashSet.Create(tree.Children[0]); + [Fact] + public async Task GetCommandStatusAsync_NonAppDesignerFolderAsNodes_ReturnsUnhandled() + { + var command = CreateInstance(); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}) + """); - Assert.False(result.Handled); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - [Fact] - public async Task TryHandleCommandAsync_NonAppDesignerFolderAsNodes_ReturnsFalse() - { - var command = CreateInstance(); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder}) - """); + Assert.False(result.Handled); + } - var nodes = ImmutableHashSet.Create(tree.Children[0]); + [Fact] + public async Task TryHandleCommandAsync_NonAppDesignerFolderAsNodes_ReturnsFalse() + { + var command = CreateInstance(); - var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder}) + """); - Assert.False(result); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - [Fact] - public async Task GetCommandStatusAsync_AppDesignerFolderAsNodes_ReturnsHandled() - { - var command = CreateInstance(); + var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + Assert.False(result); + } - var nodes = ImmutableHashSet.Create(tree.Children[0]); + [Fact] + public async Task GetCommandStatusAsync_AppDesignerFolderAsNodes_ReturnsHandled() + { + var command = CreateInstance(); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.True(result.Handled); - Assert.Equal("commandText", result.CommandText); - Assert.Equal(CommandStatus.Enabled | CommandStatus.Supported, result.Status); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - [Fact] - public async Task TryHandleCommandAsync_AppDesignerFolderAsNodes_ReturnsTrue() - { - var command = CreateInstance(); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + Assert.True(result.Handled); + Assert.Equal("commandText", result.CommandText); + Assert.Equal(CommandStatus.Enabled | CommandStatus.Supported, result.Status); + } - var nodes = ImmutableHashSet.Create(tree.Children[0]); + [Fact] + public async Task TryHandleCommandAsync_AppDesignerFolderAsNodes_ReturnsTrue() + { + var command = CreateInstance(); - var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.True(result); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - [Fact] - public async Task TryHandleCommandAsync_AppDesignerFolderAsNodes_CallsShowProjectDesignerAsync() - { - int callCount = 0; - var designerService = IProjectDesignerServiceFactory.ImplementShowProjectDesignerAsync(() => { callCount++; }); + var result = await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - var command = CreateInstance(designerService); + Assert.True(result); + } - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {Folder AppDesignerFolder}) - """); + [Fact] + public async Task TryHandleCommandAsync_AppDesignerFolderAsNodes_CallsShowProjectDesignerAsync() + { + int callCount = 0; + var designerService = IProjectDesignerServiceFactory.ImplementShowProjectDesignerAsync(() => { callCount++; }); - var nodes = ImmutableHashSet.Create(tree.Children[0]); + var command = CreateInstance(designerService); - await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {Folder AppDesignerFolder}) + """); - Assert.Equal(1, callCount); - } + var nodes = ImmutableHashSet.Create(tree.Children[0]); - internal abstract long GetCommandId(); + await command.TryHandleCommandAsync(nodes, GetCommandId(), true, 0, IntPtr.Zero, IntPtr.Zero); - internal abstract AbstractOpenProjectDesignerCommand CreateInstance(IProjectDesignerService? designerService = null); + Assert.Equal(1, callCount); } + + internal abstract long GetCommandId(); + + internal abstract AbstractOpenProjectDesignerCommand CreateInstance(IProjectDesignerService? designerService = null); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommandTests.cs index 449c30cba4..e3f549f065 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksDynamicMenuCommandTests.cs @@ -5,350 +5,349 @@ #pragma warning disable VSSDK005 // Avoid instantiating JoinableTaskContext -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class DebugFrameworksDynamicMenuCommandTests { - public class DebugFrameworksDynamicMenuCommandTests + [Fact] + public void ExecCommand_HandleNoStartupProjects() { - [Fact] - public void ExecCommand_HandleNoStartupProjects() - { - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Empty); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Empty); - var command = CreateInstance(startupHelper.Object); - Assert.False(command.ExecCommand(0, EventArgs.Empty)); - startupHelper.Verify(); - } + var command = CreateInstance(startupHelper.Object); + Assert.False(command.ExecCommand(0, EventArgs.Empty)); + startupHelper.Verify(); + } - [Theory] - [InlineData(-1, false)] - [InlineData(2, false)] - [InlineData(0, true)] - [InlineData(1, true)] - public void ExecCommand_SingleStartupProject_VerifyCorrectFrameworkSet(int cmdIndex, bool expected) + [Theory] + [InlineData(-1, false)] + [InlineData(2, false)] + [InlineData(0, true)] + [InlineData(1, true)] + public void ExecCommand_SingleStartupProject_VerifyCorrectFrameworkSet(int cmdIndex, bool expected) + { + var frameworks = new List() { "net461", "netcoreapp1.0" }; + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(frameworks); + if (expected) { - var frameworks = new List() { "net461", "netcoreapp1.0" }; - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(frameworks); - if (expected) - { - activeDebugFrameworkSvcs.ImplementSetActiveDebuggingFrameworkPropertyAsync(frameworks[cmdIndex]); - } - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.Equal(expected, command.ExecCommand(cmdIndex, EventArgs.Empty)); - - startupHelper.Verify(); - activeDebugFrameworkSvcs.Verify(); + activeDebugFrameworkSvcs.ImplementSetActiveDebuggingFrameworkPropertyAsync(frameworks[cmdIndex]); } + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.Equal(expected, command.ExecCommand(cmdIndex, EventArgs.Empty)); + + startupHelper.Verify(); + activeDebugFrameworkSvcs.Verify(); + } - [Theory] - [InlineData(-1, false)] - [InlineData(2, false)] - [InlineData(0, true)] - [InlineData(1, true)] - public void ExecCommand_MultipleStartupProjects_VerifyCorrectFrameworkSet(int cmdIndex, bool expected) + [Theory] + [InlineData(-1, false)] + [InlineData(2, false)] + [InlineData(0, true)] + [InlineData(1, true)] + public void ExecCommand_MultipleStartupProjects_VerifyCorrectFrameworkSet(int cmdIndex, bool expected) + { + var frameworks1 = new List() { "net461", "netcoreapp1.0" }; + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(frameworks1); + + var frameworks2 = new List() { "net461", "netcoreapp1.0" }; + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(frameworks2); + if (expected) { - var frameworks1 = new List() { "net461", "netcoreapp1.0" }; - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(frameworks1); - - var frameworks2 = new List() { "net461", "netcoreapp1.0" }; - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(frameworks2); - if (expected) - { - activeDebugFrameworkSvcs1.ImplementSetActiveDebuggingFrameworkPropertyAsync(frameworks1[cmdIndex]); - activeDebugFrameworkSvcs2.ImplementSetActiveDebuggingFrameworkPropertyAsync(frameworks2[cmdIndex]); - } - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.Equal(expected, command.ExecCommand(cmdIndex, EventArgs.Empty)); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); + activeDebugFrameworkSvcs1.ImplementSetActiveDebuggingFrameworkPropertyAsync(frameworks1[cmdIndex]); + activeDebugFrameworkSvcs2.ImplementSetActiveDebuggingFrameworkPropertyAsync(frameworks2[cmdIndex]); } + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - [Fact] - public void QueryStatus_HandleNoStartupProjects() - { - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Empty); + var command = CreateInstance(startupHelper.Object); + Assert.Equal(expected, command.ExecCommand(cmdIndex, EventArgs.Empty)); - var command = CreateInstance(startupHelper.Object); - Assert.False(command.QueryStatusCommand(0, EventArgs.Empty)); - startupHelper.Verify(); - } + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + } - [Fact] - public void QueryStatus_SingleStartupProject_NullFrameworks() - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(null); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs.Verify(); - } + [Fact] + public void QueryStatus_HandleNoStartupProjects() + { + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Empty); - [Theory] - [InlineData(false)] - [InlineData(true)] - public void QueryStatus_SingleStartupProject_LessThan2Frameworks(bool createList) - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(createList ? new List() { "netcoreapp1.0" } : null); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs.Verify(); - } + var command = CreateInstance(startupHelper.Object); + Assert.False(command.QueryStatusCommand(0, EventArgs.Empty)); + startupHelper.Verify(); + } - [Theory] - [InlineData(0, "netcoreapp1.0")] - [InlineData(1, "net461")] - [InlineData(2, "net461")] - [InlineData(2, "net462")] - public void QueryStatus_SingleStartupProject_TestValidFrameworkIndexes(int cmdIndex, string activeFramework) - { - var frameworks = new List() { "netcoreapp1.0", "net461", "net462" }; + [Fact] + public void QueryStatus_SingleStartupProject_NullFrameworks() + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(null); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs.Verify(); + } - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(frameworks) - .ImplementGetActiveDebuggingFrameworkPropertyAsync(activeFramework); + [Theory] + [InlineData(false)] + [InlineData(true)] + public void QueryStatus_SingleStartupProject_LessThan2Frameworks(bool createList) + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(createList ? new List() { "netcoreapp1.0" } : null); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs.Verify(); + } - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + [Theory] + [InlineData(0, "netcoreapp1.0")] + [InlineData(1, "net461")] + [InlineData(2, "net461")] + [InlineData(2, "net462")] + public void QueryStatus_SingleStartupProject_TestValidFrameworkIndexes(int cmdIndex, string activeFramework) + { + var frameworks = new List() { "netcoreapp1.0", "net461", "net462" }; - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(cmdIndex, EventArgs.Empty)); - Assert.True(command.Visible); - Assert.Equal(frameworks[cmdIndex], command.Text); - Assert.Equal(frameworks[cmdIndex] == activeFramework, command.Checked); - Assert.True(command.Enabled); + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(frameworks) + .ImplementGetActiveDebuggingFrameworkPropertyAsync(activeFramework); - startupHelper.Verify(); - activeDebugFrameworkSvcs.Verify(); - } + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - [Fact] - public void QueryStatus_MultipleStartupProjects_NullFrameworks() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(null); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(null); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); - } + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(cmdIndex, EventArgs.Empty)); + Assert.True(command.Visible); + Assert.Equal(frameworks[cmdIndex], command.Text); + Assert.Equal(frameworks[cmdIndex] == activeFramework, command.Checked); + Assert.True(command.Enabled); - [Theory] - [InlineData(false)] - [InlineData(true)] - public void QueryStatus_MultipleStartupProjects_LessThan2Frameworks(bool createList) - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(createList ? new List() { "netcoreapp1.0" } : null); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(createList ? new List() { "netcoreapp1.0" } : null); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); - } + startupHelper.Verify(); + activeDebugFrameworkSvcs.Verify(); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_OrderDifferent() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0", "net461" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_NullFrameworks() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(null); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(null); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_DifferentFrameworks() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net45", "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); - } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void QueryStatus_MultipleStartupProjects_LessThan2Frameworks(bool createList) + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(createList ? new List() { "netcoreapp1.0" } : null); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(createList ? new List() { "netcoreapp1.0" } : null); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_OneSingleFramework() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs3 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object, activeDebugFrameworkSvcs3.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); - Assert.False(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.False(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); - activeDebugFrameworkSvcs3.Verify(); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_OrderDifferent() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0", "net461" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + } - [Theory] - [InlineData(0, "netcoreapp1.0")] - [InlineData(1, "net461")] - [InlineData(2, "net461")] - [InlineData(2, "net462")] - public void QueryStatus_MultipleStartupProjects_TestValidFrameworkIndexes(int cmdIndex, string activeFramework) - { - var frameworks1 = new List() { "netcoreapp1.0", "net461", "net462" }; - - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(frameworks1) - .ImplementGetActiveDebuggingFrameworkPropertyAsync(activeFramework); - - var frameworks2 = new List() { "netcoreapp1.0", "net461", "net462" }; - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetProjectFrameworksAsync(frameworks2) - .ImplementGetActiveDebuggingFrameworkPropertyAsync(activeFramework); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = CreateInstance(startupHelper.Object); - Assert.True(command.QueryStatusCommand(cmdIndex, EventArgs.Empty)); - Assert.True(command.Visible); - Assert.Equal(frameworks1[cmdIndex], command.Text); - Assert.Equal(frameworks1[cmdIndex] == activeFramework, command.Checked); - Assert.Equal(frameworks2[cmdIndex], command.Text); - Assert.Equal(frameworks2[cmdIndex] == activeFramework, command.Checked); - Assert.True(command.Enabled); - - startupHelper.Verify(); - activeDebugFrameworkSvcs1.Verify(); - activeDebugFrameworkSvcs2.Verify(); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_DifferentFrameworks() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net45", "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + } - private static DebugFrameworksDynamicMenuCommand CreateInstance(IStartupProjectHelper startupProjectHelper) - { - return new DebugFrameworksDynamicMenuCommand(startupProjectHelper, new JoinableTaskContext()); + [Fact] + public void QueryStatus_MultipleStartupProjects_OneSingleFramework() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs3 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object, activeDebugFrameworkSvcs3.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(0, EventArgs.Empty)); + Assert.False(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.False(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + activeDebugFrameworkSvcs3.Verify(); + } + + [Theory] + [InlineData(0, "netcoreapp1.0")] + [InlineData(1, "net461")] + [InlineData(2, "net461")] + [InlineData(2, "net462")] + public void QueryStatus_MultipleStartupProjects_TestValidFrameworkIndexes(int cmdIndex, string activeFramework) + { + var frameworks1 = new List() { "netcoreapp1.0", "net461", "net462" }; + + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(frameworks1) + .ImplementGetActiveDebuggingFrameworkPropertyAsync(activeFramework); + + var frameworks2 = new List() { "netcoreapp1.0", "net461", "net462" }; + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetProjectFrameworksAsync(frameworks2) + .ImplementGetActiveDebuggingFrameworkPropertyAsync(activeFramework); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = CreateInstance(startupHelper.Object); + Assert.True(command.QueryStatusCommand(cmdIndex, EventArgs.Empty)); + Assert.True(command.Visible); + Assert.Equal(frameworks1[cmdIndex], command.Text); + Assert.Equal(frameworks1[cmdIndex] == activeFramework, command.Checked); + Assert.Equal(frameworks2[cmdIndex], command.Text); + Assert.Equal(frameworks2[cmdIndex] == activeFramework, command.Checked); + Assert.True(command.Enabled); + + startupHelper.Verify(); + activeDebugFrameworkSvcs1.Verify(); + activeDebugFrameworkSvcs2.Verify(); + } + + private static DebugFrameworksDynamicMenuCommand CreateInstance(IStartupProjectHelper startupProjectHelper) + { + return new DebugFrameworksDynamicMenuCommand(startupProjectHelper, new JoinableTaskContext()); #pragma warning restore VSSDK005 // Avoid instantiating JoinableTaskContext - } + } - private class TestDebugFrameworksDynamicMenuCommand : DebugFrameworksDynamicMenuCommand + private class TestDebugFrameworksDynamicMenuCommand : DebugFrameworksDynamicMenuCommand + { + public TestDebugFrameworksDynamicMenuCommand(IStartupProjectHelper startupHelper, JoinableTaskContext joinableTaskContext) + : base(startupHelper, joinableTaskContext) { - public TestDebugFrameworksDynamicMenuCommand(IStartupProjectHelper startupHelper, JoinableTaskContext joinableTaskContext) - : base(startupHelper, joinableTaskContext) - { - } } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdaterTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdaterTests.cs index 037b71e18b..732b518456 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdaterTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/DebugFrameworksMenuTextUpdaterTests.cs @@ -2,326 +2,325 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class DebugFrameworkMenuTextUpdaterTests { - public class DebugFrameworkMenuTextUpdaterTests + [Fact] + public void Exec_DoesNothing() { - [Fact] - public void Exec_DoesNothing() - { - var startupHelper = new Mock(); - var command = new DebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - DebugFrameworkPropertyMenuTextUpdater.ExecHandler(command, EventArgs.Empty); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + var startupHelper = new Mock(); + var command = new DebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + DebugFrameworkPropertyMenuTextUpdater.ExecHandler(command, EventArgs.Empty); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_NoStartupProjects() - { - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Empty); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_NoStartupProjects() + { + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Empty); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_SingleStartupProject_NullFrameworks() - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(null); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_SingleStartupProject_NullFrameworks() + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(null); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_SingleStartupProject_FrameworksLessThan2() - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(new List() { "net45" }); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_SingleStartupProject_FrameworksLessThan2() + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(new List() { "net45" }); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_SingleStartupProject_FrameworkNoActive() - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_SingleStartupProject_FrameworkNoActive() + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_SingleStartupProject_FrameworkNoMatchingActive() - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("net45") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_SingleStartupProject_FrameworkNoMatchingActive() + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("net45") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_SingleStartupProject_FrameworkValidActive() - { - var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "netcoreapp1.0"), command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_SingleStartupProject_FrameworkValidActive() + { + var activeDebugFrameworkSvcs = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "netcoreapp1.0"), command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_NullFrameworks() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(null); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(null); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_NullFrameworks() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(null); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(null); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_FrameworksLessThan2() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(new List() { "net45" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(new List() { "net45" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_FrameworksLessThan2() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(new List() { "net45" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(new List() { "net45" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_FrameworksOrderDifferent() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0", "net461" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_FrameworksOrderDifferent() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0", "net461" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_DifferentFrameworks() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net45", "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_DifferentFrameworks() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net45", "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_OneSingleFramework() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs3 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object, activeDebugFrameworkSvcs3.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal("", command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_OneSingleFramework() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs3 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object, activeDebugFrameworkSvcs3.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal("", command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_FrameworkNoActive() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_FrameworkNoActive() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync(null) + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_FrameworkNoMatchingActive() - { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("net45") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("net45") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); - } + [Fact] + public void QueryStatus_MultipleStartupProjects_FrameworkNoMatchingActive() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("net45") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("net45") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "net461"), command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } - [Fact] - public void QueryStatus_MultipleStartupProjects_FrameworkValidActive() + [Fact] + public void QueryStatus_MultipleStartupProjects_FrameworkValidActive() + { + var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() + .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") + .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); + + var startupHelper = new Mock(); + startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) + .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); + + var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); + command.QueryStatus(); + Assert.True(command.Visible); + Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "netcoreapp1.0"), command.Text); + Assert.False(command.Checked); + Assert.True(command.Enabled); + } + + private class TestDebugFrameworkPropertyMenuTextUpdater : DebugFrameworkPropertyMenuTextUpdater + { + public TestDebugFrameworkPropertyMenuTextUpdater(IStartupProjectHelper startupHelper) + : base(startupHelper) { - var activeDebugFrameworkSvcs1 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var activeDebugFrameworkSvcs2 = new IActiveDebugFrameworkServicesMock() - .ImplementGetActiveDebuggingFrameworkPropertyAsync("netcoreapp1.0") - .ImplementGetProjectFrameworksAsync(new List() { "net461", "netcoreapp1.0" }); - - var startupHelper = new Mock(); - startupHelper.Setup(x => x.GetExportFromDotNetStartupProjects(ProjectCapability.LaunchProfiles)) - .Returns(ImmutableArray.Create(activeDebugFrameworkSvcs1.Object, activeDebugFrameworkSvcs2.Object)); - - var command = new TestDebugFrameworkPropertyMenuTextUpdater(startupHelper.Object); - command.QueryStatus(); - Assert.True(command.Visible); - Assert.Equal(string.Format(VSResources.DebugFrameworkMenuText, "netcoreapp1.0"), command.Text); - Assert.False(command.Checked); - Assert.True(command.Enabled); } - private class TestDebugFrameworkPropertyMenuTextUpdater : DebugFrameworkPropertyMenuTextUpdater + protected override void ExecuteSynchronously(Func asyncFunction) { - public TestDebugFrameworkPropertyMenuTextUpdater(IStartupProjectHelper startupHelper) - : base(startupHelper) - { - } - - protected override void ExecuteSynchronously(Func asyncFunction) - { - asyncFunction.Invoke().Wait(); - } + asyncFunction.Invoke().Wait(); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommandTests.cs index c791f11a0c..672c904c00 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageProjectContextMenuCommandTests.cs @@ -4,55 +4,54 @@ using Microsoft.VisualStudio.ProjectSystem.Build; using Microsoft.VisualStudio.ProjectSystem.VS.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class GenerateNuGetPackageProjectContextMenuCommandTests : AbstractGenerateNuGetPackageCommandTests { - public class GenerateNuGetPackageProjectContextMenuCommandTests : AbstractGenerateNuGetPackageCommandTests - { - internal override long GetCommandId() => ManagedProjectSystemCommandId.GenerateNuGetPackageProjectContextMenu; + internal override long GetCommandId() => ManagedProjectSystemCommandId.GenerateNuGetPackageProjectContextMenu; - internal override AbstractGenerateNuGetPackageCommand CreateInstanceCore( - UnconfiguredProject project, - IProjectThreadingService threadingService, - ISolutionBuildManager solutionBuildManager, - GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) - { - return new GenerateNuGetPackageProjectContextMenuCommand(project, threadingService, solutionBuildManager, generatePackageOnBuildPropertyProvider); - } + internal override AbstractGenerateNuGetPackageCommand CreateInstanceCore( + UnconfiguredProject project, + IProjectThreadingService threadingService, + ISolutionBuildManager solutionBuildManager, + GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) + { + return new GenerateNuGetPackageProjectContextMenuCommand(project, threadingService, solutionBuildManager, generatePackageOnBuildPropertyProvider); + } - [Fact] - public async Task GetCommandStatusAsync_RootFolderAsNodes_ReturnsHandled() - { - var command = CreateInstance(); + [Fact] + public async Task GetCommandStatusAsync_RootFolderAsNodes_ReturnsHandled() + { + var command = CreateInstance(); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {AppDesignerFolder}) - """); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {AppDesignerFolder}) + """); - var nodes = ImmutableHashSet.Create(tree.Root); + var nodes = ImmutableHashSet.Create(tree.Root); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - Assert.True(result.Handled); - } + Assert.True(result.Handled); + } - [Fact] - public async Task GetCommandStatusAsync_NonRootFolderAsNodes_ReturnsUnhandled() - { - var command = CreateInstance(); + [Fact] + public async Task GetCommandStatusAsync_NonRootFolderAsNodes_ReturnsUnhandled() + { + var command = CreateInstance(); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {AppDesignerFolder}) - """); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {AppDesignerFolder}) + """); - var nodes = ImmutableHashSet.Create(tree.Children[0]); + var nodes = ImmutableHashSet.Create(tree.Children[0]); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - Assert.False(result.Handled); - } + Assert.False(result.Handled); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommandTests.cs index 3e8fd72916..94a2ddb132 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/GenerateNuGetPackageTopLevelBuildMenuCommandTests.cs @@ -4,55 +4,54 @@ using Microsoft.VisualStudio.ProjectSystem.Build; using Microsoft.VisualStudio.ProjectSystem.VS.Build; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class GenerateNuGetPackageTopLevelBuildMenuCommandTests : AbstractGenerateNuGetPackageCommandTests { - public class GenerateNuGetPackageTopLevelBuildMenuCommandTests : AbstractGenerateNuGetPackageCommandTests - { - internal override long GetCommandId() => ManagedProjectSystemCommandId.GenerateNuGetPackageTopLevelBuild; + internal override long GetCommandId() => ManagedProjectSystemCommandId.GenerateNuGetPackageTopLevelBuild; - internal override AbstractGenerateNuGetPackageCommand CreateInstanceCore( - UnconfiguredProject project, - IProjectThreadingService threadingService, - ISolutionBuildManager solutionBuildManager, - GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) - { - return new GenerateNuGetPackageTopLevelBuildMenuCommand(project, threadingService, solutionBuildManager, generatePackageOnBuildPropertyProvider); - } + internal override AbstractGenerateNuGetPackageCommand CreateInstanceCore( + UnconfiguredProject project, + IProjectThreadingService threadingService, + ISolutionBuildManager solutionBuildManager, + GeneratePackageOnBuildPropertyProvider generatePackageOnBuildPropertyProvider) + { + return new GenerateNuGetPackageTopLevelBuildMenuCommand(project, threadingService, solutionBuildManager, generatePackageOnBuildPropertyProvider); + } - [Fact] - public async Task GetCommandStatusAsync_RootFolderAsNodes_ReturnsHandled() - { - var command = CreateInstance(); + [Fact] + public async Task GetCommandStatusAsync_RootFolderAsNodes_ReturnsHandled() + { + var command = CreateInstance(); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {AppDesignerFolder}) - """); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {AppDesignerFolder}) + """); - var nodes = ImmutableHashSet.Create(tree.Root); + var nodes = ImmutableHashSet.Create(tree.Root); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - Assert.True(result.Handled); - } + Assert.True(result.Handled); + } - [Fact] - public async Task GetCommandStatusAsync_NonRootFolderAsNodes_ReturnsHandled() - { - var command = CreateInstance(); + [Fact] + public async Task GetCommandStatusAsync_NonRootFolderAsNodes_ReturnsHandled() + { + var command = CreateInstance(); - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}) - Properties (flags: {AppDesignerFolder}) - """); + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}) + Properties (flags: {AppDesignerFolder}) + """); - var nodes = ImmutableHashSet.Create(tree.Children[0]); + var nodes = ImmutableHashSet.Create(tree.Children[0]); - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - Assert.True(result.Handled); - } + Assert.True(result.Handled); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommandTests.cs index eb0d5f0b13..4a2d69af68 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerCommandTests.cs @@ -3,29 +3,28 @@ using Microsoft.VisualStudio.Input; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class OpenProjectDesignerCommandTests : AbstractOpenProjectDesignerCommandTests { - public class OpenProjectDesignerCommandTests : AbstractOpenProjectDesignerCommandTests + [Fact] + public void Constructor_NullAsDesignerService_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsDesignerService_ThrowsArgumentNull() + Assert.Throws("designerService", () => { - Assert.Throws("designerService", () => - { - new OpenProjectDesignerCommand(null!); - }); - } + new OpenProjectDesignerCommand(null!); + }); + } - internal override long GetCommandId() - { - return VisualStudioStandard97CommandId.Open; - } + internal override long GetCommandId() + { + return VisualStudioStandard97CommandId.Open; + } - internal override AbstractOpenProjectDesignerCommand CreateInstance(IProjectDesignerService? designerService = null) - { - designerService ??= IProjectDesignerServiceFactory.Create(); + internal override AbstractOpenProjectDesignerCommand CreateInstance(IProjectDesignerService? designerService = null) + { + designerService ??= IProjectDesignerServiceFactory.Create(); - return new OpenProjectDesignerCommand(designerService); - } + return new OpenProjectDesignerCommand(designerService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommandTests.cs index 47b65f623a..a13141e65d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/OpenProjectDesignerOnDefaultActionCommandTests.cs @@ -3,29 +3,28 @@ using Microsoft.VisualStudio.Input; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands; + +public class OpenProjectDesignerOnDefaultActionCommandTests : AbstractOpenProjectDesignerCommandTests { - public class OpenProjectDesignerOnDefaultActionCommandTests : AbstractOpenProjectDesignerCommandTests + [Fact] + public void Constructor_NullAsDesignerService_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsDesignerService_ThrowsArgumentNull() + Assert.Throws("designerService", () => { - Assert.Throws("designerService", () => - { - new OpenProjectDesignerOnDefaultActionCommand(null!); - }); - } + new OpenProjectDesignerOnDefaultActionCommand(null!); + }); + } - internal override long GetCommandId() - { - return UIHierarchyWindowCommandId.DoubleClick; - } + internal override long GetCommandId() + { + return UIHierarchyWindowCommandId.DoubleClick; + } - internal override AbstractOpenProjectDesignerCommand CreateInstance(IProjectDesignerService? designerService = null) - { - designerService ??= IProjectDesignerServiceFactory.Create(); + internal override AbstractOpenProjectDesignerCommand CreateInstance(IProjectDesignerService? designerService = null) + { + designerService ??= IProjectDesignerServiceFactory.Create(); - return new OpenProjectDesignerOnDefaultActionCommand(designerService); - } + return new OpenProjectDesignerOnDefaultActionCommand(designerService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommandTests.cs index 1b639a2026..1ae377b46f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/AbstractMoveCommandTests.cs @@ -1,53 +1,52 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +public abstract class AbstractMoveCommandTests { - public abstract class AbstractMoveCommandTests + [Fact] + public void Constructor_NullAsProjectTree_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstance(null!, SVsServiceProviderFactory.Create(), + ConfiguredProjectFactory.Create(), IProjectAccessorFactory.Create())); + } + + [Fact] + public void Constructor_NullAsSVsServiceProvider_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstance(IPhysicalProjectTreeFactory.Create(), null!, + ConfiguredProjectFactory.Create(), IProjectAccessorFactory.Create())); + } + + [Fact] + public void Constructor_NullAsConfiguredProject_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstance(IPhysicalProjectTreeFactory.Create(), + SVsServiceProviderFactory.Create(), null!, IProjectAccessorFactory.Create())); + } + + [Fact] + public void Constructor_NullAsAccessor_ThrowsArgumentNull() + { + Assert.Throws(() => CreateInstance(IPhysicalProjectTreeFactory.Create(), + SVsServiceProviderFactory.Create(), ConfiguredProjectFactory.Create(), null!)); + } + + internal abstract long GetCommandId(); + + internal AbstractMoveCommand CreateAbstractInstance( + IPhysicalProjectTree? projectTree = null, + Shell.SVsServiceProvider? serviceProvider = null, + ConfiguredProject? configuredProject = null, + IProjectAccessor? accessor = null) { - [Fact] - public void Constructor_NullAsProjectTree_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstance(null!, SVsServiceProviderFactory.Create(), - ConfiguredProjectFactory.Create(), IProjectAccessorFactory.Create())); - } - - [Fact] - public void Constructor_NullAsSVsServiceProvider_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstance(IPhysicalProjectTreeFactory.Create(), null!, - ConfiguredProjectFactory.Create(), IProjectAccessorFactory.Create())); - } - - [Fact] - public void Constructor_NullAsConfiguredProject_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstance(IPhysicalProjectTreeFactory.Create(), - SVsServiceProviderFactory.Create(), null!, IProjectAccessorFactory.Create())); - } - - [Fact] - public void Constructor_NullAsAccessor_ThrowsArgumentNull() - { - Assert.Throws(() => CreateInstance(IPhysicalProjectTreeFactory.Create(), - SVsServiceProviderFactory.Create(), ConfiguredProjectFactory.Create(), null!)); - } - - internal abstract long GetCommandId(); - - internal AbstractMoveCommand CreateAbstractInstance( - IPhysicalProjectTree? projectTree = null, - Shell.SVsServiceProvider? serviceProvider = null, - ConfiguredProject? configuredProject = null, - IProjectAccessor? accessor = null) - { - projectTree ??= IPhysicalProjectTreeFactory.Create(); - serviceProvider ??= SVsServiceProviderFactory.Create(); - configuredProject ??= ConfiguredProjectFactory.Create(); - accessor ??= IProjectAccessorFactory.Create(); - - return CreateInstance(projectTree, serviceProvider, configuredProject, accessor); - } - - internal abstract AbstractMoveCommand CreateInstance(IPhysicalProjectTree projectTree, Shell.SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor); + projectTree ??= IPhysicalProjectTreeFactory.Create(); + serviceProvider ??= SVsServiceProviderFactory.Create(); + configuredProject ??= ConfiguredProjectFactory.Create(); + accessor ??= IProjectAccessorFactory.Create(); + + return CreateInstance(projectTree, serviceProvider, configuredProject, accessor); } + + internal abstract AbstractMoveCommand CreateInstance(IPhysicalProjectTree projectTree, Shell.SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommandTests.cs index c44835c7b5..41d9c3ee13 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveDownCommandTests.cs @@ -2,177 +2,176 @@ using Microsoft.VisualStudio.Input; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +public class MoveDownCommandTests : AbstractMoveCommandTests { - public class MoveDownCommandTests : AbstractMoveCommandTests + [Fact] + public async Task GetCommandStatusAsync_File_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[0]); // test1.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_File_ReturnsStatusNinched() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[1]); // test2.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); + Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); + } + + [Fact] + public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[2].Children[0]); // test3.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusNinched() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[2].Children[1]); // test4.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); + Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); + } + + [Fact] + public async Task GetCommandStatusAsync_FolderOverFolder_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + Folder (flags: {Folder}), DisplayOrder: 6 + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 + File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[2]); // first folder + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_FolderOverFile_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + Folder (flags: {Folder}), DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 5 + Folder (flags: {Folder}), DisplayOrder: 6 + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 + File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[0]); // first folder + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_Folder_ReturnsStatusNinched() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + Folder (flags: {Folder}), DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3 + Folder (flags: {Folder}), DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 5 + File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 6 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[1]); // second folder + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); + Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); + } + + internal override long GetCommandId() => FSharpProjectCommandId.MoveDown; + + internal override AbstractMoveCommand CreateInstance(IPhysicalProjectTree projectTree, Shell.SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) { - [Fact] - public async Task GetCommandStatusAsync_File_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[0]); // test1.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_File_ReturnsStatusNinched() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[1]); // test2.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); - Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); - } - - [Fact] - public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[2].Children[0]); // test3.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusNinched() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[2].Children[1]); // test4.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); - Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); - } - - [Fact] - public async Task GetCommandStatusAsync_FolderOverFolder_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - Folder (flags: {Folder}), DisplayOrder: 6 - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 - File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[2]); // first folder - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_FolderOverFile_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - Folder (flags: {Folder}), DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 5 - Folder (flags: {Folder}), DisplayOrder: 6 - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 - File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[0]); // first folder - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_Folder_ReturnsStatusNinched() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - Folder (flags: {Folder}), DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3 - Folder (flags: {Folder}), DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 5 - File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 6 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[1]); // second folder - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); - Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); - } - - internal override long GetCommandId() => FSharpProjectCommandId.MoveDown; - - internal override AbstractMoveCommand CreateInstance(IPhysicalProjectTree projectTree, Shell.SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) - { - return new MoveDownCommand(projectTree, serviceProvider, configuredProject, accessor); - } + return new MoveDownCommand(projectTree, serviceProvider, configuredProject, accessor); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommandTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommandTests.cs index 83f271eddb..85544bda3c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommandTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/MoveUpCommandTests.cs @@ -2,177 +2,176 @@ using Microsoft.VisualStudio.Input; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +public class MoveUpCommandTests : AbstractMoveCommandTests { - public class MoveUpCommandTests : AbstractMoveCommandTests + [Fact] + public async Task GetCommandStatusAsync_File_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[1]); // test2.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_File_ReturnsStatusNinched() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[0]); // test1.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); + Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); + } + + [Fact] + public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[2].Children[1]); // test4.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusNinched() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[2].Children[0]); // test3.fs + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); + Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); + } + + [Fact] + public async Task GetCommandStatusAsync_FolderOverFolder_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + Folder (flags: {Folder}), DisplayOrder: 6 + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 + File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[3]); // second folder + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_FolderOverFile_ReturnsStatusEnabled() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 + Folder (flags: {Folder}), DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 + Folder (flags: {Folder}), DisplayOrder: 6 + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 + File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[2]); // first folder + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); + Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); + } + + [Fact] + public async Task GetCommandStatusAsync_Folder_ReturnsStatusNinched() + { + var command = CreateAbstractInstance(); + + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + Folder (flags: {Folder}), DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2 + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3 + Folder (flags: {Folder}), DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 5 + File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 6 + """); + + var nodes = ImmutableHashSet.Create(tree.Children[0]); // first folder + + var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); + + Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); + Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); + } + + internal override long GetCommandId() => FSharpProjectCommandId.MoveUp; + + internal override AbstractMoveCommand CreateInstance(IPhysicalProjectTree projectTree, Shell.SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) { - [Fact] - public async Task GetCommandStatusAsync_File_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[1]); // test2.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_File_ReturnsStatusNinched() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[0]); // test1.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); - Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); - } - - [Fact] - public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[2].Children[1]); // test4.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_FileInFolder_ReturnsStatusNinched() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[2].Children[0]); // test3.fs - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); - Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); - } - - [Fact] - public async Task GetCommandStatusAsync_FolderOverFolder_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - Folder (flags: {Folder}), DisplayOrder: 6 - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 - File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[3]); // second folder - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_FolderOverFile_ReturnsStatusEnabled() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2 - Folder (flags: {Folder}), DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 5 - Folder (flags: {Folder}), DisplayOrder: 6 - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 7 - File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 8 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[2]); // first folder - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Enabled)); - Assert.False(result.Status.HasFlag(CommandStatus.Ninched)); - } - - [Fact] - public async Task GetCommandStatusAsync_Folder_ReturnsStatusNinched() - { - var command = CreateAbstractInstance(); - - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - Folder (flags: {Folder}), DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2 - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3 - Folder (flags: {Folder}), DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 5 - File (flags: {}), FilePath: "C:\Foo\test6.fs", DisplayOrder: 6 - """); - - var nodes = ImmutableHashSet.Create(tree.Children[0]); // first folder - - var result = await command.GetCommandStatusAsync(nodes, GetCommandId(), true, "commandText", 0); - - Assert.True(result.Status.HasFlag(CommandStatus.Ninched)); - Assert.False(result.Status.HasFlag(CommandStatus.Enabled)); - } - - internal override long GetCommandId() => FSharpProjectCommandId.MoveUp; - - internal override AbstractMoveCommand CreateInstance(IPhysicalProjectTree projectTree, Shell.SVsServiceProvider serviceProvider, ConfiguredProject configuredProject, IProjectAccessor accessor) - { - return new MoveUpCommand(projectTree, serviceProvider, configuredProject, accessor); - } + return new MoveUpCommand(projectTree, serviceProvider, configuredProject, accessor); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelperTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelperTests.cs index 10c47e033d..5041322c2c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelperTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Input/Commands/Ordering/OrderingHelperTests.cs @@ -4,924 +4,923 @@ using Microsoft.Build.Evaluation; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering +namespace Microsoft.VisualStudio.ProjectSystem.VS.Input.Commands.Ordering; + +public class OrderingHelperTests { - public class OrderingHelperTests + private static void AssertEqualProject(string expected, Project project) { - private static void AssertEqualProject(string expected, Project project) - { - using var writer = new StringWriter(); + using var writer = new StringWriter(); - project.Save(writer); + project.Save(writer); - Assert.Equal(expected, writer.ToString()); - } + Assert.Equal(expected, writer.ToString()); + } - private static (string tempPath, string testPropsFile) CreateTempPropsFilePath() + private static (string tempPath, string testPropsFile) CreateTempPropsFilePath() + { + var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var testPropsFile = Path.Combine(tempPath, "test.props"); + return (tempPath, testPropsFile); + } + + private static Project CreateProjectWithImport(ProjectRootElement projectRootElement, ProjectRootElement projectImportElement, string tempPath, string testPropsFile) + { + try { - var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var testPropsFile = Path.Combine(tempPath, "test.props"); - return (tempPath, testPropsFile); - } + Directory.CreateDirectory(tempPath); + projectImportElement.Save(testPropsFile); - private static Project CreateProjectWithImport(ProjectRootElement projectRootElement, ProjectRootElement projectImportElement, string tempPath, string testPropsFile) + return new Project(projectRootElement); + } + finally { - try + if (Directory.Exists(tempPath)) { - Directory.CreateDirectory(tempPath); - projectImportElement.Save(testPropsFile); - - return new Project(projectRootElement); + Directory.Delete(tempPath, true); } - finally + + if (File.Exists(testPropsFile)) { - if (Directory.Exists(tempPath)) - { - Directory.Delete(tempPath, true); - } - - if (File.Exists(testPropsFile)) - { - File.Delete(testPropsFile); - } + File.Delete(testPropsFile); } } + } - [Fact] - public void MoveUpFile_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.True(OrderingHelper.TryMoveUp(project, tree.Children[1])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveUpFile_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.True(OrderingHelper.TryMoveUp(project, tree.Children[1])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveUpFile_IsUnsuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.False(OrderingHelper.TryMoveUp(project, tree.Children[0])); - Assert.False(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveUpFile_IsUnsuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.False(OrderingHelper.TryMoveUp(project, tree.Children[0])); + Assert.False(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveUpFolder_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test\test3.fs", DisplayOrder: 4, ItemName: "test/test3.fs" - File (flags: {}), FilePath: "C:\Foo\test\test4.fs", DisplayOrder: 5, ItemName: "test/test4.fs" - """); - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.True(OrderingHelper.TryMoveUp(project, tree.Children[2])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveUpFolder_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test\test3.fs", DisplayOrder: 4, ItemName: "test/test3.fs" + File (flags: {}), FilePath: "C:\Foo\test\test4.fs", DisplayOrder: 5, ItemName: "test/test4.fs" + """); + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.True(OrderingHelper.TryMoveUp(project, tree.Children[2])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveDownFile_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.True(OrderingHelper.TryMoveDown(project, tree.Children[0])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveDownFile_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.True(OrderingHelper.TryMoveDown(project, tree.Children[0])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveDownFile_IsUnsuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.False(OrderingHelper.TryMoveDown(project, tree.Children[1])); - Assert.False(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveDownFile_IsUnsuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.False(OrderingHelper.TryMoveDown(project, tree.Children[1])); + Assert.False(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveDownFolder_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test\test3.fs", DisplayOrder: 2, ItemName: "test/test3.fs" - File (flags: {}), FilePath: "C:\Foo\test\test4.fs", DisplayOrder: 3, ItemName: "test/test4.fs" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 4, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 5, ItemName: "test2.fs" - """); - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.True(OrderingHelper.TryMoveDown(project, tree.Children[0])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveDownFolder_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test\test3.fs", DisplayOrder: 2, ItemName: "test/test3.fs" + File (flags: {}), FilePath: "C:\Foo\test\test4.fs", DisplayOrder: 3, ItemName: "test/test4.fs" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 4, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 5, ItemName: "test2.fs" + """); + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.True(OrderingHelper.TryMoveDown(project, tree.Children[0])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveDownFolder_ContainsNestedFolder_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 1 - File (flags: {}), FilePath: "C:\Foo\test\test3.fs", DisplayOrder: 2, ItemName: "test/test3.fs" - Folder (flags: {Folder}), FilePath: "C:\Foo\test\nested", DisplayOrder: 3 - File (flags: {}), FilePath: "C:\Foo\test\nested\nested.fs", DisplayOrder: 4, ItemName: "test/nested/nested.fs" - File (flags: {}), FilePath: "C:\Foo\test\test4.fs", DisplayOrder: 5, ItemName: "test/test4.fs" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 6, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 7, ItemName: "test2.fs" - """); - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - Assert.True(OrderingHelper.TryMoveDown(project, tree.Children[0])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveDownFolder_ContainsNestedFolder_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 1 + File (flags: {}), FilePath: "C:\Foo\test\test3.fs", DisplayOrder: 2, ItemName: "test/test3.fs" + Folder (flags: {Folder}), FilePath: "C:\Foo\test\nested", DisplayOrder: 3 + File (flags: {}), FilePath: "C:\Foo\test\nested\nested.fs", DisplayOrder: 4, ItemName: "test/nested/nested.fs" + File (flags: {}), FilePath: "C:\Foo\test\test4.fs", DisplayOrder: 5, ItemName: "test/test4.fs" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 6, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 7, ItemName: "test2.fs" + """); + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + Assert.True(OrderingHelper.TryMoveDown(project, tree.Children[0])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveAboveFile_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - var elements = OrderingHelper.GetItemElements(project, tree.Children[0], ImmutableArray.Empty); - - Assert.True(OrderingHelper.TryMoveElementsAbove(project, elements, tree.Children[2])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveAboveFile_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + var elements = OrderingHelper.GetItemElements(project, tree.Children[0], ImmutableArray.Empty); + + Assert.True(OrderingHelper.TryMoveElementsAbove(project, elements, tree.Children[2])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveBelowFile_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - var elements = OrderingHelper.GetItemElements(project, tree.Children[0], ImmutableArray.Empty); - - Assert.True(OrderingHelper.TryMoveElementsBelow(project, elements, tree.Children[2])); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveBelowFile_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + var elements = OrderingHelper.GetItemElements(project, tree.Children[0], ImmutableArray.Empty); + + Assert.True(OrderingHelper.TryMoveElementsBelow(project, elements, tree.Children[2])); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void AddItem_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" - """); - - var updatedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3, ItemName: "test4.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4, ItemName: "test3.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - var elements = OrderingHelper.GetItemElements(project, updatedTree.Children[2], ImmutableArray.Empty); - - Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void AddItem_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" + """); + + var updatedTree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3, ItemName: "test4.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 4, ItemName: "test3.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + var elements = OrderingHelper.GetItemElements(project, updatedTree.Children[2], ImmutableArray.Empty); + + Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void AddItems_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" - """); - - var updatedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3, ItemName: "test4.fs" - File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 4, ItemName: "test5.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 5, ItemName: "test3.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - var elements = - OrderingHelper.GetItemElements(project, updatedTree.Children[2], ImmutableArray.Empty) - .AddRange(OrderingHelper.GetItemElements(project, updatedTree.Children[3], ImmutableArray.Empty)); - - Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void AddItems_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" + """); + + var updatedTree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test4.fs", DisplayOrder: 3, ItemName: "test4.fs" + File (flags: {}), FilePath: "C:\Foo\test5.fs", DisplayOrder: 4, ItemName: "test5.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 5, ItemName: "test3.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + var elements = + OrderingHelper.GetItemElements(project, updatedTree.Children[2], ImmutableArray.Empty) + .AddRange(OrderingHelper.GetItemElements(project, updatedTree.Children[3], ImmutableArray.Empty)); + + Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void AddItemsInNestedFolder_IsSuccessful() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - Folder (flags: {Folder}), FilePath: "C:\Foo\test" - Folder (flags: {Folder}), FilePath: "C:\Foo\test\nested" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" - """); - - var updatedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 3 - Folder (flags: {Folder}), FilePath: "C:\Foo\test\nested", DisplayOrder: 4 - File (flags: {}), FilePath: "C:\Foo\test\nested\test4.fs", DisplayOrder: 5, ItemName: "test\nested\test4.fs" - File (flags: {}), FilePath: "C:\Foo\test\tested\test5.fs", DisplayOrder: 6, ItemName: "test\nested\test5.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 7, ItemName: "test3.fs" - """); - - var projectRootElement = - """ - - - - netstandard2.0 - - - - - - - - - - - - """.AsProjectRootElement(); - - var project = new Project(projectRootElement); - - var elements = - OrderingHelper.GetItemElements(project, updatedTree.Children[2].Children[0].Children[0], ImmutableArray.Empty) - .AddRange(OrderingHelper.GetItemElements(project, updatedTree.Children[2].Children[0].Children[1], ImmutableArray.Empty)); - - Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); - Assert.True(project.IsDirty); - - var expected = - """ - - - - netstandard2.0 - - - - - - - - - - """; - - AssertEqualProject(expected, project); - } + [Fact] + public void AddItemsInNestedFolder_IsSuccessful() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + Folder (flags: {Folder}), FilePath: "C:\Foo\test" + Folder (flags: {Folder}), FilePath: "C:\Foo\test\nested" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 3, ItemName: "test3.fs" + """); + + var updatedTree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + Folder (flags: {Folder}), FilePath: "C:\Foo\test", DisplayOrder: 3 + Folder (flags: {Folder}), FilePath: "C:\Foo\test\nested", DisplayOrder: 4 + File (flags: {}), FilePath: "C:\Foo\test\nested\test4.fs", DisplayOrder: 5, ItemName: "test\nested\test4.fs" + File (flags: {}), FilePath: "C:\Foo\test\tested\test5.fs", DisplayOrder: 6, ItemName: "test\nested\test5.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 7, ItemName: "test3.fs" + """); + + var projectRootElement = + """ + + + + netstandard2.0 + + + + + + + + + + + + """.AsProjectRootElement(); + + var project = new Project(projectRootElement); + + var elements = + OrderingHelper.GetItemElements(project, updatedTree.Children[2].Children[0].Children[0], ImmutableArray.Empty) + .AddRange(OrderingHelper.GetItemElements(project, updatedTree.Children[2].Children[0].Children[1], ImmutableArray.Empty)); + + Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); + Assert.True(project.IsDirty); + + var expected = + """ + + + + netstandard2.0 + + + + + + + + + + """; + + AssertEqualProject(expected, project); + } - [Fact] - public void AddFile_WithImportedFileAtTop() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - """); - - var updatedTree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2, ItemName: "test3.fs" - """); - - var (tempPath, testPropsFile) = CreateTempPropsFilePath(); - - var projectRootElement = string.Format( - """ - - - - netstandard2.0 - - - - - - - - - - - """, testPropsFile).AsProjectRootElement(); - - var projectImportElement = - """ - - - - - - """.AsProjectRootElement(); - - var project = CreateProjectWithImport(projectRootElement, projectImportElement, tempPath, testPropsFile); - - var elements = - OrderingHelper.GetItemElements(project, updatedTree.Children[0], ImmutableArray.Empty) - .AddRange(OrderingHelper.GetItemElements(project, updatedTree.Children[2], ImmutableArray.Empty)); - - Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); - Assert.True(project.IsDirty); - - var expected = string.Format( - """ - - - - netstandard2.0 - - - - - - - - """, testPropsFile); - - AssertEqualProject(expected, project); - } + [Fact] + public void AddFile_WithImportedFileAtTop() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + """); + + var updatedTree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2, ItemName: "test3.fs" + """); + + var (tempPath, testPropsFile) = CreateTempPropsFilePath(); + + var projectRootElement = string.Format( + """ + + + + netstandard2.0 + + + + + + + + + + + """, testPropsFile).AsProjectRootElement(); + + var projectImportElement = + """ + + + + + + """.AsProjectRootElement(); + + var project = CreateProjectWithImport(projectRootElement, projectImportElement, tempPath, testPropsFile); + + var elements = + OrderingHelper.GetItemElements(project, updatedTree.Children[0], ImmutableArray.Empty) + .AddRange(OrderingHelper.GetItemElements(project, updatedTree.Children[2], ImmutableArray.Empty)); + + Assert.True(OrderingHelper.TryMoveElementsToTop(project, elements, tree), "TryMoveElementsToTop returned false."); + Assert.True(project.IsDirty); + + var expected = string.Format( + """ + + + + netstandard2.0 + + + + + + + + """, testPropsFile); + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveFileUp_WithImportedFileInterspersed() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2, ItemName: "test3.fs" - """); - - var (tempPath, testPropsFile) = CreateTempPropsFilePath(); - - var projectRootElement = string.Format( - """ - - - - netstandard2.0 - - - - - - - - - - - - - - """, testPropsFile).AsProjectRootElement(); - - var projectImportElement = - """ - - - - - - """.AsProjectRootElement(); - - var project = CreateProjectWithImport(projectRootElement, projectImportElement, tempPath, testPropsFile); - - Assert.True(OrderingHelper.TryMoveUp(project, tree.Children[2])); - Assert.True(project.IsDirty); - - // The expected result here may not be the desired behavior, but it is the current behavior that we need to test for. - // Moving test3.fs up, skips the import, but also moves above test1.fs, that is due to skipping imports during manipulation. - var expected = string.Format( - """ - - - - netstandard2.0 - - - - - - - - - """, testPropsFile); - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveFileUp_WithImportedFileInterspersed() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2, ItemName: "test3.fs" + """); + + var (tempPath, testPropsFile) = CreateTempPropsFilePath(); + + var projectRootElement = string.Format( + """ + + + + netstandard2.0 + + + + + + + + + + + + + + """, testPropsFile).AsProjectRootElement(); + + var projectImportElement = + """ + + + + + + """.AsProjectRootElement(); + + var project = CreateProjectWithImport(projectRootElement, projectImportElement, tempPath, testPropsFile); + + Assert.True(OrderingHelper.TryMoveUp(project, tree.Children[2])); + Assert.True(project.IsDirty); + + // The expected result here may not be the desired behavior, but it is the current behavior that we need to test for. + // Moving test3.fs up, skips the import, but also moves above test1.fs, that is due to skipping imports during manipulation. + var expected = string.Format( + """ + + + + netstandard2.0 + + + + + + + + + """, testPropsFile); + + AssertEqualProject(expected, project); + } - [Fact] - public void MoveFileDown_WithImportedFileAtBottom() - { - var tree = ProjectTreeParser.Parse( - """ - Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" - File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" - File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" - File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2, ItemName: "test3.fs" - """); - - var (tempPath, testPropsFile) = CreateTempPropsFilePath(); - - var projectRootElement = string.Format( - """ - - - - netstandard2.0 - - - - - - - - - - - """, testPropsFile).AsProjectRootElement(); - - var projectImportElement = - """ - - - - - - """.AsProjectRootElement(); - - var project = CreateProjectWithImport(projectRootElement, projectImportElement, tempPath, testPropsFile); - - // Assert false as nothing should change because we can't move over an import file that is at the very bottom. - Assert.False(OrderingHelper.TryMoveDown(project, tree.Children[1])); - Assert.False(project.IsDirty); - - var expected = string.Format( - """ - - - - netstandard2.0 - - - - - - - - """, testPropsFile); - - AssertEqualProject(expected, project); - } + [Fact] + public void MoveFileDown_WithImportedFileAtBottom() + { + var tree = ProjectTreeParser.Parse( + """ + Root (flags: {ProjectRoot}), FilePath: "C:\Foo\testing.fsproj" + File (flags: {}), FilePath: "C:\Foo\test1.fs", DisplayOrder: 1, ItemName: "test1.fs" + File (flags: {}), FilePath: "C:\Foo\test2.fs", DisplayOrder: 2, ItemName: "test2.fs" + File (flags: {}), FilePath: "C:\Foo\test3.fs", DisplayOrder: 2, ItemName: "test3.fs" + """); + + var (tempPath, testPropsFile) = CreateTempPropsFilePath(); + + var projectRootElement = string.Format( + """ + + + + netstandard2.0 + + + + + + + + + + + """, testPropsFile).AsProjectRootElement(); + + var projectImportElement = + """ + + + + + + """.AsProjectRootElement(); + + var project = CreateProjectWithImport(projectRootElement, projectImportElement, tempPath, testPropsFile); + + // Assert false as nothing should change because we can't move over an import file that is at the very bottom. + Assert.False(OrderingHelper.TryMoveDown(project, tree.Children[1])); + Assert.False(project.IsDirty); + + var expected = string.Format( + """ + + + + netstandard2.0 + + + + + + + + """, testPropsFile); + + AssertEqualProject(expected, project); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/ActiveEditorContextTrackerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/ActiveEditorContextTrackerTests.cs index 8b246422b7..3c09fac123 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/ActiveEditorContextTrackerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/ActiveEditorContextTrackerTests.cs @@ -3,203 +3,202 @@ using Microsoft.VisualStudio.ProjectSystem.LanguageServices; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; + +public class ActiveEditorContextTrackerTests { - public class ActiveEditorContextTrackerTests + [Fact] + public void IsActiveEditorContext_NullAsContextId_ThrowsArgumentNull() { - [Fact] - public void IsActiveEditorContext_NullAsContextId_ThrowsArgumentNull() + var instance = CreateInstance(); + + Assert.Throws("contextId", () => { - var instance = CreateInstance(); + instance.IsActiveEditorContext(null!); + }); + } - Assert.Throws("contextId", () => - { - instance.IsActiveEditorContext(null!); - }); - } + [Fact] + public void RegisterContext_EmptyAsContextId_ThrowsArgument() + { + var instance = CreateInstance(); - [Fact] - public void RegisterContext_EmptyAsContextId_ThrowsArgument() - { - var instance = CreateInstance(); - - Assert.Throws("contextId", () => - { - instance.RegisterContext(string.Empty); - }); - } - - [Theory] - [InlineData(VSConstants.VSITEMID_NIL)] - [InlineData(VSConstants.VSITEMID_SELECTION)] - [InlineData(0)] - public void GetProjectName_InvalidIdAsItemId_ReturnsInvalidArg(uint itemid) + Assert.Throws("contextId", () => { - var instance = CreateInstance(); + instance.RegisterContext(string.Empty); + }); + } - int result = instance.GetProjectName(itemid, out string? projectNameResult); + [Theory] + [InlineData(VSConstants.VSITEMID_NIL)] + [InlineData(VSConstants.VSITEMID_SELECTION)] + [InlineData(0)] + public void GetProjectName_InvalidIdAsItemId_ReturnsInvalidArg(uint itemid) + { + var instance = CreateInstance(); - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Null(projectNameResult); - } + int result = instance.GetProjectName(itemid, out string? projectNameResult); - [Fact] - public void IsActiveEditorContext_NotRegisteredContextAsContextId_ThrowsInvalidOperation() - { - var instance = CreateInstance(); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Null(projectNameResult); + } - Assert.Throws(() => - { - instance.IsActiveEditorContext("NotRegistered"); - }); - } + [Fact] + public void IsActiveEditorContext_NotRegisteredContextAsContextId_ThrowsInvalidOperation() + { + var instance = CreateInstance(); - [Fact] - public void RegisteredContext_AlreadyRegisteredContextAsContextId_ThrowsInvalidOperation() + Assert.Throws(() => { - var instance = CreateInstance(); + instance.IsActiveEditorContext("NotRegistered"); + }); + } - instance.RegisterContext("ContextId"); + [Fact] + public void RegisteredContext_AlreadyRegisteredContextAsContextId_ThrowsInvalidOperation() + { + var instance = CreateInstance(); - Assert.Throws(() => - { - instance.RegisterContext("ContextId"); - }); - } + instance.RegisterContext("ContextId"); - [Fact] - public void UnregisterContext_RegisteredContextAsContextId_CanUnregister() + Assert.Throws(() => { - var instance = CreateInstance(); + instance.RegisterContext("ContextId"); + }); + } - var registration = instance.RegisterContext("ContextId"); + [Fact] + public void UnregisterContext_RegisteredContextAsContextId_CanUnregister() + { + var instance = CreateInstance(); - registration.Dispose(); + var registration = instance.RegisterContext("ContextId"); - // Should be unregistered - Assert.Throws(() => instance.IsActiveEditorContext("ContextId")); - } + registration.Dispose(); - [Theory] - [InlineData("AnotherContextId")] - [InlineData("contextId")] // Case-sensitive - public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdNotSet_UsesFirstRegisteredContext(string contextId) - { - var instance = CreateInstance(); + // Should be unregistered + Assert.Throws(() => instance.IsActiveEditorContext("ContextId")); + } - instance.RegisterContext("ContextId"); - instance.RegisterContext(contextId); + [Theory] + [InlineData("AnotherContextId")] + [InlineData("contextId")] // Case-sensitive + public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdNotSet_UsesFirstRegisteredContext(string contextId) + { + var instance = CreateInstance(); - var result = instance.IsActiveEditorContext("ContextId"); + instance.RegisterContext("ContextId"); + instance.RegisterContext(contextId); - Assert.True(result); - } + var result = instance.IsActiveEditorContext("ContextId"); - [Theory] - [InlineData("AnotherContextId")] - [InlineData("contextId")] // Case-sensitive - public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdSetToNull_UsesFirstRegisteredContext(string contextId) - { - var instance = CreateInstance(); + Assert.True(result); + } - instance.RegisterContext("FirstContextId"); - instance.RegisterContext(contextId); + [Theory] + [InlineData("AnotherContextId")] + [InlineData("contextId")] // Case-sensitive + public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdSetToNull_UsesFirstRegisteredContext(string contextId) + { + var instance = CreateInstance(); - // Set it the value first - instance.ActiveIntellisenseProjectContext = contextId; + instance.RegisterContext("FirstContextId"); + instance.RegisterContext(contextId); - // Now explicitly set to null - instance.ActiveIntellisenseProjectContext = null; + // Set it the value first + instance.ActiveIntellisenseProjectContext = contextId; - var result = instance.IsActiveEditorContext("FirstContextId"); + // Now explicitly set to null + instance.ActiveIntellisenseProjectContext = null; - Assert.True(result); - } + var result = instance.IsActiveEditorContext("FirstContextId"); - [Theory] - [InlineData("")] - [InlineData("AnotherContextId")] - [InlineData("contextId")] // Case-sensitive - public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdDoesNotMatch_ReturnsFalse(string activeIntellisenseProjectContextId) - { - var instance = CreateInstance(); + Assert.True(result); + } - instance.RegisterContext("ContextId"); + [Theory] + [InlineData("")] + [InlineData("AnotherContextId")] + [InlineData("contextId")] // Case-sensitive + public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdDoesNotMatch_ReturnsFalse(string activeIntellisenseProjectContextId) + { + var instance = CreateInstance(); - instance.ActiveIntellisenseProjectContext = activeIntellisenseProjectContextId; + instance.RegisterContext("ContextId"); - var result = instance.IsActiveEditorContext("ContextId"); + instance.ActiveIntellisenseProjectContext = activeIntellisenseProjectContextId; - Assert.False(result); - } + var result = instance.IsActiveEditorContext("ContextId"); - [Fact] - public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdMatches_ReturnsTrue() - { - var instance = CreateInstance(); + Assert.False(result); + } - instance.RegisterContext("ContextId"); + [Fact] + public void IsActiveEditorContext_WhenActiveIntellisenseProjectContextIdMatches_ReturnsTrue() + { + var instance = CreateInstance(); - instance.ActiveIntellisenseProjectContext = "ContextId"; + instance.RegisterContext("ContextId"); - var result = instance.IsActiveEditorContext("ContextId"); + instance.ActiveIntellisenseProjectContext = "ContextId"; - Assert.True(result); - } + var result = instance.IsActiveEditorContext("ContextId"); - [Theory] - [InlineData("")] - [InlineData("ContextId")] - public void GetProjectName_ReturnsActiveIntellisenseProjectContextId(string activeIntellisenseProjectContextId) - { - var instance = CreateInstance(); + Assert.True(result); + } - instance.ActiveIntellisenseProjectContext = activeIntellisenseProjectContextId; + [Theory] + [InlineData("")] + [InlineData("ContextId")] + public void GetProjectName_ReturnsActiveIntellisenseProjectContextId(string activeIntellisenseProjectContextId) + { + var instance = CreateInstance(); - instance.GetProjectName(HierarchyId.Root, out string? result); + instance.ActiveIntellisenseProjectContext = activeIntellisenseProjectContextId; - Assert.Equal(activeIntellisenseProjectContextId, result); - } + instance.GetProjectName(HierarchyId.Root, out string? result); - [Theory] - [InlineData("AnotherContextId")] - [InlineData("contextId")] - public void GetProjectName_WhenActiveIntellisenseProjectContextIdNotSet_ReturnsFirstRegisteredContext(string contextId) - { - var instance = CreateInstance(); + Assert.Equal(activeIntellisenseProjectContextId, result); + } - instance.RegisterContext("FirstContextId"); - instance.RegisterContext(contextId); + [Theory] + [InlineData("AnotherContextId")] + [InlineData("contextId")] + public void GetProjectName_WhenActiveIntellisenseProjectContextIdNotSet_ReturnsFirstRegisteredContext(string contextId) + { + var instance = CreateInstance(); - instance.GetProjectName(HierarchyId.Root, out string? result); + instance.RegisterContext("FirstContextId"); + instance.RegisterContext(contextId); - Assert.Equal("FirstContextId", result); - } + instance.GetProjectName(HierarchyId.Root, out string? result); - [Theory] - [InlineData("AnotherContextId")] - [InlineData("contextId")] - public void GetProjectName_WhenActiveIntellisenseProjectContextIdSetToNull_ReturnsFirstRegisteredContext(string contextId) - { - var instance = CreateInstance(); + Assert.Equal("FirstContextId", result); + } - instance.RegisterContext("FirstContextId"); - instance.RegisterContext(contextId); + [Theory] + [InlineData("AnotherContextId")] + [InlineData("contextId")] + public void GetProjectName_WhenActiveIntellisenseProjectContextIdSetToNull_ReturnsFirstRegisteredContext(string contextId) + { + var instance = CreateInstance(); - // Set it the value first - instance.ActiveIntellisenseProjectContext = contextId; + instance.RegisterContext("FirstContextId"); + instance.RegisterContext(contextId); - // Now explicitly set to null - instance.ActiveIntellisenseProjectContext = null; + // Set it the value first + instance.ActiveIntellisenseProjectContext = contextId; - instance.GetProjectName(HierarchyId.Root, out string? result); + // Now explicitly set to null + instance.ActiveIntellisenseProjectContext = null; - Assert.Equal("FirstContextId", result); - } + instance.GetProjectName(HierarchyId.Root, out string? result); - private static VsActiveEditorContextTracker CreateInstance() - { - return new VsActiveEditorContextTracker(null, new ActiveEditorContextTracker(null)); - } + Assert.Equal("FirstContextId", result); + } + + private static VsActiveEditorContextTracker CreateInstance() + { + return new VsActiveEditorContextTracker(null, new ActiveEditorContextTracker(null)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProviderTests.cs index 587adc11f9..9ce41f3bfb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpCodeDomProviderTests.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp; + +public class CSharpCodeDomProviderTests { - public class CSharpCodeDomProviderTests + [Fact] + public void Constructor_DoesNotThrow() { - [Fact] - public void Constructor_DoesNotThrow() - { - new CSharpCodeDomProvider(); - } + new CSharpCodeDomProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProviderTests.cs index ba8809f48f..8877eb18e2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/CSharp/CSharpLanguageFeaturesProviderTests.cs @@ -1,145 +1,144 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.CSharp; + +public class CSharpLanguageFeaturesProviderTests { - public class CSharpLanguageFeaturesProviderTests + [Fact] + public void Constructor_DoesNotThrow() { - [Fact] - public void Constructor_DoesNotThrow() - { - new CSharpLanguageFeaturesProvider(); - } + new CSharpLanguageFeaturesProvider(); + } + + [Fact] + public void MakeProperIdentifier_NullAsName_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public void MakeProperIdentifier_NullAsName_ThrowsArgumentNull() + Assert.Throws("name", () => { - var provider = CreateInstance(); + provider.MakeProperIdentifier(null!); + }); + } - Assert.Throws("name", () => - { - provider.MakeProperIdentifier(null!); - }); - } + [Fact] + public void MakeProperIdentifier_EmptyAsName_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public void MakeProperIdentifier_EmptyAsName_ThrowsArgument() + Assert.Throws("name", () => { - var provider = CreateInstance(); + provider.MakeProperIdentifier(""); + }); + } - Assert.Throws("name", () => - { - provider.MakeProperIdentifier(""); - }); - } + [Fact] + public void MakeProperNamespace_NullAsName_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public void MakeProperNamespace_NullAsName_ThrowsArgumentNull() + Assert.Throws("name", () => { - var provider = CreateInstance(); + provider.MakeProperNamespace(null!); + }); + } - Assert.Throws("name", () => - { - provider.MakeProperNamespace(null!); - }); - } + [Fact] + public void MakeProperNamespace_EmptyAsName_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public void MakeProperNamespace_EmptyAsName_ThrowsArgument() - { - var provider = CreateInstance(); - - Assert.Throws("name", () => - { - provider.MakeProperNamespace(""); - }); - } - - [Theory] // Input // Expected - [InlineData("_" , "_")] - [InlineData("_._", "_._")] - [InlineData("A" , "A")] - [InlineData(" " , "_")] - [InlineData(" " , "__")] - [InlineData("A." , "A")] - [InlineData("A123" , "A123")] - [InlineData("A" , "A_T_")] - [InlineData("A_1" , "A_1")] - [InlineData("A.B", "A.B")] - [InlineData("Microsoft.VisualStudio.ProjectSystem", "Microsoft.VisualStudio.ProjectSystem")] - [InlineData(".A", "A")] - [InlineData(".A.", "A")] - [InlineData("..A.", "A")] - [InlineData("A....A.", "A.A")] - [InlineData("A B", "A_B")] - [InlineData("1.2", "_1._2")] - [InlineData("A.B and C", "A.B_and_C")] - [InlineData("A,B and C", "A_B_and_C")] - [InlineData("\u1234", "\u1234")] - [InlineData("\u00C6sh", "\u00C6sh")] - [InlineData("my\u034Fvery\u034Flong\u034Fidentifier", "my\u034Fvery\u034Flong\u034Fidentifier")] // COMBINING GRAPHEME JOINERs, not actual spaces - public void MakeProperNamespace_ValuesAsName_ReturnsProperNamespace(string name, string expected) + Assert.Throws("name", () => { - var provider = CreateInstance(); + provider.MakeProperNamespace(""); + }); + } - string result = provider.MakeProperNamespace(name); + [Theory] // Input // Expected + [InlineData("_" , "_")] + [InlineData("_._", "_._")] + [InlineData("A" , "A")] + [InlineData(" " , "_")] + [InlineData(" " , "__")] + [InlineData("A." , "A")] + [InlineData("A123" , "A123")] + [InlineData("A" , "A_T_")] + [InlineData("A_1" , "A_1")] + [InlineData("A.B", "A.B")] + [InlineData("Microsoft.VisualStudio.ProjectSystem", "Microsoft.VisualStudio.ProjectSystem")] + [InlineData(".A", "A")] + [InlineData(".A.", "A")] + [InlineData("..A.", "A")] + [InlineData("A....A.", "A.A")] + [InlineData("A B", "A_B")] + [InlineData("1.2", "_1._2")] + [InlineData("A.B and C", "A.B_and_C")] + [InlineData("A,B and C", "A_B_and_C")] + [InlineData("\u1234", "\u1234")] + [InlineData("\u00C6sh", "\u00C6sh")] + [InlineData("my\u034Fvery\u034Flong\u034Fidentifier", "my\u034Fvery\u034Flong\u034Fidentifier")] // COMBINING GRAPHEME JOINERs, not actual spaces + public void MakeProperNamespace_ValuesAsName_ReturnsProperNamespace(string name, string expected) + { + var provider = CreateInstance(); - Assert.Equal(expected, result); - } + string result = provider.MakeProperNamespace(name); - [Fact] - public void ConcatNamespaces_NullAsNamespaceNames_ThrowsArgumentNull() - { - var provider = CreateInstance(); + Assert.Equal(expected, result); + } - Assert.Throws("namespaceNames", () => - { - provider.ConcatNamespaces(null!); - }); - } + [Fact] + public void ConcatNamespaces_NullAsNamespaceNames_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public void ConcatNamespaces_EmptyArrayAsNamespaceNames_ThrowsArgument() + Assert.Throws("namespaceNames", () => { - var provider = CreateInstance(); + provider.ConcatNamespaces(null!); + }); + } - Assert.Throws("namespaceNames", () => - { - provider.ConcatNamespaces(new string[0]); - }); - } + [Fact] + public void ConcatNamespaces_EmptyArrayAsNamespaceNames_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public void ConcatNamespaces_ArrayWithNullAsNamespaceNames_ThrowsArgument() + Assert.Throws("namespaceNames", () => { - var provider = CreateInstance(); - - Assert.Throws("namespaceNames", () => - { - provider.ConcatNamespaces(new string[] { null! }); - }); - } - - [Theory] // Input // Expected - [InlineData(new[] { "" }, "")] - [InlineData(new[] { "A" }, "A")] - [InlineData(new[] { "A", "B"}, "A.B")] - [InlineData(new[] { "A", "B", "C"}, "A.B.C")] - [InlineData(new[] { "Microsoft", "VisualStudio", "ProjectSystem"}, "Microsoft.VisualStudio.ProjectSystem")] - [InlineData(new[] { "Microsoft.VisualStudio", "ProjectSystem"}, "Microsoft.VisualStudio.ProjectSystem")] - [InlineData(new[] { "", "Microsoft", "VisualStudio", "ProjectSystem"}, "Microsoft.VisualStudio.ProjectSystem")] - [InlineData(new[] { "Microsoft", "", "ProjectSystem"}, "Microsoft.ProjectSystem")] - public void ConcatNamespaces_ValuesAsNamespacesName_ReturnsConcatenatedNamespaces(string[] namespaceNames, string expected) + provider.ConcatNamespaces(new string[0]); + }); + } + + [Fact] + public void ConcatNamespaces_ArrayWithNullAsNamespaceNames_ThrowsArgument() + { + var provider = CreateInstance(); + + Assert.Throws("namespaceNames", () => { - var provider = CreateInstance(); + provider.ConcatNamespaces(new string[] { null! }); + }); + } - string result = provider.ConcatNamespaces(namespaceNames); + [Theory] // Input // Expected + [InlineData(new[] { "" }, "")] + [InlineData(new[] { "A" }, "A")] + [InlineData(new[] { "A", "B"}, "A.B")] + [InlineData(new[] { "A", "B", "C"}, "A.B.C")] + [InlineData(new[] { "Microsoft", "VisualStudio", "ProjectSystem"}, "Microsoft.VisualStudio.ProjectSystem")] + [InlineData(new[] { "Microsoft.VisualStudio", "ProjectSystem"}, "Microsoft.VisualStudio.ProjectSystem")] + [InlineData(new[] { "", "Microsoft", "VisualStudio", "ProjectSystem"}, "Microsoft.VisualStudio.ProjectSystem")] + [InlineData(new[] { "Microsoft", "", "ProjectSystem"}, "Microsoft.ProjectSystem")] + public void ConcatNamespaces_ValuesAsNamespacesName_ReturnsConcatenatedNamespaces(string[] namespaceNames, string expected) + { + var provider = CreateInstance(); - Assert.Equal(expected, result); - } + string result = provider.ConcatNamespaces(namespaceNames); - private static CSharpLanguageFeaturesProvider CreateInstance() - { - return new CSharpLanguageFeaturesProvider(); - } + Assert.Equal(expected, result); + } + + private static CSharpLanguageFeaturesProvider CreateInstance() + { + return new CSharpLanguageFeaturesProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.EvaluationCommandLineHandler.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.EvaluationCommandLineHandler.cs index fc121e25e6..ca459e6d8a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.EvaluationCommandLineHandler.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.EvaluationCommandLineHandler.cs @@ -2,40 +2,39 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public partial class AbstractEvaluationCommandLineHandlerTests { - public partial class AbstractEvaluationCommandLineHandlerTests + private class EvaluationCommandLineHandler : AbstractEvaluationCommandLineHandler { - private class EvaluationCommandLineHandler : AbstractEvaluationCommandLineHandler + public EvaluationCommandLineHandler(UnconfiguredProject project) + : base(project) + { + Files = new Dictionary>(); + } + + public ICollection FileNames + { + get { return Files.Keys; } + } + + public Dictionary> Files { get; } + + protected override void AddToContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) + { + Files.Add(fullPath, metadata); + } + + protected override void RemoveFromContext(IWorkspaceProjectContext context, string fullPath, IManagedProjectDiagnosticOutputService logger) + { + Files.Remove(fullPath); + } + + protected override void UpdateInContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary previousMetadata, IImmutableDictionary currentMetadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) { - public EvaluationCommandLineHandler(UnconfiguredProject project) - : base(project) - { - Files = new Dictionary>(); - } - - public ICollection FileNames - { - get { return Files.Keys; } - } - - public Dictionary> Files { get; } - - protected override void AddToContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary metadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) - { - Files.Add(fullPath, metadata); - } - - protected override void RemoveFromContext(IWorkspaceProjectContext context, string fullPath, IManagedProjectDiagnosticOutputService logger) - { - Files.Remove(fullPath); - } - - protected override void UpdateInContext(IWorkspaceProjectContext context, string fullPath, IImmutableDictionary previousMetadata, IImmutableDictionary currentMetadata, bool isActiveContext, IManagedProjectDiagnosticOutputService logger) - { - RemoveFromContext(context, fullPath, logger); - AddToContext(context, fullPath, currentMetadata, isActiveContext, logger); - } + RemoveFromContext(context, fullPath, logger); + AddToContext(context, fullPath, currentMetadata, isActiveContext, logger); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.cs index b0eeaecbe4..75b98131cb 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/AbstractEvaluationCommandLineHandlerTests.cs @@ -2,467 +2,466 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public partial class AbstractEvaluationCommandLineHandlerTests { - public partial class AbstractEvaluationCommandLineHandlerTests + [Fact] + public void ApplyProjectEvaluation_WhenNoChanges_DoesNothing() + { + var handler = CreateInstance(); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var version = 1; + var difference = IProjectChangeDiffFactory.WithNoChanges(); + + ApplyProjectEvaluation(context, handler, version, difference); + + Assert.Empty(handler.FileNames); + } + + [Fact] + public void ApplyProjectBuild_WhenNoChanges_DoesNothing() + { + var handler = CreateInstance(); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var version = 1; + var difference = IProjectChangeDiffFactory.WithNoChanges(); + + ApplyProjectBuild(context, handler, version, difference); + + Assert.Empty(handler.FileNames); + } + + [Theory] // Include path Expected full path + [InlineData(@"..\Source.cs", @"C:\Source.cs")] + [InlineData(@"..\AnotherProject\Source.cs", @"C:\AnotherProject\Source.cs")] + [InlineData(@"Source.cs", @"C:\Project\Source.cs")] + [InlineData(@"C:\Source.cs", @"C:\Source.cs")] + [InlineData(@"C:\Project\Source.cs", @"C:\Project\Source.cs")] + [InlineData(@"D:\Temp\Source.cs", @"D:\Temp\Source.cs")] + public void ApplyProjectEvaluation_AddsItemFullPathRelativeToProject(string includePath, string expected) + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); + var difference = IProjectChangeDiffFactory.WithAddedItems(includePath); + + ApplyProjectEvaluation(context, handler, 1, difference); + + Assert.Single(handler.FileNames, expected); + } + + [Theory] // Include path Expected full path + [InlineData(@"..\Source.cs", @"C:\Source.cs")] + [InlineData(@"..\AnotherProject\Source.cs", @"C:\AnotherProject\Source.cs")] + [InlineData(@"Source.cs", @"C:\Project\Source.cs")] + [InlineData(@"C:\Source.cs", @"C:\Source.cs")] + [InlineData(@"C:\Project\Source.cs", @"C:\Project\Source.cs")] + [InlineData(@"D:\Temp\Source.cs", @"D:\Temp\Source.cs")] + public void ApplyProjectBuild_AddsItemFullPathRelativeToProject(string includePath, string expected) + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); + var difference = IProjectChangeDiffFactory.WithAddedItems(includePath); + + ApplyProjectBuild(context, handler, 1, difference); + + Assert.Single(handler.FileNames, expected); + } + + [Theory] // Current state Added files Expected state + [InlineData("", "A.cs", @"C:\Project\A.cs")] + [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] + [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] + public void ApplyProjectEvaluation_WithExistingEvaluationChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) + { + string[] expectedFiles = expected.Split(';'); + + var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); + ApplyProjectEvaluation(context, handler, 2, difference); + + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Fact] + public void AddEvaluationChanges_CanAddItemWithMetadata() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithAddedItems("A.cs"); + var metadata = MetadataFactory.Create("A.cs", ("Name", "Value")); + + ApplyProjectEvaluation(context, handler, 1, difference, metadata: metadata); + + var result = handler.Files[@"C:\Project\A.cs"]; + + Assert.Equal("Value", result["Name"]); + } + + [Fact] + public void AddEvaluationChanges_ItemsWithExclusionMetadataAreIgnored() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithAddedItems("A.cs;B.cs;C.cs"); + var metadata = MetadataFactory.Create("A.cs", ("ExcludeFromCurrentConfiguration", "true")) + .Add("B.cs", ("ExcludeFromCurrentConfiguration", "false")); + + + ApplyProjectEvaluation(context, handler, 1, difference, metadata: metadata); + + string[] expectedFiles = new[] { @"C:\Project\B.cs", @"C:\Project\C.cs" }; + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Theory] // Current state Added files Expected state + [InlineData("", "A.cs", @"C:\Project\A.cs")] + [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] + [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] + public void ApplyProjectBuild_WithExistingEvaluationChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) + { + string[] expectedFiles = expected.Split(';'); + + var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); + ApplyProjectBuild(context, handler, 1, difference); + + Assert.Equal(handler.FileNames.OrderBy(f => f), expectedFiles.OrderBy(f => f)); + } + + [Theory] // Current state Removed files Expected state + [InlineData("", "A.cs", @"")] + [InlineData("", "A.cs;B.cs", @"")] + [InlineData("A.cs", "A.cs", @"")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] + public void ApplyProjectEvaluation_WithExistingEvaluationChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) + { + string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); + + var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); + ApplyProjectEvaluation(context, handler, 2, difference); + + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Theory] // Current state Removed files Expected state + [InlineData("", "A.cs", @"")] + [InlineData("", "A.cs;B.cs", @"")] + [InlineData("A.cs", "A.cs", @"")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] + public void ApplyProjectBuild_WithExistingEvaluationChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) + { + string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); + + var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); + ApplyProjectBuild(context, handler, 1, difference); + + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Theory] // Current state Added files Expected state + [InlineData("", "A.cs", @"C:\Project\A.cs")] + [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] + [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] + public void ApplyProjectEvaluation_WithExistingDesignTimeChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) + { + string[] expectedFiles = expected.Split(';'); + + var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); + ApplyProjectEvaluation(context, handler, 2, difference); + + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Theory] // Current state Added files Expected state + [InlineData("", "A.cs", @"C:\Project\A.cs")] + [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] + [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] + public void ApplyProjectBuild_WithExistingDesignTimeChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) + { + string[] expectedFiles = expected.Split(';'); + + var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); + ApplyProjectBuild(context, handler, 2, difference); + + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Theory] // Current state Removed files Expected state + [InlineData("", "A.cs", @"")] + [InlineData("", "A.cs;B.cs", @"")] + [InlineData("A.cs", "A.cs", @"")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] + public void ApplyProjectEvaluation_WithExistingDesignTimeChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) + { + string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); + + var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); + + var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); + ApplyProjectEvaluation(context, handler, 2, difference); + + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } + + [Theory] // Current state Removed files Expected state + [InlineData("", "A.cs", @"")] + [InlineData("", "A.cs;B.cs", @"")] + [InlineData("A.cs", "A.cs", @"")] + [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] + [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] + public void ApplyProjectBuild_WithExistingDesignTimeChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) { - [Fact] - public void ApplyProjectEvaluation_WhenNoChanges_DoesNothing() - { - var handler = CreateInstance(); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var version = 1; - var difference = IProjectChangeDiffFactory.WithNoChanges(); - - ApplyProjectEvaluation(context, handler, version, difference); - - Assert.Empty(handler.FileNames); - } - - [Fact] - public void ApplyProjectBuild_WhenNoChanges_DoesNothing() - { - var handler = CreateInstance(); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var version = 1; - var difference = IProjectChangeDiffFactory.WithNoChanges(); - - ApplyProjectBuild(context, handler, version, difference); - - Assert.Empty(handler.FileNames); - } - - [Theory] // Include path Expected full path - [InlineData(@"..\Source.cs", @"C:\Source.cs")] - [InlineData(@"..\AnotherProject\Source.cs", @"C:\AnotherProject\Source.cs")] - [InlineData(@"Source.cs", @"C:\Project\Source.cs")] - [InlineData(@"C:\Source.cs", @"C:\Source.cs")] - [InlineData(@"C:\Project\Source.cs", @"C:\Project\Source.cs")] - [InlineData(@"D:\Temp\Source.cs", @"D:\Temp\Source.cs")] - public void ApplyProjectEvaluation_AddsItemFullPathRelativeToProject(string includePath, string expected) - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); - var difference = IProjectChangeDiffFactory.WithAddedItems(includePath); - - ApplyProjectEvaluation(context, handler, 1, difference); - - Assert.Single(handler.FileNames, expected); - } - - [Theory] // Include path Expected full path - [InlineData(@"..\Source.cs", @"C:\Source.cs")] - [InlineData(@"..\AnotherProject\Source.cs", @"C:\AnotherProject\Source.cs")] - [InlineData(@"Source.cs", @"C:\Project\Source.cs")] - [InlineData(@"C:\Source.cs", @"C:\Source.cs")] - [InlineData(@"C:\Project\Source.cs", @"C:\Project\Source.cs")] - [InlineData(@"D:\Temp\Source.cs", @"D:\Temp\Source.cs")] - public void ApplyProjectBuild_AddsItemFullPathRelativeToProject(string includePath, string expected) - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); - var difference = IProjectChangeDiffFactory.WithAddedItems(includePath); - - ApplyProjectBuild(context, handler, 1, difference); - - Assert.Single(handler.FileNames, expected); - } - - [Theory] // Current state Added files Expected state - [InlineData("", "A.cs", @"C:\Project\A.cs")] - [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] - [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] - public void ApplyProjectEvaluation_WithExistingEvaluationChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) - { - string[] expectedFiles = expected.Split(';'); - - var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); - ApplyProjectEvaluation(context, handler, 2, difference); - - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Fact] - public void AddEvaluationChanges_CanAddItemWithMetadata() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithAddedItems("A.cs"); - var metadata = MetadataFactory.Create("A.cs", ("Name", "Value")); - - ApplyProjectEvaluation(context, handler, 1, difference, metadata: metadata); - - var result = handler.Files[@"C:\Project\A.cs"]; - - Assert.Equal("Value", result["Name"]); - } - - [Fact] - public void AddEvaluationChanges_ItemsWithExclusionMetadataAreIgnored() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithAddedItems("A.cs;B.cs;C.cs"); - var metadata = MetadataFactory.Create("A.cs", ("ExcludeFromCurrentConfiguration", "true")) - .Add("B.cs", ("ExcludeFromCurrentConfiguration", "false")); - - - ApplyProjectEvaluation(context, handler, 1, difference, metadata: metadata); - - string[] expectedFiles = new[] { @"C:\Project\B.cs", @"C:\Project\C.cs" }; - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Theory] // Current state Added files Expected state - [InlineData("", "A.cs", @"C:\Project\A.cs")] - [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] - [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] - public void ApplyProjectBuild_WithExistingEvaluationChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) - { - string[] expectedFiles = expected.Split(';'); - - var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); - ApplyProjectBuild(context, handler, 1, difference); - - Assert.Equal(handler.FileNames.OrderBy(f => f), expectedFiles.OrderBy(f => f)); - } - - [Theory] // Current state Removed files Expected state - [InlineData("", "A.cs", @"")] - [InlineData("", "A.cs;B.cs", @"")] - [InlineData("A.cs", "A.cs", @"")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] - public void ApplyProjectEvaluation_WithExistingEvaluationChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) - { - string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); - - var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); - ApplyProjectEvaluation(context, handler, 2, difference); - - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Theory] // Current state Removed files Expected state - [InlineData("", "A.cs", @"")] - [InlineData("", "A.cs;B.cs", @"")] - [InlineData("A.cs", "A.cs", @"")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] - public void ApplyProjectBuild_WithExistingEvaluationChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) - { - string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); - - var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); - ApplyProjectBuild(context, handler, 1, difference); - - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Theory] // Current state Added files Expected state - [InlineData("", "A.cs", @"C:\Project\A.cs")] - [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] - [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] - public void ApplyProjectEvaluation_WithExistingDesignTimeChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) - { - string[] expectedFiles = expected.Split(';'); - - var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); - ApplyProjectEvaluation(context, handler, 2, difference); - - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Theory] // Current state Added files Expected state - [InlineData("", "A.cs", @"C:\Project\A.cs")] - [InlineData("", "A.cs;B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs", "A.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs")] - [InlineData("A.cs;B.cs;C.cs", "D.cs;E.cs;F.cs", @"C:\Project\A.cs;C:\Project\B.cs;C:\Project\C.cs;C:\Project\D.cs;C:\Project\E.cs;C:\Project\F.cs")] - public void ApplyProjectBuild_WithExistingDesignTimeChanges_CanAddItem(string currentFiles, string filesToAdd, string expected) - { - string[] expectedFiles = expected.Split(';'); - - var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithAddedItems(filesToAdd); - ApplyProjectBuild(context, handler, 2, difference); - - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Theory] // Current state Removed files Expected state - [InlineData("", "A.cs", @"")] - [InlineData("", "A.cs;B.cs", @"")] - [InlineData("A.cs", "A.cs", @"")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] - public void ApplyProjectEvaluation_WithExistingDesignTimeChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) - { - string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); - - var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); - - var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); - ApplyProjectEvaluation(context, handler, 2, difference); - - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } - - [Theory] // Current state Removed files Expected state - [InlineData("", "A.cs", @"")] - [InlineData("", "A.cs;B.cs", @"")] - [InlineData("A.cs", "A.cs", @"")] - [InlineData("A.cs;B.cs", "B.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs", "B.cs;C.cs", @"C:\Project\A.cs")] - [InlineData("A.cs;B.cs;C.cs", "A.cs;E.cs;F.cs", @"C:\Project\B.cs;C:\Project\C.cs")] - public void ApplyProjectBuild_WithExistingDesignTimeChanges_CanRemoveItem(string currentFiles, string filesToRemove, string expected) - { - string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); + string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); - var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); + var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); - var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); - ApplyProjectBuild(context, handler, 2, difference); + var difference = IProjectChangeDiffFactory.WithRemovedItems(filesToRemove); + ApplyProjectBuild(context, handler, 2, difference); - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } - [Theory] // Current state Original name New name Expected state - [InlineData("A.cs", "A.cs", "B.cs", @"C:\Project\B.cs")] - [InlineData("A.cs;B.cs", "B.cs", "C.cs", @"C:\Project\A.cs;C:\Project\C.cs")] - [InlineData("A.cs;B.cs;C.cs", "B.cs;C.cs", "D.cs;E.cs", @"C:\Project\A.cs;C:\Project\D.cs;C:\Project\E.cs")] - [InlineData("A.cs;B.cs", "A.cs;B.cs", "B.cs;A.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - public void ApplyProjectEvaluation_WithExistingEvaluationChanges_CanRenameItem(string currentFiles, string originalNames, string newNames, string expected) - { - string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); + [Theory] // Current state Original name New name Expected state + [InlineData("A.cs", "A.cs", "B.cs", @"C:\Project\B.cs")] + [InlineData("A.cs;B.cs", "B.cs", "C.cs", @"C:\Project\A.cs;C:\Project\C.cs")] + [InlineData("A.cs;B.cs;C.cs", "B.cs;C.cs", "D.cs;E.cs", @"C:\Project\A.cs;C:\Project\D.cs;C:\Project\E.cs")] + [InlineData("A.cs;B.cs", "A.cs;B.cs", "B.cs;A.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + public void ApplyProjectEvaluation_WithExistingEvaluationChanges_CanRenameItem(string currentFiles, string originalNames, string newNames, string expected) + { + string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); - var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); + var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); - var difference = IProjectChangeDiffFactory.WithRenamedItems(originalNames, newNames); + var difference = IProjectChangeDiffFactory.WithRenamedItems(originalNames, newNames); - ApplyProjectEvaluation(context, handler, 2, difference); + ApplyProjectEvaluation(context, handler, 2, difference); - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } - [Theory] // Current state Original name New name Expected state - [InlineData("A.cs", "A.cs", "B.cs", @"C:\Project\B.cs")] - [InlineData("A.cs;B.cs", "B.cs", "C.cs", @"C:\Project\A.cs;C:\Project\C.cs")] - [InlineData("A.cs;B.cs;C.cs", "B.cs;C.cs", "D.cs;E.cs", @"C:\Project\A.cs;C:\Project\D.cs;C:\Project\E.cs")] - [InlineData("A.cs;B.cs", "A.cs;B.cs", "B.cs;A.cs", @"C:\Project\A.cs;C:\Project\B.cs")] - public void ApplyProjectEvaluation_WithExistingDesignTimeChanges_CanRenameItem(string currentFiles, string originalNames, string newNames, string expected) - { - string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); + [Theory] // Current state Original name New name Expected state + [InlineData("A.cs", "A.cs", "B.cs", @"C:\Project\B.cs")] + [InlineData("A.cs;B.cs", "B.cs", "C.cs", @"C:\Project\A.cs;C:\Project\C.cs")] + [InlineData("A.cs;B.cs;C.cs", "B.cs;C.cs", "D.cs;E.cs", @"C:\Project\A.cs;C:\Project\D.cs;C:\Project\E.cs")] + [InlineData("A.cs;B.cs", "A.cs;B.cs", "B.cs;A.cs", @"C:\Project\A.cs;C:\Project\B.cs")] + public void ApplyProjectEvaluation_WithExistingDesignTimeChanges_CanRenameItem(string currentFiles, string originalNames, string newNames, string expected) + { + string[] expectedFiles = expected.Length == 0 ? Array.Empty() : expected.Split(';'); - var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); - var context = IWorkspaceProjectContextMockFactory.Create(); + var handler = CreateInstanceWithDesignTimeItems(@"C:\Project\Project.csproj", currentFiles); + var context = IWorkspaceProjectContextMockFactory.Create(); - var difference = IProjectChangeDiffFactory.WithRenamedItems(originalNames, newNames); + var difference = IProjectChangeDiffFactory.WithRenamedItems(originalNames, newNames); - ApplyProjectEvaluation(context, handler, 2, difference); + ApplyProjectEvaluation(context, handler, 2, difference); - Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); - } + Assert.Equal(expectedFiles.OrderBy(f => f), handler.FileNames.OrderBy(f => f)); + } - [Fact] - public void ApplyProjectEvaluationChanges_WithExistingEvaluationChanges_CanAddChangeMetadata() - { - var file = "A.cs"; - var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", file); - var context = IWorkspaceProjectContextMockFactory.Create(); + [Fact] + public void ApplyProjectEvaluationChanges_WithExistingEvaluationChanges_CanAddChangeMetadata() + { + var file = "A.cs"; + var handler = CreateInstanceWithEvaluationItems(@"C:\Project\Project.csproj", file); + var context = IWorkspaceProjectContextMockFactory.Create(); - var difference = IProjectChangeDiffFactory.WithChangedItems(file); - var metadata = MetadataFactory.Create(file, ("Name", "Value")); + var difference = IProjectChangeDiffFactory.WithChangedItems(file); + var metadata = MetadataFactory.Create(file, ("Name", "Value")); - ApplyProjectEvaluation(context, handler, 2, difference, metadata: metadata); + ApplyProjectEvaluation(context, handler, 2, difference, metadata: metadata); - var result = handler.Files[@"C:\Project\A.cs"]; + var result = handler.Files[@"C:\Project\A.cs"]; - Assert.Equal("Value", result["Name"]); - } + Assert.Equal("Value", result["Name"]); + } - [Fact] - public void ApplyProjectBuild_WhenNewerEvaluationChangesWithAddedConflict_EvaluationWinsOut() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); + [Fact] + public void ApplyProjectBuild_WhenNewerEvaluationChangesWithAddedConflict_EvaluationWinsOut() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); - int evaluationVersion = 1; + int evaluationVersion = 1; - // Setup the "current state" - ApplyProjectEvaluation(context, handler, evaluationVersion, IProjectChangeDiffFactory.WithAddedItems("Source.cs")); + // Setup the "current state" + ApplyProjectEvaluation(context, handler, evaluationVersion, IProjectChangeDiffFactory.WithAddedItems("Source.cs")); - int designTimeVersion = 0; + int designTimeVersion = 0; - ApplyProjectBuild(context, handler, designTimeVersion, IProjectChangeDiffFactory.WithRemovedItems("Source.cs")); + ApplyProjectBuild(context, handler, designTimeVersion, IProjectChangeDiffFactory.WithRemovedItems("Source.cs")); - Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); - } + Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); + } - [Fact] - public void ApplyProjectBuild_WhenNewerEvaluationChangesWithRemovedConflict_EvaluationWinsOut() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); + [Fact] + public void ApplyProjectBuild_WhenNewerEvaluationChangesWithRemovedConflict_EvaluationWinsOut() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); - int evaluationVersion = 1; + int evaluationVersion = 1; - // Setup the "current state" - ApplyProjectEvaluation(context, handler, evaluationVersion, IProjectChangeDiffFactory.WithRemovedItems("Source.cs")); + // Setup the "current state" + ApplyProjectEvaluation(context, handler, evaluationVersion, IProjectChangeDiffFactory.WithRemovedItems("Source.cs")); - int designTimeVersion = 0; + int designTimeVersion = 0; - ApplyProjectBuild(context, handler, designTimeVersion, IProjectChangeDiffFactory.WithAddedItems("Source.cs")); + ApplyProjectBuild(context, handler, designTimeVersion, IProjectChangeDiffFactory.WithAddedItems("Source.cs")); - Assert.Empty(handler.FileNames); - } + Assert.Empty(handler.FileNames); + } - [Fact] - public void ApplyProjectBuild_WhenOlderEvaluationChangesWithRemovedConflict_DesignTimeWinsOut() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); + [Fact] + public void ApplyProjectBuild_WhenOlderEvaluationChangesWithRemovedConflict_DesignTimeWinsOut() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); - int evaluationVersion = 0; + int evaluationVersion = 0; - // Setup the "current state" - ApplyProjectEvaluation(context, handler, evaluationVersion, IProjectChangeDiffFactory.WithRemovedItems("Source.cs")); + // Setup the "current state" + ApplyProjectEvaluation(context, handler, evaluationVersion, IProjectChangeDiffFactory.WithRemovedItems("Source.cs")); - int designTimeVersion = 1; + int designTimeVersion = 1; - ApplyProjectBuild(context, handler, designTimeVersion, IProjectChangeDiffFactory.WithAddedItems("Source.cs")); + ApplyProjectBuild(context, handler, designTimeVersion, IProjectChangeDiffFactory.WithAddedItems("Source.cs")); - Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); - } + Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); + } - [Fact] - public void ApplyProjectEvaluation_ChangingExclusionMetadata_IncludesFile() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); + [Fact] + public void ApplyProjectEvaluation_ChangingExclusionMetadata_IncludesFile() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); - var metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "true")); + var metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "true")); - ApplyProjectEvaluation(context, handler, version: 0, IProjectChangeDiffFactory.WithAddedItems("Source.cs"), metadata: metadata); + ApplyProjectEvaluation(context, handler, version: 0, IProjectChangeDiffFactory.WithAddedItems("Source.cs"), metadata: metadata); - Assert.Empty(handler.FileNames); + Assert.Empty(handler.FileNames); - var previousMetadata = metadata; - metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "false")); + var previousMetadata = metadata; + metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "false")); - ApplyProjectEvaluation(context, handler, version: 1, IProjectChangeDiffFactory.WithChangedItems("Source.cs"), previousMetadata, metadata); + ApplyProjectEvaluation(context, handler, version: 1, IProjectChangeDiffFactory.WithChangedItems("Source.cs"), previousMetadata, metadata); - Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); - } + Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); + } - [Fact] - public void ApplyProjectEvaluation_ChangingExclusionMetadata_ExcludesFile() - { - var handler = CreateInstance(@"C:\Project\Project.csproj"); - var context = IWorkspaceProjectContextMockFactory.Create(); + [Fact] + public void ApplyProjectEvaluation_ChangingExclusionMetadata_ExcludesFile() + { + var handler = CreateInstance(@"C:\Project\Project.csproj"); + var context = IWorkspaceProjectContextMockFactory.Create(); - var metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "false")); + var metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "false")); - ApplyProjectEvaluation(context, handler, version: 0, IProjectChangeDiffFactory.WithAddedItems("Source.cs"), metadata: metadata); + ApplyProjectEvaluation(context, handler, version: 0, IProjectChangeDiffFactory.WithAddedItems("Source.cs"), metadata: metadata); - Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); + Assert.Single(handler.FileNames, @"C:\Project\Source.cs"); - var previousMetadata = metadata; - metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "true")); + var previousMetadata = metadata; + metadata = MetadataFactory.Create("Source.cs", ("ExcludeFromCurrentConfiguration", "true")); - ApplyProjectEvaluation(context, handler, version: 1, IProjectChangeDiffFactory.WithChangedItems("Source.cs"), previousMetadata, metadata); + ApplyProjectEvaluation(context, handler, version: 1, IProjectChangeDiffFactory.WithChangedItems("Source.cs"), previousMetadata, metadata); - Assert.Empty(handler.FileNames); - } + Assert.Empty(handler.FileNames); + } - private static void ApplyProjectEvaluation( - IWorkspaceProjectContext context, - AbstractEvaluationCommandLineHandler handler, - IComparable version, - IProjectChangeDiff difference, - IImmutableDictionary>? previousMetadata = null, - IImmutableDictionary>? metadata = null) - { - metadata ??= ImmutableDictionary>.Empty; - previousMetadata ??= ImmutableDictionary>.Empty; - bool isActiveContext = true; - var logger = IManagedProjectDiagnosticOutputServiceFactory.Create(); + private static void ApplyProjectEvaluation( + IWorkspaceProjectContext context, + AbstractEvaluationCommandLineHandler handler, + IComparable version, + IProjectChangeDiff difference, + IImmutableDictionary>? previousMetadata = null, + IImmutableDictionary>? metadata = null) + { + metadata ??= ImmutableDictionary>.Empty; + previousMetadata ??= ImmutableDictionary>.Empty; + bool isActiveContext = true; + var logger = IManagedProjectDiagnosticOutputServiceFactory.Create(); - handler.ApplyProjectEvaluation(context, version, difference, previousMetadata, metadata, isActiveContext, logger); - } + handler.ApplyProjectEvaluation(context, version, difference, previousMetadata, metadata, isActiveContext, logger); + } - private static void ApplyProjectBuild(IWorkspaceProjectContext context, AbstractEvaluationCommandLineHandler handler, IComparable version, IProjectChangeDiff difference) - { - bool isActiveContext = true; - var logger = IManagedProjectDiagnosticOutputServiceFactory.Create(); + private static void ApplyProjectBuild(IWorkspaceProjectContext context, AbstractEvaluationCommandLineHandler handler, IComparable version, IProjectChangeDiff difference) + { + bool isActiveContext = true; + var logger = IManagedProjectDiagnosticOutputServiceFactory.Create(); - handler.ApplyProjectBuild(context, version, difference, isActiveContext, logger); - } + handler.ApplyProjectBuild(context, version, difference, isActiveContext, logger); + } - private static EvaluationCommandLineHandler CreateInstanceWithEvaluationItems(string fullPath, string semiColonSeparatedItems) - { - var handler = CreateInstance(fullPath); - var context = IWorkspaceProjectContextMockFactory.Create(); + private static EvaluationCommandLineHandler CreateInstanceWithEvaluationItems(string fullPath, string semiColonSeparatedItems) + { + var handler = CreateInstance(fullPath); + var context = IWorkspaceProjectContextMockFactory.Create(); - // Setup the "current state" - ApplyProjectEvaluation(context, handler, 1, IProjectChangeDiffFactory.WithAddedItems(semiColonSeparatedItems)); + // Setup the "current state" + ApplyProjectEvaluation(context, handler, 1, IProjectChangeDiffFactory.WithAddedItems(semiColonSeparatedItems)); - return handler; - } + return handler; + } - private static EvaluationCommandLineHandler CreateInstanceWithDesignTimeItems(string fullPath, string semiColonSeparatedItems) - { - var handler = CreateInstance(fullPath); - var context = IWorkspaceProjectContextMockFactory.Create(); + private static EvaluationCommandLineHandler CreateInstanceWithDesignTimeItems(string fullPath, string semiColonSeparatedItems) + { + var handler = CreateInstance(fullPath); + var context = IWorkspaceProjectContextMockFactory.Create(); - // Setup the "current state" - ApplyProjectBuild(context, handler, 1, IProjectChangeDiffFactory.WithAddedItems(semiColonSeparatedItems)); + // Setup the "current state" + ApplyProjectBuild(context, handler, 1, IProjectChangeDiffFactory.WithAddedItems(semiColonSeparatedItems)); - return handler; - } + return handler; + } - private static EvaluationCommandLineHandler CreateInstance(string? fullPath = null) - { - var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); + private static EvaluationCommandLineHandler CreateInstance(string? fullPath = null) + { + var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); - return new EvaluationCommandLineHandler(project); - } + return new EvaluationCommandLineHandler(project); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_CommandLineTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_CommandLineTests.cs index 2935d03cb9..45437eacac 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_CommandLineTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_CommandLineTests.cs @@ -3,68 +3,67 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public class CompileItemHandler_CommandTests { - public class CompileItemHandler_CommandTests + [Fact] + public void Constructor_NullAsProject_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsProject_ThrowsArgumentNull() + Assert.Throws("project", () => { - Assert.Throws("project", () => - { - new CompileItemHandler(null!); - }); - } + new CompileItemHandler(null!); + }); + } - [Fact] - public void UniqueSourceFilesPushedToWorkspace() - { - var sourceFilesPushedToWorkspace = new HashSet(StringComparers.Paths); - void onSourceFileAdded(string s) => Assert.True(sourceFilesPushedToWorkspace.Add(s)); - void onSourceFileRemoved(string s) => sourceFilesPushedToWorkspace.Remove(s); + [Fact] + public void UniqueSourceFilesPushedToWorkspace() + { + var sourceFilesPushedToWorkspace = new HashSet(StringComparers.Paths); + void onSourceFileAdded(string s) => Assert.True(sourceFilesPushedToWorkspace.Add(s)); + void onSourceFileRemoved(string s) => sourceFilesPushedToWorkspace.Remove(s); - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForSourceFiles(project, onSourceFileAdded, onSourceFileRemoved); - var logger = Mock.Of(); + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForSourceFiles(project, onSourceFileAdded, onSourceFileRemoved); + var logger = Mock.Of(); - var handler = new CompileItemHandler(project); - var projectDir = project.GetProjectDirectory(); - var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [@"C:\file1.cs", @"C:\file2.cs", @"C:\file1.cs"], baseDirectory: projectDir, sdkDirectory: null)); - var empty = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [], baseDirectory: projectDir, sdkDirectory: null)); + var handler = new CompileItemHandler(project); + var projectDir = project.GetProjectDirectory(); + var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [@"C:\file1.cs", @"C:\file2.cs", @"C:\file1.cs"], baseDirectory: projectDir, sdkDirectory: null)); + var empty = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [], baseDirectory: projectDir, sdkDirectory: null)); - handler.Handle(context, 10, added: added, removed: empty, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); + handler.Handle(context, 10, added: added, removed: empty, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); - AssertEx.CollectionLength(sourceFilesPushedToWorkspace, 2); - Assert.Contains(@"C:\file1.cs", sourceFilesPushedToWorkspace); - Assert.Contains(@"C:\file2.cs", sourceFilesPushedToWorkspace); + AssertEx.CollectionLength(sourceFilesPushedToWorkspace, 2); + Assert.Contains(@"C:\file1.cs", sourceFilesPushedToWorkspace); + Assert.Contains(@"C:\file2.cs", sourceFilesPushedToWorkspace); - var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [@"C:\file1.cs", @"C:\file1.cs"], baseDirectory: projectDir, sdkDirectory: null)); - handler.Handle(context, 10, added: empty, removed: removed, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); + var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [@"C:\file1.cs", @"C:\file1.cs"], baseDirectory: projectDir, sdkDirectory: null)); + handler.Handle(context, 10, added: empty, removed: removed, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); - Assert.Single(sourceFilesPushedToWorkspace); - Assert.Contains(@"C:\file2.cs", sourceFilesPushedToWorkspace); - } + Assert.Single(sourceFilesPushedToWorkspace); + Assert.Contains(@"C:\file2.cs", sourceFilesPushedToWorkspace); + } - [Fact] - public void RootedSourceFilesPushedToWorkspace() - { - var sourceFilesPushedToWorkspace = new HashSet(StringComparers.Paths); - void onSourceFileAdded(string s) => Assert.True(sourceFilesPushedToWorkspace.Add(s)); - void onSourceFileRemoved(string s) => sourceFilesPushedToWorkspace.Remove(s); + [Fact] + public void RootedSourceFilesPushedToWorkspace() + { + var sourceFilesPushedToWorkspace = new HashSet(StringComparers.Paths); + void onSourceFileAdded(string s) => Assert.True(sourceFilesPushedToWorkspace.Add(s)); + void onSourceFileRemoved(string s) => sourceFilesPushedToWorkspace.Remove(s); - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\ProjectFolder\MyProject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForSourceFiles(project, onSourceFileAdded, onSourceFileRemoved); - var logger = Mock.Of(); + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\ProjectFolder\MyProject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForSourceFiles(project, onSourceFileAdded, onSourceFileRemoved); + var logger = Mock.Of(); - var handler = new CompileItemHandler(project); - var projectDir = project.GetProjectDirectory(); - var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [@"file1.cs", @"..\ProjectFolder\file1.cs"], baseDirectory: projectDir, sdkDirectory: null)); - var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [], baseDirectory: projectDir, sdkDirectory: null)); + var handler = new CompileItemHandler(project); + var projectDir = project.GetProjectDirectory(); + var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [@"file1.cs", @"..\ProjectFolder\file1.cs"], baseDirectory: projectDir, sdkDirectory: null)); + var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: [], baseDirectory: projectDir, sdkDirectory: null)); - handler.Handle(context, 10, added: added, removed: removed, new ContextState(true, false), logger: logger); + handler.Handle(context, 10, added: added, removed: removed, new ContextState(true, false), logger: logger); - Assert.Single(sourceFilesPushedToWorkspace); - Assert.Contains(@"C:\ProjectFolder\file1.cs", sourceFilesPushedToWorkspace); - } + Assert.Single(sourceFilesPushedToWorkspace); + Assert.Contains(@"C:\ProjectFolder\file1.cs", sourceFilesPushedToWorkspace); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_EvaluationTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_EvaluationTests.cs index 5a73b6d724..ec0580d31d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_EvaluationTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/CompileItemHandler_EvaluationTests.cs @@ -1,21 +1,20 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public class CompileItemHandler_EvaluationTests : EvaluationHandlerTestBase { - public class CompileItemHandler_EvaluationTests : EvaluationHandlerTestBase + [Fact] + public void Constructor_NullAsProject_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsProject_ThrowsArgumentNull() + Assert.Throws("project", () => { - Assert.Throws("project", () => - { - new CompileItemHandler(null!); - }); - } + new CompileItemHandler(null!); + }); + } - internal override IProjectEvaluationHandler CreateInstance() - { - return new CompileItemHandler(UnconfiguredProjectFactory.Create()); - } + internal override IProjectEvaluationHandler CreateInstance() + { + return new CompileItemHandler(UnconfiguredProjectFactory.Create()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/DynamicItemHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/DynamicItemHandlerTests.cs index fac7027786..77f9d03f33 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/DynamicItemHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/DynamicItemHandlerTests.cs @@ -2,122 +2,121 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public class DynamicItemHandlerTests { - public class DynamicItemHandlerTests + [Fact] + public void Handle_RazorAndCshtmlFiles_AddsToContext() + { + var dynamicFiles = new HashSet(StringComparers.Paths); + void onDynamicFileAdded(string s) => Assert.True(dynamicFiles.Add(s)); + + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Myproject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForDynamicFiles(project, onDynamicFileAdded); + + var handler = new DynamicItemHandler(project); + + var projectChanges = ImmutableDictionary.Empty.Add( + "None", + IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "AddedItems": [ "File1.razor", "File1.cshtml", "File1.cs" ] + } + } + """)); + + handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); + + Assert.Equal(2, dynamicFiles.Count); + Assert.Contains(@"C:\File1.razor", dynamicFiles); + Assert.Contains(@"C:\File1.cshtml", dynamicFiles); + } + + [Fact] + public void Handle_RazorAndCshtmlFiles_InDifferentItemTypes_AddsToContext() { - [Fact] - public void Handle_RazorAndCshtmlFiles_AddsToContext() - { - var dynamicFiles = new HashSet(StringComparers.Paths); - void onDynamicFileAdded(string s) => Assert.True(dynamicFiles.Add(s)); + var dynamicFiles = new HashSet(StringComparers.Paths); + void onDynamicFileAdded(string s) => Assert.True(dynamicFiles.Add(s)); - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Myproject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForDynamicFiles(project, onDynamicFileAdded); + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Myproject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForDynamicFiles(project, onDynamicFileAdded); - var handler = new DynamicItemHandler(project); + var handler = new DynamicItemHandler(project); - var projectChanges = ImmutableDictionary.Empty.Add( + var projectChanges = ImmutableDictionary.Empty + .Add( "None", IProjectChangeDescriptionFactory.FromJson( """ { "Difference": { "AnyChanges": true, - "AddedItems": [ "File1.razor", "File1.cshtml", "File1.cs" ] + "AddedItems": [ "File1.razor", "File1.cs" ] + } + } + """)) + .Add( + "Content", + IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "AddedItems": [ "File1.cshtml", "File2.cs" ] } } """)); - handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); - - Assert.Equal(2, dynamicFiles.Count); - Assert.Contains(@"C:\File1.razor", dynamicFiles); - Assert.Contains(@"C:\File1.cshtml", dynamicFiles); - } - - [Fact] - public void Handle_RazorAndCshtmlFiles_InDifferentItemTypes_AddsToContext() - { - var dynamicFiles = new HashSet(StringComparers.Paths); - void onDynamicFileAdded(string s) => Assert.True(dynamicFiles.Add(s)); - - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Myproject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForDynamicFiles(project, onDynamicFileAdded); - - var handler = new DynamicItemHandler(project); - - var projectChanges = ImmutableDictionary.Empty - .Add( - "None", - IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "AddedItems": [ "File1.razor", "File1.cs" ] - } - } - """)) - .Add( - "Content", - IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "AddedItems": [ "File1.cshtml", "File2.cs" ] - } - } - """)); - - handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); - - Assert.Equal(2, dynamicFiles.Count); - Assert.Contains(@"C:\File1.razor", dynamicFiles); - Assert.Contains(@"C:\File1.cshtml", dynamicFiles); - } - - [Fact] - public void Handle_RazorAndCshtmlFiles_InDifferentItemTypes_IgnoresDuplicates() - { - var dynamicFiles = new HashSet(StringComparers.Paths); - void onDynamicFileAdded(string s) => Assert.True(dynamicFiles.Add(s)); - - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Myproject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForDynamicFiles(project, onDynamicFileAdded); - - var handler = new DynamicItemHandler(project); - - var projectChanges = ImmutableDictionary.Empty - .Add( - "None", - IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "AddedItems": [ "File1.razor", "File1.cs" ] - } + handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); + + Assert.Equal(2, dynamicFiles.Count); + Assert.Contains(@"C:\File1.razor", dynamicFiles); + Assert.Contains(@"C:\File1.cshtml", dynamicFiles); + } + + [Fact] + public void Handle_RazorAndCshtmlFiles_InDifferentItemTypes_IgnoresDuplicates() + { + var dynamicFiles = new HashSet(StringComparers.Paths); + void onDynamicFileAdded(string s) => Assert.True(dynamicFiles.Add(s)); + + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\Myproject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForDynamicFiles(project, onDynamicFileAdded); + + var handler = new DynamicItemHandler(project); + + var projectChanges = ImmutableDictionary.Empty + .Add( + "None", + IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "AddedItems": [ "File1.razor", "File1.cs" ] } - """)) - .Add( - "Content", - IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "AddedItems": [ "File1.razor", "File1.cshtml", "File2.cs" ] - } + } + """)) + .Add( + "Content", + IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "AddedItems": [ "File1.razor", "File1.cshtml", "File2.cs" ] } - """)); + } + """)); - handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); + handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); - Assert.Equal(2, dynamicFiles.Count); - Assert.Contains(@"C:\File1.razor", dynamicFiles); - Assert.Contains(@"C:\File1.cshtml", dynamicFiles); - } + Assert.Equal(2, dynamicFiles.Count); + Assert.Contains(@"C:\File1.razor", dynamicFiles); + Assert.Contains(@"C:\File1.cshtml", dynamicFiles); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/EvaluationHandlerTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/EvaluationHandlerTestBase.cs index d78f25838e..7adb230d39 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/EvaluationHandlerTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/EvaluationHandlerTestBase.cs @@ -2,25 +2,24 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public abstract class EvaluationHandlerTestBase { - public abstract class EvaluationHandlerTestBase + [Fact] + public void EvaluationRuleName_ReturnsValue() { - [Fact] - public void EvaluationRuleName_ReturnsValue() - { - var handler = CreateInstance(); - - Assert.NotEmpty(handler.ProjectEvaluationRule); - } + var handler = CreateInstance(); - internal static void Handle(IWorkspaceProjectContext context, IProjectEvaluationHandler handler, IProjectChangeDescription projectChange, ProjectConfiguration? projectConfiguration = null) - { - projectConfiguration ??= ProjectConfigurationFactory.Create("Debug|AnyCPU"); + Assert.NotEmpty(handler.ProjectEvaluationRule); + } - handler.Handle(context, projectConfiguration, 1, projectChange, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); - } + internal static void Handle(IWorkspaceProjectContext context, IProjectEvaluationHandler handler, IProjectChangeDescription projectChange, ProjectConfiguration? projectConfiguration = null) + { + projectConfiguration ??= ProjectConfigurationFactory.Create("Debug|AnyCPU"); - internal abstract IProjectEvaluationHandler CreateInstance(); + handler.Handle(context, projectConfiguration, 1, projectChange, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); } + + internal abstract IProjectEvaluationHandler CreateInstance(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/MetadataReferenceItemHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/MetadataReferenceItemHandlerTests.cs index ff8b3fbfd1..fec4d8991f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/MetadataReferenceItemHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/MetadataReferenceItemHandlerTests.cs @@ -3,61 +3,60 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public class MetadataReferenceItemHandlerTests { - public class MetadataReferenceItemHandlerTests + [Fact] + public void DuplicateMetadataReferencesPushedToWorkspace() + { + var referencesPushedToWorkspace = new HashSet(StringComparers.Paths); + void onReferenceAdded(string s) => referencesPushedToWorkspace.Add(s); + void onReferenceRemoved(string s) => referencesPushedToWorkspace.Remove(s); + + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); + var logger = Mock.Of(); + + var handler = new MetadataReferenceItemHandler(project); + var projectDir = project.GetProjectDirectory(); + var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly2.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null)); + var empty = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); + + handler.Handle(context, 10, added: added, removed: empty, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); + + AssertEx.CollectionLength(referencesPushedToWorkspace, 2); + Assert.Contains(@"C:\Assembly1.dll", referencesPushedToWorkspace); + Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); + + var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null)); + handler.Handle(context, 10, added: empty, removed: removed, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); + + Assert.Single(referencesPushedToWorkspace); + Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); + } + + [Fact] + public void RootedReferencesPushedToWorkspace() { - [Fact] - public void DuplicateMetadataReferencesPushedToWorkspace() - { - var referencesPushedToWorkspace = new HashSet(StringComparers.Paths); - void onReferenceAdded(string s) => referencesPushedToWorkspace.Add(s); - void onReferenceRemoved(string s) => referencesPushedToWorkspace.Remove(s); - - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); - var logger = Mock.Of(); - - var handler = new MetadataReferenceItemHandler(project); - var projectDir = project.GetProjectDirectory(); - var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly2.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null)); - var empty = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); - - handler.Handle(context, 10, added: added, removed: empty, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); - - AssertEx.CollectionLength(referencesPushedToWorkspace, 2); - Assert.Contains(@"C:\Assembly1.dll", referencesPushedToWorkspace); - Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); - - var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:C:\Assembly1.dll", @"/reference:C:\Assembly1.dll" }, baseDirectory: projectDir, sdkDirectory: null)); - handler.Handle(context, 10, added: empty, removed: removed, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); - - Assert.Single(referencesPushedToWorkspace); - Assert.Contains(@"C:\Assembly2.dll", referencesPushedToWorkspace); - } - - [Fact] - public void RootedReferencesPushedToWorkspace() - { - var referencesPushedToWorkspace = new HashSet(StringComparers.Paths); - void onReferenceAdded(string s) => referencesPushedToWorkspace.Add(s); - void onReferenceRemoved(string s) => referencesPushedToWorkspace.Remove(s); - - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\ProjectFolder\MyProject.csproj"); - var context = IWorkspaceProjectContextMockFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); - var logger = Mock.Of(); - - var handler = new MetadataReferenceItemHandler(project); - var projectDir = project.GetProjectDirectory(); - var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:Assembly1.dll", @"/reference:C:\ProjectFolder\Assembly2.dll", @"/reference:..\ProjectFolder\Assembly3.dll" }, baseDirectory: projectDir, sdkDirectory: null)); - var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); - - handler.Handle(context, 10, added: added, removed: removed, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); - - AssertEx.CollectionLength(referencesPushedToWorkspace, 3); - Assert.Contains(@"C:\ProjectFolder\Assembly1.dll", referencesPushedToWorkspace); - Assert.Contains(@"C:\ProjectFolder\Assembly2.dll", referencesPushedToWorkspace); - Assert.Contains(@"C:\ProjectFolder\Assembly3.dll", referencesPushedToWorkspace); - } + var referencesPushedToWorkspace = new HashSet(StringComparers.Paths); + void onReferenceAdded(string s) => referencesPushedToWorkspace.Add(s); + void onReferenceRemoved(string s) => referencesPushedToWorkspace.Remove(s); + + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\ProjectFolder\MyProject.csproj"); + var context = IWorkspaceProjectContextMockFactory.CreateForMetadataReferences(project, onReferenceAdded, onReferenceRemoved); + var logger = Mock.Of(); + + var handler = new MetadataReferenceItemHandler(project); + var projectDir = project.GetProjectDirectory(); + var added = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new[] { @"/reference:Assembly1.dll", @"/reference:C:\ProjectFolder\Assembly2.dll", @"/reference:..\ProjectFolder\Assembly3.dll" }, baseDirectory: projectDir, sdkDirectory: null)); + var removed = BuildOptions.FromCommandLineArguments(CSharpCommandLineParser.Default.Parse(args: new string[] { }, baseDirectory: projectDir, sdkDirectory: null)); + + handler.Handle(context, 10, added: added, removed: removed, new ContextState(isActiveEditorContext: true, isActiveConfiguration: false), logger: logger); + + AssertEx.CollectionLength(referencesPushedToWorkspace, 3); + Assert.Contains(@"C:\ProjectFolder\Assembly1.dll", referencesPushedToWorkspace); + Assert.Contains(@"C:\ProjectFolder\Assembly2.dll", referencesPushedToWorkspace); + Assert.Contains(@"C:\ProjectFolder\Assembly3.dll", referencesPushedToWorkspace); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectFilePathAndDisplayNameEvaluationHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectFilePathAndDisplayNameEvaluationHandlerTests.cs index a9f9070bc8..c62dee3149 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectFilePathAndDisplayNameEvaluationHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectFilePathAndDisplayNameEvaluationHandlerTests.cs @@ -3,135 +3,134 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.Configuration; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public class ProjectFilePathAndDisplayNameEvaluationHandlerTests : EvaluationHandlerTestBase { - public class ProjectFilePathAndDisplayNameEvaluationHandlerTests : EvaluationHandlerTestBase + [Fact] + public void Handle_WhenMSBuildProjectFullPathPropertyNotChanged_DoesNothing() { - [Fact] - public void Handle_WhenMSBuildProjectFullPathPropertyNotChanged_DoesNothing() - { - var context = IWorkspaceProjectContextMockFactory.Create(); - context.ProjectFilePath = @"ProjectFilePath"; - context.DisplayName = "DisplayName"; - - var handler = CreateInstance(context: context); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true - } + var context = IWorkspaceProjectContextMockFactory.Create(); + context.ProjectFilePath = @"ProjectFilePath"; + context.DisplayName = "DisplayName"; + + var handler = CreateInstance(context: context); + + var projectChange = IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true } - """); - - Handle(context, handler, projectChange); - - Assert.Equal(@"ProjectFilePath", context.ProjectFilePath); - Assert.Equal(@"DisplayName", context.DisplayName); - } - - [Fact] - public void Handle_WhenMSBuildProjectFullPathPropertyChanged_SetsProjectFilePath() - { - var context = IWorkspaceProjectContextMockFactory.Create(); - context.ProjectFilePath = @"ProjectFilePath"; - - var handler = CreateInstance(context: context); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "ChangedProperties": [ "MSBuildProjectFullPath" ] - }, - "After": { - "Properties": { - "MSBuildProjectFullPath": "NewProjectFilePath" - } + } + """); + + Handle(context, handler, projectChange); + + Assert.Equal(@"ProjectFilePath", context.ProjectFilePath); + Assert.Equal(@"DisplayName", context.DisplayName); + } + + [Fact] + public void Handle_WhenMSBuildProjectFullPathPropertyChanged_SetsProjectFilePath() + { + var context = IWorkspaceProjectContextMockFactory.Create(); + context.ProjectFilePath = @"ProjectFilePath"; + + var handler = CreateInstance(context: context); + + var projectChange = IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "ChangedProperties": [ "MSBuildProjectFullPath" ] + }, + "After": { + "Properties": { + "MSBuildProjectFullPath": "NewProjectFilePath" } } - """); - Handle(context, handler, projectChange); - - Assert.Equal(@"NewProjectFilePath", context.ProjectFilePath); - } - - [Fact] - public void Handle_WhenMSBuildProjectFullPathPropertyChanged_SetsDisplayNameToFileNameWithoutExtension() - { - var context = IWorkspaceProjectContextMockFactory.Create(); - - var handler = CreateInstance(context: context); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "ChangedProperties": [ "MSBuildProjectFullPath" ] - }, - "After": { - "Properties": { - "MSBuildProjectFullPath": "C:\\Project\\Project.csproj" - } + } + """); + Handle(context, handler, projectChange); + + Assert.Equal(@"NewProjectFilePath", context.ProjectFilePath); + } + + [Fact] + public void Handle_WhenMSBuildProjectFullPathPropertyChanged_SetsDisplayNameToFileNameWithoutExtension() + { + var context = IWorkspaceProjectContextMockFactory.Create(); + + var handler = CreateInstance(context: context); + + var projectChange = IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "ChangedProperties": [ "MSBuildProjectFullPath" ] + }, + "After": { + "Properties": { + "MSBuildProjectFullPath": "C:\\Project\\Project.csproj" } } - """); - Handle(context, handler, projectChange); - - Assert.Equal(@"Project", context.DisplayName); - } - - [Theory] // Dimension Names Dimension Values Implicit Dimension Names, Expected - [InlineData("Configuration", "Debug", "", "Project")] - [InlineData("Configuration", "Debug", "Configuration", "Project (Debug)")] - [InlineData("Configuration|Platform", "Debug|AnyCPU", "", "Project")] - [InlineData("Configuration|Platform", "Debug|AnyCPU", "Configuration", "Project (Debug)")] - [InlineData("Configuration|Platform", "Debug|AnyCPU", "Configuration|Platform", "Project (Debug, AnyCPU)")] - [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "", "Project")] - [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "Configuration", "Project (Debug)")] - [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "Configuration|Platform", "Project (Debug, AnyCPU)")] - [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "Configuration|Platform|TargetFramework", "Project (Debug, AnyCPU, net45)")] - [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "TargetFramework", "Project (net45)")] - public void Handle_WhenMSBuildProjectFullPathPropertyChangedAndMultitargeting_IncludeDimensionValuesInDisplayName(string dimensionNames, string dimensionValues, string implicitDimensionNames, string expected) - { - var context = IWorkspaceProjectContextMockFactory.Create(); - var implicitlyActiveDimensionProvider = IImplicitlyActiveDimensionProviderFactory.ImplementGetImplicitlyActiveDimensions(n => implicitDimensionNames.SplitReturningEmptyIfEmpty('|')); - var configuration = ProjectConfigurationFactory.Create(dimensionNames, dimensionValues); - var handler = CreateInstance(implicitlyActiveDimensionProvider, context); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "ChangedProperties": [ "MSBuildProjectFullPath" ] - }, - "After": { - "Properties": { - "MSBuildProjectFullPath": "C:\\Project\\Project.csproj" - } + } + """); + Handle(context, handler, projectChange); + + Assert.Equal(@"Project", context.DisplayName); + } + + [Theory] // Dimension Names Dimension Values Implicit Dimension Names, Expected + [InlineData("Configuration", "Debug", "", "Project")] + [InlineData("Configuration", "Debug", "Configuration", "Project (Debug)")] + [InlineData("Configuration|Platform", "Debug|AnyCPU", "", "Project")] + [InlineData("Configuration|Platform", "Debug|AnyCPU", "Configuration", "Project (Debug)")] + [InlineData("Configuration|Platform", "Debug|AnyCPU", "Configuration|Platform", "Project (Debug, AnyCPU)")] + [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "", "Project")] + [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "Configuration", "Project (Debug)")] + [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "Configuration|Platform", "Project (Debug, AnyCPU)")] + [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "Configuration|Platform|TargetFramework", "Project (Debug, AnyCPU, net45)")] + [InlineData("Configuration|Platform|TargetFramework", "Debug|AnyCPU|net45", "TargetFramework", "Project (net45)")] + public void Handle_WhenMSBuildProjectFullPathPropertyChangedAndMultitargeting_IncludeDimensionValuesInDisplayName(string dimensionNames, string dimensionValues, string implicitDimensionNames, string expected) + { + var context = IWorkspaceProjectContextMockFactory.Create(); + var implicitlyActiveDimensionProvider = IImplicitlyActiveDimensionProviderFactory.ImplementGetImplicitlyActiveDimensions(n => implicitDimensionNames.SplitReturningEmptyIfEmpty('|')); + var configuration = ProjectConfigurationFactory.Create(dimensionNames, dimensionValues); + var handler = CreateInstance(implicitlyActiveDimensionProvider, context); + + var projectChange = IProjectChangeDescriptionFactory.FromJson( + """ + { + "Difference": { + "AnyChanges": true, + "ChangedProperties": [ "MSBuildProjectFullPath" ] + }, + "After": { + "Properties": { + "MSBuildProjectFullPath": "C:\\Project\\Project.csproj" } } - """); + } + """); - Handle(context, handler, projectChange, configuration); + Handle(context, handler, projectChange, configuration); - Assert.Equal(expected, context.DisplayName); - } + Assert.Equal(expected, context.DisplayName); + } - internal override IProjectEvaluationHandler CreateInstance() - { - return CreateInstance(null, null); - } + internal override IProjectEvaluationHandler CreateInstance() + { + return CreateInstance(null, null); + } - private static ProjectFilePathAndDisplayNameEvaluationHandler CreateInstance(IImplicitlyActiveDimensionProvider? implicitlyActiveDimensionProvider = null, IWorkspaceProjectContext? context = null) - { - implicitlyActiveDimensionProvider ??= IImplicitlyActiveDimensionProviderFactory.Create(); + private static ProjectFilePathAndDisplayNameEvaluationHandler CreateInstance(IImplicitlyActiveDimensionProvider? implicitlyActiveDimensionProvider = null, IWorkspaceProjectContext? context = null) + { + implicitlyActiveDimensionProvider ??= IImplicitlyActiveDimensionProviderFactory.Create(); - return new ProjectFilePathAndDisplayNameEvaluationHandler(implicitlyActiveDimensionProvider); - } + return new ProjectFilePathAndDisplayNameEvaluationHandler(implicitlyActiveDimensionProvider); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectPropertiesItemHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectPropertiesItemHandlerTests.cs index 845a10dc14..fd7c87f783 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectPropertiesItemHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/ProjectPropertiesItemHandlerTests.cs @@ -2,103 +2,25 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public class ProjectPropertiesItemHandlerTests : EvaluationHandlerTestBase { - public class ProjectPropertiesItemHandlerTests : EvaluationHandlerTestBase + [Fact] + public void Handle_WhenPropertyIsChanged_CallsSetProperty() { - [Fact] - public void Handle_WhenPropertyIsChanged_CallsSetProperty() - { - string? nameResult = null; - string? valueResult = null; - var context = IWorkspaceProjectContextMockFactory.ImplementSetProperty((name, value) => { nameResult = name; valueResult = value; }); - - var handler = CreateInstance(); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "ChangedProperties": [ "RootNamespace" ] - }, - "After": { - "Properties": { - "RootNamespace": "value" - } - } - } - """); + string? nameResult = null; + string? valueResult = null; + var context = IWorkspaceProjectContextMockFactory.ImplementSetProperty((name, value) => { nameResult = name; valueResult = value; }); - Handle(context, handler, projectChange); + var handler = CreateInstance(); - Assert.Equal("RootNamespace", nameResult); - Assert.Equal("value", valueResult); - } - - [Fact] - public void Handle_WhenTargetPathIsChanged_SetsBinOutputPath() - { - var context = IWorkspaceProjectContextMockFactory.Create(); - context.BinOutputPath = @"BinOutputPath"; - - var handler = CreateInstance(); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "ChangedProperties": [ "TargetPath" ] - }, - "After": { - "Properties": { - "TargetPath": "NewBinOutputPath" - } - } - } - """); - - Handle(context, handler, projectChange); - - Assert.Equal("NewBinOutputPath", context.BinOutputPath); - } - - [Fact] - public void Handle_WhenTargetPathIsNotChanged_DoesNotSetBinOutputPath() - { - var context = IWorkspaceProjectContextMockFactory.Create(); - context.BinOutputPath = @"BinOutputPath"; - - var handler = CreateInstance(); - - var projectChange = IProjectChangeDescriptionFactory.FromJson( - """ - { - "Difference": { - "AnyChanges": true, - "ChangedProperties": [ ] - }, - "After": { - "Properties": { - "TargetPath": "NewBinOutputPath" - } - } - } - """); - - Handle(context, handler, projectChange); - - Assert.Equal("BinOutputPath", context.BinOutputPath); - } - - [Theory] - [InlineData( + var projectChange = IProjectChangeDescriptionFactory.FromJson( """ { "Difference": { - "AnyChanges": false, - "ChangedProperties": [ ] + "AnyChanges": true, + "ChangedProperties": [ "RootNamespace" ] }, "After": { "Properties": { @@ -106,8 +28,23 @@ public void Handle_WhenTargetPathIsNotChanged_DoesNotSetBinOutputPath() } } } - """)] - [InlineData( + """); + + Handle(context, handler, projectChange); + + Assert.Equal("RootNamespace", nameResult); + Assert.Equal("value", valueResult); + } + + [Fact] + public void Handle_WhenTargetPathIsChanged_SetsBinOutputPath() + { + var context = IWorkspaceProjectContextMockFactory.Create(); + context.BinOutputPath = @"BinOutputPath"; + + var handler = CreateInstance(); + + var projectChange = IProjectChangeDescriptionFactory.FromJson( """ { "Difference": { @@ -116,12 +53,26 @@ public void Handle_WhenTargetPathIsNotChanged_DoesNotSetBinOutputPath() }, "After": { "Properties": { - "TargetPath": "value" + "TargetPath": "NewBinOutputPath" } } } - """)] - [InlineData( + """); + + Handle(context, handler, projectChange); + + Assert.Equal("NewBinOutputPath", context.BinOutputPath); + } + + [Fact] + public void Handle_WhenTargetPathIsNotChanged_DoesNotSetBinOutputPath() + { + var context = IWorkspaceProjectContextMockFactory.Create(); + context.BinOutputPath = @"BinOutputPath"; + + var handler = CreateInstance(); + + var projectChange = IProjectChangeDescriptionFactory.FromJson( """ { "Difference": { @@ -130,28 +81,76 @@ public void Handle_WhenTargetPathIsNotChanged_DoesNotSetBinOutputPath() }, "After": { "Properties": { - "RootNamespace": "value" + "TargetPath": "NewBinOutputPath" } } } - """)] - public void Handle_WhenPropertyIsNotChanged_DoesNotCallSetProperty(string input) - { - int callCount = 0; - var context = IWorkspaceProjectContextMockFactory.ImplementSetProperty((name, value) => { callCount++; }); - - var handler = CreateInstance(); + """); - var projectChange = IProjectChangeDescriptionFactory.FromJson(input); + Handle(context, handler, projectChange); - Handle(context, handler, projectChange); + Assert.Equal("BinOutputPath", context.BinOutputPath); + } - Assert.Equal(0, callCount); + [Theory] + [InlineData( + """ + { + "Difference": { + "AnyChanges": false, + "ChangedProperties": [ ] + }, + "After": { + "Properties": { + "RootNamespace": "value" + } + } } - - internal override IProjectEvaluationHandler CreateInstance() + """)] + [InlineData( + """ { - return new ProjectPropertiesItemHandler(); + "Difference": { + "AnyChanges": true, + "ChangedProperties": [ "TargetPath" ] + }, + "After": { + "Properties": { + "TargetPath": "value" + } + } } + """)] + [InlineData( + """ + { + "Difference": { + "AnyChanges": true, + "ChangedProperties": [ ] + }, + "After": { + "Properties": { + "RootNamespace": "value" + } + } + } + """)] + public void Handle_WhenPropertyIsNotChanged_DoesNotCallSetProperty(string input) + { + int callCount = 0; + var context = IWorkspaceProjectContextMockFactory.ImplementSetProperty((name, value) => { callCount++; }); + + var handler = CreateInstance(); + + var projectChange = IProjectChangeDescriptionFactory.FromJson(input); + + Handle(context, handler, projectChange); + + Assert.Equal(0, callCount); + } + + internal override IProjectEvaluationHandler CreateInstance() + { + return new ProjectPropertiesItemHandler(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/SourceItemsHandlerTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/SourceItemsHandlerTestBase.cs index d8232b9cbc..968225eec4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/SourceItemsHandlerTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/Handlers/SourceItemsHandlerTestBase.cs @@ -2,15 +2,14 @@ using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers +namespace Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers; + +public abstract class SourceItemsHandlerTestBase { - public abstract class SourceItemsHandlerTestBase + internal static void Handle(IWorkspaceProjectContext context, ISourceItemsHandler handler, IImmutableDictionary projectChanges) { - internal static void Handle(IWorkspaceProjectContext context, ISourceItemsHandler handler, IImmutableDictionary projectChanges) - { - handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); - } - - internal abstract ISourceItemsHandler CreateInstance(); + handler.Handle(context, projectChanges, new ContextState(), IManagedProjectDiagnosticOutputServiceFactory.Create()); } + + internal abstract ISourceItemsHandler CreateInstance(); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProviderTests.cs index c28f4cd5fd..ef6c9f6ed1 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VisualBasic/VisualBasicCodeDomProviderTests.cs @@ -1,13 +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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices.VisualBasic; + +public class VisualBasicCodeDomProviderTests { - public class VisualBasicCodeDomProviderTests + [Fact] + public void Constructor_DoesNotThrow() { - [Fact] - public void Constructor_DoesNotThrow() - { - new VisualBasicCodeDomProvider(); - } + new VisualBasicCodeDomProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactoryTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactoryTests.cs index 1d9224b640..19f30fb087 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactoryTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/LanguageServices/VsContainedLanguageComponentsFactoryTests.cs @@ -5,120 +5,119 @@ using Microsoft.VisualStudio.TextManager.Interop; using IOleAsyncServiceProvider = Microsoft.VisualStudio.Shell.Interop.COMAsyncServiceProvider.IAsyncServiceProvider; -namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices +namespace Microsoft.VisualStudio.ProjectSystem.VS.LanguageServices; + +public class VsContainedLanguageComponentsFactoryTests { - public class VsContainedLanguageComponentsFactoryTests + private const string LanguageServiceId = "{517FA117-46EB-4402-A0D5-D4B7D89FCC33}"; + + [Fact] + public void GetContainedLanguageFactoryForFile_WhenIsDocumentInProjectFails_ReturnE_FAIL() + { + var project = IVsProject_Factory.ImplementIsDocumentInProject(HResult.Fail); + + var factory = CreateInstance(project); + + var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); + + AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); + } + + [Fact] + public void GetContainedLanguageFactoryForFile_WhenFilePathNotFound_ReturnE_FAIL() + { + var project = IVsProject_Factory.ImplementIsDocumentInProject(found: false); + + var factory = CreateInstance(project); + + var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); + + AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); + } + + [Theory] + [InlineData("")] + [InlineData("ABC")] + [InlineData("ABCD-ABC")] + public void GetContainedLanguageFactoryForFile_WhenLanguageServiceIdEmptyOrInvalid_ReturnE_FAIL(string languageServiceId) + { + var project = IVsProject_Factory.ImplementIsDocumentInProject(found: true); + var properties = ProjectPropertiesFactory.Create(ConfigurationGeneral.SchemaName, ConfigurationGeneral.LanguageServiceIdProperty, languageServiceId); + + var factory = CreateInstance(project, properties: properties); + + var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); + + AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); + } + + [Fact] + public void GetContainedLanguageFactoryForFile_WhenNoContainedLanguageFactory_ReturnE_FAIL() { - private const string LanguageServiceId = "{517FA117-46EB-4402-A0D5-D4B7D89FCC33}"; + var project = IVsProject_Factory.ImplementIsDocumentInProject(found: true); + var properties = ProjectPropertiesFactory.Create(ConfigurationGeneral.SchemaName, ConfigurationGeneral.LanguageServiceIdProperty, LanguageServiceId); - [Fact] - public void GetContainedLanguageFactoryForFile_WhenIsDocumentInProjectFails_ReturnE_FAIL() - { - var project = IVsProject_Factory.ImplementIsDocumentInProject(HResult.Fail); + var factory = CreateInstance(project, containedLanguageFactory: null!, properties: properties); + + var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); + + AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); + } + + [Fact] + public void GetContainedLanguageFactoryForFile_WhenReturnsResult_ReturnsS_OK() + { + var hierarchy = IVsHierarchyFactory.Create(); + var project = IVsProject_Factory.ImplementIsDocumentInProject(found: true, itemid: 1); + var properties = ProjectPropertiesFactory.Create(ConfigurationGeneral.SchemaName, ConfigurationGeneral.LanguageServiceIdProperty, LanguageServiceId); + var containedLanguageFactory = IVsContainedLanguageFactoryFactory.Create(); + + var factory = CreateInstance(project, containedLanguageFactory: containedLanguageFactory, hierarchy: hierarchy, properties: properties); + + var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); + + Assert.Equal(VSConstants.S_OK, result); + Assert.Same(hierarchy, hierarchyResult); + Assert.Same(containedLanguageFactory, containedLanguageFactoryResult); + Assert.Equal(1u, itemIdResult); + } + + private static void AssertFailed(int result, IVsHierarchy? hierarchy, uint itemid, IVsContainedLanguageFactory? containedLanguageFactory) + { + Assert.Equal(VSConstants.E_FAIL, result); + Assert.Null(hierarchy); + Assert.Null(containedLanguageFactory); + Assert.Equal((uint)VSConstants.VSITEMID.Nil, itemid); + } + + private static VsContainedLanguageComponentsFactory CreateInstance( + IVsProject4 project, + IVsContainedLanguageFactory? containedLanguageFactory = null, + IVsHierarchy? hierarchy = null, + ProjectProperties? properties = null, + IWorkspaceWriter? workspaceWriter = null) + { + var serviceProvider = IOleAsyncServiceProviderFactory.ImplementQueryServiceAsync(containedLanguageFactory, new Guid(LanguageServiceId)); + + var projectVsServices = new IUnconfiguredProjectVsServicesMock(); + projectVsServices.ImplementVsHierarchy(hierarchy); + projectVsServices.ImplementVsProject(project); + projectVsServices.ImplementThreadingService(IProjectThreadingServiceFactory.Create()); + projectVsServices.ImplementActiveConfiguredProjectProperties(properties); + + return CreateInstance(serviceProvider, projectVsServices.Object, workspaceWriter); + } + + private static VsContainedLanguageComponentsFactory CreateInstance( + IOleAsyncServiceProvider? serviceProvider = null, + IUnconfiguredProjectVsServices? projectVsServices = null, + IWorkspaceWriter? workspaceWriter = null) + { + projectVsServices ??= IUnconfiguredProjectVsServicesFactory.Create(); + workspaceWriter ??= IWorkspaceWriterFactory.Create(); - var factory = CreateInstance(project); - - var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); - - AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); - } - - [Fact] - public void GetContainedLanguageFactoryForFile_WhenFilePathNotFound_ReturnE_FAIL() - { - var project = IVsProject_Factory.ImplementIsDocumentInProject(found: false); - - var factory = CreateInstance(project); - - var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); - - AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); - } - - [Theory] - [InlineData("")] - [InlineData("ABC")] - [InlineData("ABCD-ABC")] - public void GetContainedLanguageFactoryForFile_WhenLanguageServiceIdEmptyOrInvalid_ReturnE_FAIL(string languageServiceId) - { - var project = IVsProject_Factory.ImplementIsDocumentInProject(found: true); - var properties = ProjectPropertiesFactory.Create(ConfigurationGeneral.SchemaName, ConfigurationGeneral.LanguageServiceIdProperty, languageServiceId); - - var factory = CreateInstance(project, properties: properties); - - var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); - - AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); - } - - [Fact] - public void GetContainedLanguageFactoryForFile_WhenNoContainedLanguageFactory_ReturnE_FAIL() - { - var project = IVsProject_Factory.ImplementIsDocumentInProject(found: true); - var properties = ProjectPropertiesFactory.Create(ConfigurationGeneral.SchemaName, ConfigurationGeneral.LanguageServiceIdProperty, LanguageServiceId); - - var factory = CreateInstance(project, containedLanguageFactory: null!, properties: properties); - - var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); - - AssertFailed(result, hierarchyResult, itemIdResult, containedLanguageFactoryResult); - } - - [Fact] - public void GetContainedLanguageFactoryForFile_WhenReturnsResult_ReturnsS_OK() - { - var hierarchy = IVsHierarchyFactory.Create(); - var project = IVsProject_Factory.ImplementIsDocumentInProject(found: true, itemid: 1); - var properties = ProjectPropertiesFactory.Create(ConfigurationGeneral.SchemaName, ConfigurationGeneral.LanguageServiceIdProperty, LanguageServiceId); - var containedLanguageFactory = IVsContainedLanguageFactoryFactory.Create(); - - var factory = CreateInstance(project, containedLanguageFactory: containedLanguageFactory, hierarchy: hierarchy, properties: properties); - - var result = factory.GetContainedLanguageFactoryForFile("FilePath", out var hierarchyResult, out var itemIdResult, out var containedLanguageFactoryResult); - - Assert.Equal(VSConstants.S_OK, result); - Assert.Same(hierarchy, hierarchyResult); - Assert.Same(containedLanguageFactory, containedLanguageFactoryResult); - Assert.Equal(1u, itemIdResult); - } - - private static void AssertFailed(int result, IVsHierarchy? hierarchy, uint itemid, IVsContainedLanguageFactory? containedLanguageFactory) - { - Assert.Equal(VSConstants.E_FAIL, result); - Assert.Null(hierarchy); - Assert.Null(containedLanguageFactory); - Assert.Equal((uint)VSConstants.VSITEMID.Nil, itemid); - } - - private static VsContainedLanguageComponentsFactory CreateInstance( - IVsProject4 project, - IVsContainedLanguageFactory? containedLanguageFactory = null, - IVsHierarchy? hierarchy = null, - ProjectProperties? properties = null, - IWorkspaceWriter? workspaceWriter = null) - { - var serviceProvider = IOleAsyncServiceProviderFactory.ImplementQueryServiceAsync(containedLanguageFactory, new Guid(LanguageServiceId)); - - var projectVsServices = new IUnconfiguredProjectVsServicesMock(); - projectVsServices.ImplementVsHierarchy(hierarchy); - projectVsServices.ImplementVsProject(project); - projectVsServices.ImplementThreadingService(IProjectThreadingServiceFactory.Create()); - projectVsServices.ImplementActiveConfiguredProjectProperties(properties); - - return CreateInstance(serviceProvider, projectVsServices.Object, workspaceWriter); - } - - private static VsContainedLanguageComponentsFactory CreateInstance( - IOleAsyncServiceProvider? serviceProvider = null, - IUnconfiguredProjectVsServices? projectVsServices = null, - IWorkspaceWriter? workspaceWriter = null) - { - projectVsServices ??= IUnconfiguredProjectVsServicesFactory.Create(); - workspaceWriter ??= IWorkspaceWriterFactory.Create(); - - return new VsContainedLanguageComponentsFactory(IVsServiceFactory.Create(serviceProvider!), - projectVsServices, - workspaceWriter); - } + return new VsContainedLanguageComponentsFactory(IVsServiceFactory.Create(serviceProvider!), + projectVsServices, + workspaceWriter); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoadTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoadTests.cs index fd6ec21746..caeaecd09c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoadTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/ProjectCapabilitiesMissingVetoProjectLoadTests.cs @@ -1,63 +1,62 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public class ProjectCapabilitiesMissingVetoProjectLoadTests { - public class ProjectCapabilitiesMissingVetoProjectLoadTests + [Theory] + [InlineData("Project.vbproj")] + [InlineData("Project.csproj")] + [InlineData("Project.fsproj")] + public async Task AllowProjectLoadAsync_WhenMissingCapability_ReturnsFail(string fullPath) { - [Theory] - [InlineData("Project.vbproj")] - [InlineData("Project.csproj")] - [InlineData("Project.fsproj")] - public async Task AllowProjectLoadAsync_WhenMissingCapability_ReturnsFail(string fullPath) - { - var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); - var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(c => false); + var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); + var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(c => false); - var veto = CreateInstance(project, capabilitiesService); + var veto = CreateInstance(project, capabilitiesService); - var result = await Assert.ThrowsAnyAsync(() => - { - return veto.AllowProjectLoadAsync(true, ProjectConfigurationFactory.Create("Debug|AnyCPU"), CancellationToken.None); - }); + var result = await Assert.ThrowsAnyAsync(() => + { + return veto.AllowProjectLoadAsync(true, ProjectConfigurationFactory.Create("Debug|AnyCPU"), CancellationToken.None); + }); - Assert.Equal(VSConstants.E_FAIL, result.HResult); - } + Assert.Equal(VSConstants.E_FAIL, result.HResult); + } - [Theory] - [InlineData("Project.vbproj")] - [InlineData("Project.csproj")] - [InlineData("Project.fsproj")] - public async Task AllowProjectLoadAsync_WhenContainsCapability_ReturnsTrue(string fullPath) - { - var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); - var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(c => true); + [Theory] + [InlineData("Project.vbproj")] + [InlineData("Project.csproj")] + [InlineData("Project.fsproj")] + public async Task AllowProjectLoadAsync_WhenContainsCapability_ReturnsTrue(string fullPath) + { + var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); + var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(c => true); - var veto = CreateInstance(project, capabilitiesService); + var veto = CreateInstance(project, capabilitiesService); - var result = await veto.AllowProjectLoadAsync(true, ProjectConfigurationFactory.Create("Debug|AnyCPU"), CancellationToken.None); + var result = await veto.AllowProjectLoadAsync(true, ProjectConfigurationFactory.Create("Debug|AnyCPU"), CancellationToken.None); - Assert.True(result); - } + Assert.True(result); + } - [Theory] - [InlineData("Project.vcxproj")] - [InlineData("Project.shproj")] - [InlineData("Project.androidproj")] - public async Task AllowProjectLoadAsync_WhenUnrecognizedExtension_ReturnsTrue(string fullPath) - { - var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); - var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(c => throw new Exception("Should not be hit")); + [Theory] + [InlineData("Project.vcxproj")] + [InlineData("Project.shproj")] + [InlineData("Project.androidproj")] + public async Task AllowProjectLoadAsync_WhenUnrecognizedExtension_ReturnsTrue(string fullPath) + { + var project = UnconfiguredProjectFactory.ImplementFullPath(fullPath); + var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(c => throw new Exception("Should not be hit")); - var veto = CreateInstance(project, capabilitiesService); + var veto = CreateInstance(project, capabilitiesService); - var result = await veto.AllowProjectLoadAsync(true, ProjectConfigurationFactory.Create("Debug|AnyCPU"), CancellationToken.None); + var result = await veto.AllowProjectLoadAsync(true, ProjectConfigurationFactory.Create("Debug|AnyCPU"), CancellationToken.None); - Assert.True(result); - } + Assert.True(result); + } - private static ProjectCapabilitiesMissingVetoProjectLoad CreateInstance(UnconfiguredProject project, IProjectCapabilitiesService projectCapabilitiesService) - { - return new ProjectCapabilitiesMissingVetoProjectLoad(project, projectCapabilitiesService); - } + private static ProjectCapabilitiesMissingVetoProjectLoad CreateInstance(UnconfiguredProject project, IProjectCapabilitiesService projectCapabilitiesService) + { + return new ProjectCapabilitiesMissingVetoProjectLoad(project, projectCapabilitiesService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AuthenticationModeEnumProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AuthenticationModeEnumProviderTests.cs index 7e70dc0e6e..302d06254a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AuthenticationModeEnumProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AuthenticationModeEnumProviderTests.cs @@ -3,49 +3,48 @@ using Microsoft.VisualStudio.Mocks; using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class AuthenticationModeEnumProviderTests { - public class AuthenticationModeEnumProviderTests + [Fact] + public async Task GetProviderAsync_ReturnsNonNullGenerator() { - [Fact] - public async Task GetProviderAsync_ReturnsNonNullGenerator() - { - var remoteDebuggerAuthenticationService = IRemoteDebuggerAuthenticationServiceFactory.Create(); + var remoteDebuggerAuthenticationService = IRemoteDebuggerAuthenticationServiceFactory.Create(); - var provider = new AuthenticationModeEnumProvider(remoteDebuggerAuthenticationService); - var generator = await provider.GetProviderAsync(options: null); + var provider = new AuthenticationModeEnumProvider(remoteDebuggerAuthenticationService); + var generator = await provider.GetProviderAsync(options: null); - Assert.NotNull(generator); - } + Assert.NotNull(generator); + } - [Fact] - public async Task TryCreateEnumValueAsync_ReturnsNull() - { - var remoteDebuggerAuthenticationService = IRemoteDebuggerAuthenticationServiceFactory.Create(); + [Fact] + public async Task TryCreateEnumValueAsync_ReturnsNull() + { + var remoteDebuggerAuthenticationService = IRemoteDebuggerAuthenticationServiceFactory.Create(); - var provider = new AuthenticationModeEnumProvider(remoteDebuggerAuthenticationService); - var generator = await provider.GetProviderAsync(options: null); + var provider = new AuthenticationModeEnumProvider(remoteDebuggerAuthenticationService); + var generator = await provider.GetProviderAsync(options: null); - Assert.Null(await generator.TryCreateEnumValueAsync("MyMode")); - } + Assert.Null(await generator.TryCreateEnumValueAsync("MyMode")); + } - [Fact] - public async Task GetListValuesAsync_ReturnsPageNamesAndDisplayNames() - { - var remoteDebuggerAuthenticationService = IRemoteDebuggerAuthenticationServiceFactory.Create( - IRemoteAuthenticationProviderFactory.Create("cheetah", "Fast authentication!"), - IRemoteAuthenticationProviderFactory.Create("tortoise", "Sloooow authentication...")); + [Fact] + public async Task GetListValuesAsync_ReturnsPageNamesAndDisplayNames() + { + var remoteDebuggerAuthenticationService = IRemoteDebuggerAuthenticationServiceFactory.Create( + IRemoteAuthenticationProviderFactory.Create("cheetah", "Fast authentication!"), + IRemoteAuthenticationProviderFactory.Create("tortoise", "Sloooow authentication...")); - var provider = new AuthenticationModeEnumProvider(remoteDebuggerAuthenticationService); - var generator = await provider.GetProviderAsync(options: null); + var provider = new AuthenticationModeEnumProvider(remoteDebuggerAuthenticationService); + var generator = await provider.GetProviderAsync(options: null); - var values = await generator.GetListedValuesAsync(); + var values = await generator.GetListedValuesAsync(); - Assert.Collection(values, new Action[] - { - ev => { Assert.Equal(expected: "cheetah", actual: ev.Name); Assert.Equal(expected: "Fast authentication!", actual: ev.DisplayName); }, - ev => { Assert.Equal(expected: "tortoise", actual: ev.Name); Assert.Equal(expected: "Sloooow authentication...", actual: ev.DisplayName); } - }); - } + Assert.Collection(values, new Action[] + { + ev => { Assert.Equal(expected: "cheetah", actual: ev.Name); Assert.Equal(expected: "Fast authentication!", actual: ev.DisplayName); }, + ev => { Assert.Equal(expected: "tortoise", actual: ev.Name); Assert.Equal(expected: "Sloooow authentication...", actual: ev.DisplayName); } + }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProviderTests.cs index df69960b61..ae1db12774 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/AvoidPersistingProjectGuidStorageProviderTests.cs @@ -3,456 +3,455 @@ using Microsoft.Build; using Microsoft.Build.Construction; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class AvoidPersistingProjectGuidStorageProviderTests { - public class AvoidPersistingProjectGuidStorageProviderTests - { - [Theory] - [InlineData( - """ - - """ - )] - [InlineData( - """ - + [Theory] + [InlineData( + """ + + """ + )] + [InlineData( + """ + + + + + """ + )] + [InlineData( + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + + """ + )] + [InlineData( + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - """ - )] - [InlineData( - """ + + + """ + )] + public async Task GetProjectGuidAsync_WhenNoProjectGuidProperty_ReturnsEmpty(string projectXml) + { + var provider = CreateInstance(projectXml); + + var result = await provider.GetProjectGuidAsync(); + + Assert.Equal(Guid.Empty, result); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("Not a guid")] + [InlineData("{42C4D8D7-3011-4EBA-AA8F-94AE42EFE399-Bar}")] + [InlineData("{42C4D8D7-3011-4EBA- AA8F-94AE42EFE399}")] + [InlineData("{ 0xFFFFFFFFFFFFFFFFFF,0xdddd,0xdddd,{ 0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd} }")] // Overflow + public async Task GetProjectGuidAsync_WhenInvalidProjectGuidProperty_ReturnsEmpty(string guid) + { + var projectXml = + $""" - {C26D43ED-ED18-46F9-8950-0B1A7232746E} + {guid} - """ - )] - [InlineData( - """ - - - - - - """ - )] - [InlineData( - """ - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - - """ - )] - public async Task GetProjectGuidAsync_WhenNoProjectGuidProperty_ReturnsEmpty(string projectXml) - { - var provider = CreateInstance(projectXml); + """; - var result = await provider.GetProjectGuidAsync(); + var provider = CreateInstance(projectXml); - Assert.Equal(Guid.Empty, result); - } + var result = await provider.GetProjectGuidAsync(); - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData("Not a guid")] - [InlineData("{42C4D8D7-3011-4EBA-AA8F-94AE42EFE399-Bar}")] - [InlineData("{42C4D8D7-3011-4EBA- AA8F-94AE42EFE399}")] - [InlineData("{ 0xFFFFFFFFFFFFFFFFFF,0xdddd,0xdddd,{ 0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd} }")] // Overflow - public async Task GetProjectGuidAsync_WhenInvalidProjectGuidProperty_ReturnsEmpty(string guid) - { - var projectXml = - $""" - - - {guid} - - - """; + Assert.Equal(Guid.Empty, result); + } - var provider = CreateInstance(projectXml); + [Theory] + [InlineData( + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] - var result = await provider.GetProjectGuidAsync(); + [InlineData( + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + {D110509C-066B-434E-B456-15B71F0DA330} + + + """ + )] + [InlineData( + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """ + )] + [InlineData( + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + public async Task GetProjectGuidAsync_ReturnsFirstProjectGuidIgnoringConditions(string projectXml) + { + var provider = CreateInstance(projectXml); - Assert.Equal(Guid.Empty, result); - } + var result = await provider.GetProjectGuidAsync(); - [Theory] - [InlineData( - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] + Assert.Equal(new Guid("C26D43ED-ED18-46F9-8950-0B1A7232746E"), result); + } - [InlineData( - """ - - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ + [Theory] + [InlineData("c26d43eded1846f989500b1a7232746e")] + [InlineData("C26D43EDED1846F989500B1A7232746E")] + [InlineData("c26d43ed-ed18-46f9-8950-0b1a7232746e")] + [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E")] + [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e}")] + [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e} ")] + [InlineData("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")] + [InlineData("(c26d43ed-ed18-46f9-8950-0b1a7232746e)")] + [InlineData("(C26D43ED-ED18-46F9-8950-0B1A7232746E)")] + [InlineData("{0xc26d43ed,0xed18,0x46f9,{0x89,0x50,0x0b,0x1a,0x72,0x32,0x74,0x6e}}")] + [InlineData("{0XC26D43ED,0XED18,0X46F9,{0X89,0X50,0X0B,0X1A,0X72,0X32,0X74,0X6E}}")] + [InlineData(" C26D43ED-ED18-46F9-8950-0B1A7232746E")] + [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E ")] + [InlineData("C26D43EDED1846F989500B1%417232746E")] // With escaped characters + public async Task GetProjectGuidAsync_ReturnsProjectGuid(string guid) + { + var projectXml = + $""" - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - {D110509C-066B-434E-B456-15B71F0DA330} + {guid} - """ - )] - [InlineData( - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """ - )] - [InlineData( - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - public async Task GetProjectGuidAsync_ReturnsFirstProjectGuidIgnoringConditions(string projectXml) - { - var provider = CreateInstance(projectXml); + """; - var result = await provider.GetProjectGuidAsync(); + var provider = CreateInstance(projectXml); - Assert.Equal(new Guid("C26D43ED-ED18-46F9-8950-0B1A7232746E"), result); - } + var result = await provider.GetProjectGuidAsync(); - [Theory] - [InlineData("c26d43eded1846f989500b1a7232746e")] - [InlineData("C26D43EDED1846F989500B1A7232746E")] - [InlineData("c26d43ed-ed18-46f9-8950-0b1a7232746e")] - [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E")] - [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e}")] - [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e} ")] - [InlineData("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")] - [InlineData("(c26d43ed-ed18-46f9-8950-0b1a7232746e)")] - [InlineData("(C26D43ED-ED18-46F9-8950-0B1A7232746E)")] - [InlineData("{0xc26d43ed,0xed18,0x46f9,{0x89,0x50,0x0b,0x1a,0x72,0x32,0x74,0x6e}}")] - [InlineData("{0XC26D43ED,0XED18,0X46F9,{0X89,0X50,0X0B,0X1A,0X72,0X32,0X74,0X6E}}")] - [InlineData(" C26D43ED-ED18-46F9-8950-0B1A7232746E")] - [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E ")] - [InlineData("C26D43EDED1846F989500B1%417232746E")] // With escaped characters - public async Task GetProjectGuidAsync_ReturnsProjectGuid(string guid) - { - var projectXml = - $""" - - - {guid} - - - """; + Assert.Equal(new Guid("C26D43ED-ED18-46F9-8950-0B1A7232746E"), result); + } - var provider = CreateInstance(projectXml); + [Theory] + [InlineData( + """ + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """, + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """, + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """, + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """, + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + + + {D110509C-066B-434E-B456-15B71F0DA330} + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """, + """ + + + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """, + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + [InlineData( + """ + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """, + """ + + + {C26D43ED-ED18-46F9-8950-0B1A7232746E} + + + """ + )] + public async Task SetProjectGuidAsync_SetsFirstProjectGuidIgnoringConditions(string input, string expected) + { + var result = ProjectRootElementFactory.Create(input); + var provider = CreateInstance(result); - var result = await provider.GetProjectGuidAsync(); + await provider.SetProjectGuidAsync(new Guid("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")); - Assert.Equal(new Guid("C26D43ED-ED18-46F9-8950-0B1A7232746E"), result); - } + MSBuildAssert.AssertProjectXml(expected, result); + } - [Theory] - [InlineData( - """ - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """, - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """, - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """, - """ - - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """, - """ - - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - - - {D110509C-066B-434E-B456-15B71F0DA330} - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """, - """ - - - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - [InlineData( - """ - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """, - """ + [Theory] + [InlineData("c26d43eded1846f989500b1a7232746e")] + [InlineData("C26D43EDED1846F989500B1A7232746E")] + [InlineData("c26d43ed-ed18-46f9-8950-0b1a7232746e")] + [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E")] + [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e}")] + [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e} ")] + [InlineData("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")] + [InlineData("(c26d43ed-ed18-46f9-8950-0b1a7232746e)")] + [InlineData("(C26D43ED-ED18-46F9-8950-0B1A7232746E)")] + [InlineData("{0xc26d43ed,0xed18,0x46f9,{0x89,0x50,0x0b,0x1a,0x72,0x32,0x74,0x6e}}")] + [InlineData("{0XC26D43ED,0XED18,0X46F9,{0X89,0X50,0X0B,0X1A,0X72,0X32,0X74,0X6E}}")] + [InlineData(" C26D43ED-ED18-46F9-8950-0B1A7232746E")] + [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E ")] + [InlineData("C26D43EDED1846F989500B1%417232746E")] // With escaped characters + public async Task SetProjectGuidAsync_WhenProjectGuidPropertyAlreadyHasSameGuid_DoesNotSet(string guid) + { + var projectXml = + $""" - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} + {guid} - """ - )] - [InlineData( - """ - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """, - """ - - - {C26D43ED-ED18-46F9-8950-0B1A7232746E} - - - """ - )] - public async Task SetProjectGuidAsync_SetsFirstProjectGuidIgnoringConditions(string input, string expected) - { - var result = ProjectRootElementFactory.Create(input); - var provider = CreateInstance(result); - - await provider.SetProjectGuidAsync(new Guid("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")); - - MSBuildAssert.AssertProjectXml(expected, result); - } - - [Theory] - [InlineData("c26d43eded1846f989500b1a7232746e")] - [InlineData("C26D43EDED1846F989500B1A7232746E")] - [InlineData("c26d43ed-ed18-46f9-8950-0b1a7232746e")] - [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E")] - [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e}")] - [InlineData("{c26d43ed-ed18-46f9-8950-0b1a7232746e} ")] - [InlineData("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")] - [InlineData("(c26d43ed-ed18-46f9-8950-0b1a7232746e)")] - [InlineData("(C26D43ED-ED18-46F9-8950-0B1A7232746E)")] - [InlineData("{0xc26d43ed,0xed18,0x46f9,{0x89,0x50,0x0b,0x1a,0x72,0x32,0x74,0x6e}}")] - [InlineData("{0XC26D43ED,0XED18,0X46F9,{0X89,0X50,0X0B,0X1A,0X72,0X32,0X74,0X6E}}")] - [InlineData(" C26D43ED-ED18-46F9-8950-0B1A7232746E")] - [InlineData("C26D43ED-ED18-46F9-8950-0B1A7232746E ")] - [InlineData("C26D43EDED1846F989500B1%417232746E")] // With escaped characters - public async Task SetProjectGuidAsync_WhenProjectGuidPropertyAlreadyHasSameGuid_DoesNotSet(string guid) - { - var projectXml = - $""" - - - {guid} - - - """; + """; - var result = ProjectRootElementFactory.Create(projectXml); - var provider = CreateInstance(result); + var result = ProjectRootElementFactory.Create(projectXml); + var provider = CreateInstance(result); - await provider.SetProjectGuidAsync(new Guid("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")); + await provider.SetProjectGuidAsync(new Guid("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")); - MSBuildAssert.AssertProjectXml(projectXml, result); - } + MSBuildAssert.AssertProjectXml(projectXml, result); + } - [Theory] - [InlineData( - """ - - """ - )] - [InlineData( - """ - + [Theory] + [InlineData( + """ + + """ + )] + [InlineData( + """ + + + + + """ + )] + [InlineData( + """ + + + {D110509C-066B-434E-B456-15B71F0DA330} + + + """ + )] + [InlineData( + """ + + + + + + """ + )] + [InlineData( + """ + + + {D110509C-066B-434E-B456-15B71F0DA330} - - """ - )] - [InlineData( - """ - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - """ - )] - [InlineData( - """ - - - - - - """ - )] - [InlineData( - """ - - - - {D110509C-066B-434E-B456-15B71F0DA330} - - - - """ - )] - public async Task SetProjectGuidAsync_WhenNoProjectGuidProperty_DoesNotSet(string projectXml) - { - var result = ProjectRootElementFactory.Create(projectXml); - var provider = CreateInstance(result); + + + """ + )] + public async Task SetProjectGuidAsync_WhenNoProjectGuidProperty_DoesNotSet(string projectXml) + { + var result = ProjectRootElementFactory.Create(projectXml); + var provider = CreateInstance(result); - await provider.SetProjectGuidAsync(new Guid("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")); + await provider.SetProjectGuidAsync(new Guid("{C26D43ED-ED18-46F9-8950-0B1A7232746E}")); - MSBuildAssert.AssertProjectXml(projectXml, result); - } + MSBuildAssert.AssertProjectXml(projectXml, result); + } - private static AvoidPersistingProjectGuidStorageProvider CreateInstance(string projectXml) - { - return CreateInstance(ProjectRootElementFactory.Create(projectXml)); - } + private static AvoidPersistingProjectGuidStorageProvider CreateInstance(string projectXml) + { + return CreateInstance(ProjectRootElementFactory.Create(projectXml)); + } - private static AvoidPersistingProjectGuidStorageProvider CreateInstance(ProjectRootElement projectXml) - { - var projectAccessor = IProjectAccessorFactory.Create(projectXml); + private static AvoidPersistingProjectGuidStorageProvider CreateInstance(ProjectRootElement projectXml) + { + var projectAccessor = IProjectAccessorFactory.Create(projectXml); - return new AvoidPersistingProjectGuidStorageProvider(projectAccessor, UnconfiguredProjectFactory.Create()); - } + return new AvoidPersistingProjectGuidStorageProvider(projectAccessor, UnconfiguredProjectFactory.Create()); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationPropertiesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationPropertiesTests.cs index 13bf490736..6189877758 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationPropertiesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectConfigurationPropertiesTests.cs @@ -1,81 +1,80 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp; + +public class CSharpProjectConfigurationPropertiesTests { - public class CSharpProjectConfigurationPropertiesTests + [Fact] + public void Constructor_NullAsProjectProperties_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsProjectProperties_ThrowsArgumentNull() - { - Assert.Throws("projectProperties", () => - { - new CSharpProjectConfigurationProperties(null!, null!); - }); - } - - [Fact] - public void Constructor_NullAsThreadingService_ThrowsArgumentNull() + Assert.Throws("projectProperties", () => { - Assert.Throws("threadingService", () => - { - new CSharpProjectConfigurationProperties(ProjectPropertiesFactory.CreateEmpty(), null!); - }); - } - - [Fact] - public void CSharpProjectConfigurationProperties_CodeAnalysisRuleSet() + new CSharpProjectConfigurationProperties(null!, null!); + }); + } + + [Fact] + public void Constructor_NullAsThreadingService_ThrowsArgumentNull() + { + Assert.Throws("threadingService", () => { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.CodeAnalysisRuleSetProperty, "Blah", setValues); + new CSharpProjectConfigurationProperties(ProjectPropertiesFactory.CreateEmpty(), null!); + }); + } - var projectProperties = ProjectPropertiesFactory.Create(project, data); + [Fact] + public void CSharpProjectConfigurationProperties_CodeAnalysisRuleSet() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.CodeAnalysisRuleSetProperty, "Blah", setValues); - var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); - Assert.Equal("Blah", vsLangProjectProperties.CodeAnalysisRuleSet); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var testValue = "Testing"; - vsLangProjectProperties.CodeAnalysisRuleSet = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); + Assert.Equal("Blah", vsLangProjectProperties.CodeAnalysisRuleSet); - [Fact] - public void CSharpProjectConfigurationProperties_LangVersion() - { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.LangVersionProperty, "6", setValues); + var testValue = "Testing"; + vsLangProjectProperties.CodeAnalysisRuleSet = testValue; + Assert.Equal(setValues.Single(), testValue); + } - var projectProperties = ProjectPropertiesFactory.Create(project, data); + [Fact] + public void CSharpProjectConfigurationProperties_LangVersion() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.LangVersionProperty, "6", setValues); - var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); - Assert.Equal("6", vsLangProjectProperties.LanguageVersion); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var testValue = "7.1"; - vsLangProjectProperties.LanguageVersion = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); + Assert.Equal("6", vsLangProjectProperties.LanguageVersion); - [Fact] - public void CSharpProjectConfigurationProperties_OutputPath() - { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.OutputPathProperty, "OldPath", setValues); + var testValue = "7.1"; + vsLangProjectProperties.LanguageVersion = testValue; + Assert.Equal(setValues.Single(), testValue); + } + + [Fact] + public void CSharpProjectConfigurationProperties_OutputPath() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.OutputPathProperty, "OldPath", setValues); - var projectProperties = ProjectPropertiesFactory.Create(project, data); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); - Assert.Equal("OldPath", vsLangProjectProperties.OutputPath); + var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); + Assert.Equal("OldPath", vsLangProjectProperties.OutputPath); - var testValue = "newPath"; - vsLangProjectProperties.OutputPath = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var testValue = "newPath"; + vsLangProjectProperties.OutputPath = testValue; + Assert.Equal(setValues.Single(), testValue); + } - private static CSharpProjectConfigurationProperties CreateInstance(ProjectProperties projectProperties, IProjectThreadingService projectThreadingService) - { - return new CSharpProjectConfigurationProperties(projectProperties, projectThreadingService); - } + private static CSharpProjectConfigurationProperties CreateInstance(ProjectProperties projectProperties, IProjectThreadingService projectThreadingService) + { + return new CSharpProjectConfigurationProperties(projectProperties, projectThreadingService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProviderTests.cs index 5a38150f8f..e03835f0e4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/CSharp/CSharpProjectDesignerPageProviderTests.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.CSharp; + +public class CSharpProjectDesignerPageProviderTests { - public class CSharpProjectDesignerPageProviderTests + [Fact] + public void Constructor_DoesNotThrow() + { + CreateInstance(); + } + + [Fact] + public async Task GetPagesAsync_WhenAllCapabilitiesPresent_ReturnsPagesInOrder() + { + var provider = CreateInstance(ProjectCapability.LaunchProfiles, ProjectCapability.Pack); + var result = await provider.GetPagesAsync(); + + var expected = ImmutableArray.Create( + CSharpProjectDesignerPage.Application, + CSharpProjectDesignerPage.Build, + CSharpProjectDesignerPage.BuildEvents, + CSharpProjectDesignerPage.Package, + CSharpProjectDesignerPage.Debug, + CSharpProjectDesignerPage.Signing, + CSharpProjectDesignerPage.CodeAnalysis + ); + + Assert.Equal(expected, result); + } + + [Fact] + public async Task GetPagesAsync_WhenNoLaunchProfilesCapability_DoesNotContainDebugPage() + { + var provider = CreateInstance(); + var result = await provider.GetPagesAsync(); + + Assert.DoesNotContain(CSharpProjectDesignerPage.Debug, result); + } + + [Fact] + public async Task GetPagesAsync_WhenNoPackCapability_DoesNotContainPackagePage() + { + var provider = CreateInstance(); + var result = await provider.GetPagesAsync(); + + Assert.DoesNotContain(CSharpProjectDesignerPage.Package, result); + } + + private static CSharpProjectDesignerPageProvider CreateInstance(params string[] capabilities) { - [Fact] - public void Constructor_DoesNotThrow() - { - CreateInstance(); - } - - [Fact] - public async Task GetPagesAsync_WhenAllCapabilitiesPresent_ReturnsPagesInOrder() - { - var provider = CreateInstance(ProjectCapability.LaunchProfiles, ProjectCapability.Pack); - var result = await provider.GetPagesAsync(); - - var expected = ImmutableArray.Create( - CSharpProjectDesignerPage.Application, - CSharpProjectDesignerPage.Build, - CSharpProjectDesignerPage.BuildEvents, - CSharpProjectDesignerPage.Package, - CSharpProjectDesignerPage.Debug, - CSharpProjectDesignerPage.Signing, - CSharpProjectDesignerPage.CodeAnalysis - ); - - Assert.Equal(expected, result); - } - - [Fact] - public async Task GetPagesAsync_WhenNoLaunchProfilesCapability_DoesNotContainDebugPage() - { - var provider = CreateInstance(); - var result = await provider.GetPagesAsync(); - - Assert.DoesNotContain(CSharpProjectDesignerPage.Debug, result); - } - - [Fact] - public async Task GetPagesAsync_WhenNoPackCapability_DoesNotContainPackagePage() - { - var provider = CreateInstance(); - var result = await provider.GetPagesAsync(); - - Assert.DoesNotContain(CSharpProjectDesignerPage.Package, result); - } - - private static CSharpProjectDesignerPageProvider CreateInstance(params string[] capabilities) - { - bool containsCapability(string c) => capabilities.Contains(c); - var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(containsCapability); - return new CSharpProjectDesignerPageProvider(capabilitiesService); - } + bool containsCapability(string c) => capabilities.Contains(c); + var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(containsCapability); + return new CSharpProjectDesignerPageProvider(capabilitiesService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProviderTests.cs index ac1f3c2684..6449357e44 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/FSharp/FSharpProjectDesignerPageProviderTests.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.FSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.FSharp; + +public class FSharpProjectDesignerPageProviderTests { - public class FSharpProjectDesignerPageProviderTests + [Fact] + public void Constructor_DoesNotThrow() + { + CreateInstance(); + } + + [Fact] + public async Task GetPagesAsync_WhenAllCapabilitiesPresent_ReturnsPagesInOrder() + { + var provider = CreateInstance(ProjectCapability.LaunchProfiles, ProjectCapability.Pack); + var result = await provider.GetPagesAsync(); + + var expected = ImmutableArray.Create( + FSharpProjectDesignerPage.Application, + FSharpProjectDesignerPage.Build, + FSharpProjectDesignerPage.BuildEvents, + FSharpProjectDesignerPage.Debug, + FSharpProjectDesignerPage.Package, + FSharpProjectDesignerPage.ReferencePaths, + FSharpProjectDesignerPage.Signing + ); + + Assert.Equal(expected, result); + } + + [Fact] + public async Task GetPagesAsync_WhenNoLaunchProfilesCapability_DoesNotContainDebugPage() + { + var provider = CreateInstance(); + var result = await provider.GetPagesAsync(); + + Assert.DoesNotContain(FSharpProjectDesignerPage.Debug, result); + } + + [Fact] + public async Task GetPagesAsync_WhenNoPackCapability_DoesNotContainPackagePage() + { + var provider = CreateInstance(); + var result = await provider.GetPagesAsync(); + + Assert.DoesNotContain(FSharpProjectDesignerPage.Package, result); + } + + private static FSharpProjectDesignerPageProvider CreateInstance(params string[] capabilities) { - [Fact] - public void Constructor_DoesNotThrow() - { - CreateInstance(); - } - - [Fact] - public async Task GetPagesAsync_WhenAllCapabilitiesPresent_ReturnsPagesInOrder() - { - var provider = CreateInstance(ProjectCapability.LaunchProfiles, ProjectCapability.Pack); - var result = await provider.GetPagesAsync(); - - var expected = ImmutableArray.Create( - FSharpProjectDesignerPage.Application, - FSharpProjectDesignerPage.Build, - FSharpProjectDesignerPage.BuildEvents, - FSharpProjectDesignerPage.Debug, - FSharpProjectDesignerPage.Package, - FSharpProjectDesignerPage.ReferencePaths, - FSharpProjectDesignerPage.Signing - ); - - Assert.Equal(expected, result); - } - - [Fact] - public async Task GetPagesAsync_WhenNoLaunchProfilesCapability_DoesNotContainDebugPage() - { - var provider = CreateInstance(); - var result = await provider.GetPagesAsync(); - - Assert.DoesNotContain(FSharpProjectDesignerPage.Debug, result); - } - - [Fact] - public async Task GetPagesAsync_WhenNoPackCapability_DoesNotContainPackagePage() - { - var provider = CreateInstance(); - var result = await provider.GetPagesAsync(); - - Assert.DoesNotContain(FSharpProjectDesignerPage.Package, result); - } - - private static FSharpProjectDesignerPageProvider CreateInstance(params string[] capabilities) - { - bool containsCapability(string c) => capabilities.Contains(c); - var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(containsCapability); - return new FSharpProjectDesignerPageProvider(capabilitiesService); - } + bool containsCapability(string c) => capabilities.Contains(c); + var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(containsCapability); + return new FSharpProjectDesignerPageProvider(capabilitiesService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/LaunchTargetPropertyPageEnumProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/LaunchTargetPropertyPageEnumProviderTests.cs index 98d1f91265..b90537637f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/LaunchTargetPropertyPageEnumProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/LaunchTargetPropertyPageEnumProviderTests.cs @@ -2,82 +2,81 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class LaunchTargetPropertyPageEnumProviderTests { - public class LaunchTargetPropertyPageEnumProviderTests + [Fact] + public async Task GetProviderAsync_ReturnsNonNullGenerator() { - [Fact] - public async Task GetProviderAsync_ReturnsNonNullGenerator() - { - var project = ConfiguredProjectFactory.Create(); + var project = ConfiguredProjectFactory.Create(); - var provider = new LaunchTargetPropertyPageEnumProvider(project); - var generator = await provider.GetProviderAsync(options: null); + var provider = new LaunchTargetPropertyPageEnumProvider(project); + var generator = await provider.GetProviderAsync(options: null); - Assert.NotNull(generator); - } + Assert.NotNull(generator); + } - [Fact] - public async Task TryCreateEnumValueAsync_ReturnsNull() - { - var project = ConfiguredProjectFactory.Create(); + [Fact] + public async Task TryCreateEnumValueAsync_ReturnsNull() + { + var project = ConfiguredProjectFactory.Create(); - var provider = new LaunchTargetPropertyPageEnumProvider(project); - var generator = await provider.GetProviderAsync(options: null); + var provider = new LaunchTargetPropertyPageEnumProvider(project); + var generator = await provider.GetProviderAsync(options: null); - Assert.Null(await generator.TryCreateEnumValueAsync("MyTarget")); - } + Assert.Null(await generator.TryCreateEnumValueAsync("MyTarget")); + } - [Fact] - public async Task GetListValuesAsync_ReturnsPageNamesAndDisplayNames() - { - var catalogProvider = GetCatalogProviderAndData(); + [Fact] + public async Task GetListValuesAsync_ReturnsPageNamesAndDisplayNames() + { + var catalogProvider = GetCatalogProviderAndData(); - var provider = new LaunchTargetPropertyPageEnumProvider( - ConfiguredProjectFactory.Create( - services: ConfiguredProjectServicesFactory.Create( - propertyPagesCatalogProvider: catalogProvider))); - var generator = await provider.GetProviderAsync(options: null); + var provider = new LaunchTargetPropertyPageEnumProvider( + ConfiguredProjectFactory.Create( + services: ConfiguredProjectServicesFactory.Create( + propertyPagesCatalogProvider: catalogProvider))); + var generator = await provider.GetProviderAsync(options: null); - var values = await generator.GetListedValuesAsync(); + var values = await generator.GetListedValuesAsync(); - Assert.Collection(values, new Action[] + Assert.Collection(values, new Action[] + { + ev => { Assert.Equal(expected: "BetaCommandPageId", actual: ev.Name); Assert.Equal(expected: "Beta", actual: ev.DisplayName); }, + ev => { Assert.Equal(expected: "GammaCommandPageId", actual: ev.Name); Assert.Equal(expected: "Gamma", actual: ev.DisplayName); } + }); + } + + private static IPropertyPagesCatalogProvider GetCatalogProviderAndData() + { + var betaPage = IRuleFactory.Create( + name: "BetaCommandPageId", + displayName: "Beta", + pageTemplate: "CommandNameBasedDebugger", + metadata: new Dictionary { - ev => { Assert.Equal(expected: "BetaCommandPageId", actual: ev.Name); Assert.Equal(expected: "Beta", actual: ev.DisplayName); }, - ev => { Assert.Equal(expected: "GammaCommandPageId", actual: ev.Name); Assert.Equal(expected: "Gamma", actual: ev.DisplayName); } + { "CommandName", "BetaCommand" } + }); + var gammaPage = IRuleFactory.Create( + name: "GammaCommandPageId", + displayName: "Gamma", + pageTemplate: "CommandNameBasedDebugger", + metadata: new Dictionary + { + { "CommandName", "GammaCommand" } }); - } - private static IPropertyPagesCatalogProvider GetCatalogProviderAndData() - { - var betaPage = IRuleFactory.Create( - name: "BetaCommandPageId", - displayName: "Beta", - pageTemplate: "CommandNameBasedDebugger", - metadata: new Dictionary - { - { "CommandName", "BetaCommand" } - }); - var gammaPage = IRuleFactory.Create( - name: "GammaCommandPageId", - displayName: "Gamma", - pageTemplate: "CommandNameBasedDebugger", - metadata: new Dictionary - { - { "CommandName", "GammaCommand" } - }); - - var catalog = IPropertyPagesCatalogFactory.Create( - new Dictionary - { - { "BetaPage", betaPage }, - { "GammaPage", gammaPage } - }); - - var catalogProvider = IPropertyPagesCatalogProviderFactory.Create( - new Dictionary { { "Project", catalog } }); - - return catalogProvider; - } + var catalog = IPropertyPagesCatalogFactory.Create( + new Dictionary + { + { "BetaPage", betaPage }, + { "GammaPage", gammaPage } + }); + + var catalogProvider = IPropertyPagesCatalogProviderFactory.Create( + new Dictionary { { "Project", catalog } }); + + return catalogProvider; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeExValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeExValueProviderTests.cs index a7619b7f2c..cbbdff422e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeExValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeExValueProviderTests.cs @@ -1,59 +1,58 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class OutputTypeExValueProviderTests { - public class OutputTypeExValueProviderTests + [Theory] + [InlineData("WinExe", "0")] + [InlineData("Exe", "1")] + [InlineData("exe", "1")] + [InlineData("Library", "2")] + [InlineData("WinMDObj", "3")] + [InlineData("AppContainerExe", "4")] + [InlineData("", "0")] + public async Task GetEvaluatedValue(object outputTypePropertyValue, string expectedMappedValue) { - [Theory] - [InlineData("WinExe", "0")] - [InlineData("Exe", "1")] - [InlineData("exe", "1")] - [InlineData("Library", "2")] - [InlineData("WinMDObj", "3")] - [InlineData("AppContainerExe", "4")] - [InlineData("", "0")] - public async Task GetEvaluatedValue(object outputTypePropertyValue, string expectedMappedValue) - { - var properties = ProjectPropertiesFactory.Create( - UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, outputTypePropertyValue)); - var provider = new OutputTypeExValueProvider(properties); + var properties = ProjectPropertiesFactory.Create( + UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, outputTypePropertyValue)); + var provider = new OutputTypeExValueProvider(properties); - var actualPropertyValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, null!); - Assert.Equal(expectedMappedValue, actualPropertyValue); - } + var actualPropertyValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, null!); + Assert.Equal(expectedMappedValue, actualPropertyValue); + } - [Theory] - [InlineData("0", "WinExe")] - [InlineData("1", "Exe")] - [InlineData("2", "Library")] - [InlineData("3", "WinMDObj")] - [InlineData("4", "AppContainerExe")] - public async Task SetValue(string incomingValue, string expectedOutputTypeValue) - { - var setValues = new List(); - var properties = ProjectPropertiesFactory.Create( - UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); - var provider = new OutputTypeExValueProvider(properties); - await provider.OnSetPropertyValueAsync(string.Empty, incomingValue, null!); + [Theory] + [InlineData("0", "WinExe")] + [InlineData("1", "Exe")] + [InlineData("2", "Library")] + [InlineData("3", "WinMDObj")] + [InlineData("4", "AppContainerExe")] + public async Task SetValue(string incomingValue, string expectedOutputTypeValue) + { + var setValues = new List(); + var properties = ProjectPropertiesFactory.Create( + UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); + var provider = new OutputTypeExValueProvider(properties); + await provider.OnSetPropertyValueAsync(string.Empty, incomingValue, null!); - Assert.Equal(setValues.Single(), expectedOutputTypeValue); - } + Assert.Equal(setValues.Single(), expectedOutputTypeValue); + } - [Fact] - public async Task SetValue_ThrowsKeyNotFoundException() - { - var setValues = new List(); - var properties = ProjectPropertiesFactory.Create( - UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); - var provider = new OutputTypeExValueProvider(properties); + [Fact] + public async Task SetValue_ThrowsKeyNotFoundException() + { + var setValues = new List(); + var properties = ProjectPropertiesFactory.Create( + UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); + var provider = new OutputTypeExValueProvider(properties); - await Assert.ThrowsAsync(async () => - { - await provider.OnSetPropertyValueAsync(string.Empty, "InvalidValue", null!); - }); - } + await Assert.ThrowsAsync(async () => + { + await provider.OnSetPropertyValueAsync(string.Empty, "InvalidValue", null!); + }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeValueProviderTests.cs index 3d558717d1..e39cb1be70 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/OutputTypeValueProviderTests.cs @@ -1,59 +1,58 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class OutputTypeValueProviderTests { - public class OutputTypeValueProviderTests + [Theory] + [InlineData("WinExe", "0")] + [InlineData("Exe", "1")] + [InlineData("exe", "1")] + [InlineData("Library", "2")] + [InlineData("WinMDObj", "2")] + [InlineData("AppContainerExe", "1")] + [InlineData("", "0")] + public async Task GetEvaluatedValue(object outputTypePropertyValue, string expectedMappedValue) { - [Theory] - [InlineData("WinExe", "0")] - [InlineData("Exe", "1")] - [InlineData("exe", "1")] - [InlineData("Library", "2")] - [InlineData("WinMDObj", "2")] - [InlineData("AppContainerExe", "1")] - [InlineData("", "0")] - public async Task GetEvaluatedValue(object outputTypePropertyValue, string expectedMappedValue) - { - var properties = ProjectPropertiesFactory.Create( - UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, outputTypePropertyValue)); - var provider = new OutputTypeValueProvider(properties); + var properties = ProjectPropertiesFactory.Create( + UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, outputTypePropertyValue)); + var provider = new OutputTypeValueProvider(properties); - var actualPropertyValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, null!); - Assert.Equal(expectedMappedValue, actualPropertyValue); - } + var actualPropertyValue = await provider.OnGetEvaluatedPropertyValueAsync(string.Empty, string.Empty, null!); + Assert.Equal(expectedMappedValue, actualPropertyValue); + } - [Theory] - [InlineData("0", "WinExe")] - [InlineData("1", "Exe")] - [InlineData("2", "Library")] - public async Task SetValue(string incomingValue, string expectedOutputTypeValue) - { - var setValues = new List(); - var properties = ProjectPropertiesFactory.Create( - UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); - var provider = new OutputTypeValueProvider(properties); + [Theory] + [InlineData("0", "WinExe")] + [InlineData("1", "Exe")] + [InlineData("2", "Library")] + public async Task SetValue(string incomingValue, string expectedOutputTypeValue) + { + var setValues = new List(); + var properties = ProjectPropertiesFactory.Create( + UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); + var provider = new OutputTypeValueProvider(properties); - await provider.OnSetPropertyValueAsync(string.Empty, incomingValue, null!); - Assert.Equal(setValues.Single(), expectedOutputTypeValue); - } + await provider.OnSetPropertyValueAsync(string.Empty, incomingValue, null!); + Assert.Equal(setValues.Single(), expectedOutputTypeValue); + } - [Theory] - [InlineData("3")] - [InlineData("InvalidValue")] - public async Task SetValue_ThrowsKeyNotFoundException(string invalidValue) - { - var setValues = new List(); - var properties = ProjectPropertiesFactory.Create( - UnconfiguredProjectFactory.Create(), - new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); - var provider = new OutputTypeValueProvider(properties); + [Theory] + [InlineData("3")] + [InlineData("InvalidValue")] + public async Task SetValue_ThrowsKeyNotFoundException(string invalidValue) + { + var setValues = new List(); + var properties = ProjectPropertiesFactory.Create( + UnconfiguredProjectFactory.Create(), + new PropertyPageData(ConfigurationGeneral.SchemaName, ConfigurationGeneral.OutputTypeProperty, "InitialValue", setValues)); + var provider = new OutputTypeValueProvider(properties); - await Assert.ThrowsAsync(async () => - { - await provider.OnSetPropertyValueAsync(string.Empty, invalidValue, null!); - }); - } + await Assert.ThrowsAsync(async () => + { + await provider.OnSetPropertyValueAsync(string.Empty, invalidValue, null!); + }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PostBuildEventValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PostBuildEventValueProviderTests.cs index b404538217..a40016666d 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PostBuildEventValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PostBuildEventValueProviderTests.cs @@ -3,802 +3,801 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public static class PostBuildEventValueProviderTests { - public static class PostBuildEventValueProviderTests + private static readonly PostBuildEventValueProvider.PostBuildEventHelper systemUnderTest = + new(); + + [Fact] + public static void GetPropertyAsync_AllTargetsPresent() + { + var root = + """ + + + + Exe + netcoreapp1.1 + + + + + + + + + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(@"echo ""post build output""", actual); + } + + [Fact] + public static void GetPropertyAsync_PostBuildTargetPresent() + { + var root = + """ + + + + Exe + netcoreapp1.1 + + + + + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(@"echo ""post build output""", actual); + } + + [Fact] + public static void GetPropertyAsync_PostBuildTargetPresent_LowerCase() + { + var root = + """ + + + + Exe + netcoreapp1.1 + + + + + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(@"echo ""post build output""", actual); + } + + [Fact] + public static void GetPropertyAsync_NoTargetsPresent() + { + var root = + """ + + + + Exe + netcoreapp1.1 + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Null(actual); + } + + [Fact] + public static async Task GetPropertyAsync_ExistingProperties() + { + var expected = "echo $(ProjectDir)"; + var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("PostBuildEvent", expected); + var (success, actual) = await systemUnderTest.TryGetUnevaluatedPropertyValueAsync(projectProperties); + Assert.True(success); + Assert.Equal(expected, actual); + } + + [Fact] + public static void GetPropertyAsync_WrongTargetName() + { + var root = + """ + + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + var result = systemUnderTest.TryGetValueFromTarget(root); + Assert.Null(result); + } + + [Fact] + public static void GetPropertyAsync_WrongExec() + { + var root = + """ + + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + var result = systemUnderTest.TryGetValueFromTarget(root); + Assert.Null(result); + } + + [Fact] + public static void SetPropertyAsync_NoTargetsPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build output""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_LowerCase() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_NoTasks() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_NoTasks_Removal() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_MultipleTasks() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_DoNotRemoveTarget_EmptyString() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(string.Empty, root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_RemoveTarget_EmptyString() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(string.Empty, root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_RemoveTarget_WhitespaceCharacter() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(" ", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_RemoveTarget_TabCharacter() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty("\t\t\t", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_DoNotRemoveTarget_NewlineCharacter() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty("\r\n", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetNameCollision() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetNameCollision02() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetNameCollision_LowerCase() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static async Task SetPropertyAsync_ExistingProperties() + { + var postbuildEventProjectProperties = + IProjectPropertiesFactory.MockWithPropertyAndValue("PostBuildEvent", "echo $(ProjectDir)").Object; + var success = await systemUnderTest.TrySetPropertyAsync(@"echo ""post build $(OutDir)""", postbuildEventProjectProperties); + Assert.True(success); + + var expected = @"echo ""post build $(OutDir)"""; + var actual = await postbuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PostBuildEvent"); + Assert.Equal(expected, actual); + } + + [Fact] + public static async Task SetPropertyAsync_RemoveExistingProperties() + { + var postbuildEventProjectProperties = + IProjectPropertiesFactory.CreateWithPropertyAndValue("PostBuildEvent", "echo $(ProjectDir)"); + var success = await systemUnderTest.TrySetPropertyAsync(" ", postbuildEventProjectProperties); + Assert.True(success); + + var result = await postbuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PostBuildEvent"); + Assert.Null(result); + } + + [Fact] + public static void SetPropertyAsync_WrongTargetName() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Read_CheckEscaped() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + + const string expected = "echo %DATE%"; + string? actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Read_CheckNotDoubleEscaped() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + + const string expected = "echo %25DATE%"; + string? actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Write_CheckEscaped() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + + const string expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + systemUnderTest.SetProperty("echo %DATE%", root); + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Write_CheckNotDoubleEscaped() { - private static readonly PostBuildEventValueProvider.PostBuildEventHelper systemUnderTest = - new(); - - [Fact] - public static void GetPropertyAsync_AllTargetsPresent() - { - var root = - """ - - - - Exe - netcoreapp1.1 - - - - - - - - - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(@"echo ""post build output""", actual); - } - - [Fact] - public static void GetPropertyAsync_PostBuildTargetPresent() - { - var root = - """ - - - - Exe - netcoreapp1.1 - - - - - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(@"echo ""post build output""", actual); - } - - [Fact] - public static void GetPropertyAsync_PostBuildTargetPresent_LowerCase() - { - var root = - """ - - - - Exe - netcoreapp1.1 - - - - - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(@"echo ""post build output""", actual); - } - - [Fact] - public static void GetPropertyAsync_NoTargetsPresent() - { - var root = - """ - - - - Exe - netcoreapp1.1 - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Null(actual); - } - - [Fact] - public static async Task GetPropertyAsync_ExistingProperties() - { - var expected = "echo $(ProjectDir)"; - var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("PostBuildEvent", expected); - var (success, actual) = await systemUnderTest.TryGetUnevaluatedPropertyValueAsync(projectProperties); - Assert.True(success); - Assert.Equal(expected, actual); - } - - [Fact] - public static void GetPropertyAsync_WrongTargetName() - { - var root = - """ - - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - var result = systemUnderTest.TryGetValueFromTarget(root); - Assert.Null(result); - } - - [Fact] - public static void GetPropertyAsync_WrongExec() - { - var root = - """ - - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - var result = systemUnderTest.TryGetValueFromTarget(root); - Assert.Null(result); - } - - [Fact] - public static void SetPropertyAsync_NoTargetsPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build output""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_LowerCase() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_NoTasks() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_NoTasks_Removal() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_MultipleTasks() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_DoNotRemoveTarget_EmptyString() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(string.Empty, root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_RemoveTarget_EmptyString() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(string.Empty, root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_RemoveTarget_WhitespaceCharacter() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(" ", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_RemoveTarget_TabCharacter() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty("\t\t\t", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_DoNotRemoveTarget_NewlineCharacter() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty("\r\n", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetNameCollision() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetNameCollision02() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetNameCollision_LowerCase() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static async Task SetPropertyAsync_ExistingProperties() - { - var postbuildEventProjectProperties = - IProjectPropertiesFactory.MockWithPropertyAndValue("PostBuildEvent", "echo $(ProjectDir)").Object; - var success = await systemUnderTest.TrySetPropertyAsync(@"echo ""post build $(OutDir)""", postbuildEventProjectProperties); - Assert.True(success); - - var expected = @"echo ""post build $(OutDir)"""; - var actual = await postbuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PostBuildEvent"); - Assert.Equal(expected, actual); - } - - [Fact] - public static async Task SetPropertyAsync_RemoveExistingProperties() - { - var postbuildEventProjectProperties = - IProjectPropertiesFactory.CreateWithPropertyAndValue("PostBuildEvent", "echo $(ProjectDir)"); - var success = await systemUnderTest.TrySetPropertyAsync(" ", postbuildEventProjectProperties); - Assert.True(success); - - var result = await postbuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PostBuildEvent"); - Assert.Null(result); - } - - [Fact] - public static void SetPropertyAsync_WrongTargetName() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Read_CheckEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - - const string expected = "echo %DATE%"; - string? actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Read_CheckNotDoubleEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - - const string expected = "echo %25DATE%"; - string? actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Write_CheckEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - - const string expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - systemUnderTest.SetProperty("echo %DATE%", root); - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Write_CheckNotDoubleEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - - const string expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - systemUnderTest.SetProperty("echo %25DATE%", root); - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + + const string expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + systemUnderTest.SetProperty("echo %25DATE%", root); + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PreBuildEventValueProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PreBuildEventValueProviderTests.cs index fbf6543d94..09a7defd27 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PreBuildEventValueProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/PreBuildEventValueProviderTests.cs @@ -3,790 +3,789 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Properties.InterceptedProjectProperties; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public static class PreBuildEventValueProviderTests { - public static class PreBuildEventValueProviderTests + private static readonly PreBuildEventValueProvider.PreBuildEventHelper systemUnderTest = + new(); + + [Fact] + public static void GetPropertyAsync_AllTargetsPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(@"echo ""prebuild output""", actual); + } + + [Fact] + public static void GetPropertyAsync_PreBuildTargetPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(@"echo ""prebuild output""", actual); + } + + [Fact] + public static void GetPropertyAsync_PreBuildTargetPresent_LowerCase() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(@"echo ""prebuild output""", actual); + } + + [Fact] + public static void GetPropertyAsync_NoTargetsPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + var actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Null(actual); + } + + [Fact] + public static async Task GetPropertyAsync_ExistingPropertiesAsync() + { + var expected = "echo $(ProjectDir)"; + var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("PreBuildEvent", expected); + var (success, actual) = await systemUnderTest.TryGetUnevaluatedPropertyValueAsync(projectProperties); + Assert.True(success); + Assert.Equal(expected, actual); + } + + [Fact] + public static void GetPropertyAsync_WrongTargetName() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + var result = systemUnderTest.TryGetValueFromTarget(root); + Assert.Null(result); + } + + [Fact] + public static void GetPropertyAsync_WrongExec() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + var result = systemUnderTest.TryGetValueFromTarget(root); + Assert.Null(result); + } + + [Fact] + public static void SetPropertyAsync_NoTargetsPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build output""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_LowerCase() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_NoTasks() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_NoTasks_Removal() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetPresent_MultipleTasks() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_DoNotRemoveTarget_EmptyString() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(string.Empty, root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_RemoveTarget_EmptyString() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(string.Empty, root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_RemoveTarget_WhitespaceCharacter() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(" ", root); + var stringWriter = new StringWriter(); + root.Save(stringWriter); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_RemoveTarget_TabCharacter() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty("\t\t\t", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_DoNotRemoveTarget_NewlineCharacter() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty("\r\n", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetNameCollision() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetNameCollision02() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void SetPropertyAsync_TargetNameCollision_LowerCase() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static async Task SetPropertyAsync_ExistingProperties() + { + var prebuildEventProjectProperties = + IProjectPropertiesFactory.MockWithPropertyAndValue("PreBuildEvent", "echo $(ProjectDir)").Object; + var success = await systemUnderTest.TrySetPropertyAsync(@"echo ""post build $(OutDir)""", prebuildEventProjectProperties); + Assert.True(success); + + var expected = @"echo ""post build $(OutDir)"""; + var actual = await prebuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PreBuildEvent"); + Assert.Equal(expected, actual); + } + + [Fact] + public static async Task SetPropertyAsync_RemoveExistingProperties() + { + var prebuildEventProjectProperties = + IProjectPropertiesFactory.CreateWithPropertyAndValue("PreBuildEvent", "echo $(ProjectDir)"); + var success = await systemUnderTest.TrySetPropertyAsync(" ", prebuildEventProjectProperties); + Assert.True(success); + + var result = await prebuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PreBuildEvent"); + Assert.Null(result); + } + + [Fact] + public static void SetPropertyAsync_WrongTargetName() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); + + var expected = + """ + + + Exe + netcoreapp1.1 + + + + + + + + + """; + + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Read_CheckEscaped() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + + const string expected = "echo %DATE%"; + string? actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Read_CheckNotDoubleEscaped() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + + + + """.AsProjectRootElement(); + + const string expected = "echo %25DATE%"; + string? actual = systemUnderTest.TryGetValueFromTarget(root); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Write_CheckEscaped() + { + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + + const string expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + systemUnderTest.SetProperty("echo %DATE%", root); + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); + } + + [Fact] + public static void EscapeValue_Write_CheckNotDoubleEscaped() { - private static readonly PreBuildEventValueProvider.PreBuildEventHelper systemUnderTest = - new(); - - [Fact] - public static void GetPropertyAsync_AllTargetsPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(@"echo ""prebuild output""", actual); - } - - [Fact] - public static void GetPropertyAsync_PreBuildTargetPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(@"echo ""prebuild output""", actual); - } - - [Fact] - public static void GetPropertyAsync_PreBuildTargetPresent_LowerCase() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(@"echo ""prebuild output""", actual); - } - - [Fact] - public static void GetPropertyAsync_NoTargetsPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - var actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Null(actual); - } - - [Fact] - public static async Task GetPropertyAsync_ExistingPropertiesAsync() - { - var expected = "echo $(ProjectDir)"; - var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("PreBuildEvent", expected); - var (success, actual) = await systemUnderTest.TryGetUnevaluatedPropertyValueAsync(projectProperties); - Assert.True(success); - Assert.Equal(expected, actual); - } - - [Fact] - public static void GetPropertyAsync_WrongTargetName() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - var result = systemUnderTest.TryGetValueFromTarget(root); - Assert.Null(result); - } - - [Fact] - public static void GetPropertyAsync_WrongExec() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - var result = systemUnderTest.TryGetValueFromTarget(root); - Assert.Null(result); - } - - [Fact] - public static void SetPropertyAsync_NoTargetsPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build output""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_LowerCase() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_NoTasks() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_NoTasks_Removal() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetPresent_MultipleTasks() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_DoNotRemoveTarget_EmptyString() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(string.Empty, root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_RemoveTarget_EmptyString() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(string.Empty, root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_RemoveTarget_WhitespaceCharacter() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(" ", root); - var stringWriter = new StringWriter(); - root.Save(stringWriter); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_RemoveTarget_TabCharacter() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty("\t\t\t", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_DoNotRemoveTarget_NewlineCharacter() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty("\r\n", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetNameCollision() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetNameCollision02() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void SetPropertyAsync_TargetNameCollision_LowerCase() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""pre build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static async Task SetPropertyAsync_ExistingProperties() - { - var prebuildEventProjectProperties = - IProjectPropertiesFactory.MockWithPropertyAndValue("PreBuildEvent", "echo $(ProjectDir)").Object; - var success = await systemUnderTest.TrySetPropertyAsync(@"echo ""post build $(OutDir)""", prebuildEventProjectProperties); - Assert.True(success); - - var expected = @"echo ""post build $(OutDir)"""; - var actual = await prebuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PreBuildEvent"); - Assert.Equal(expected, actual); - } - - [Fact] - public static async Task SetPropertyAsync_RemoveExistingProperties() - { - var prebuildEventProjectProperties = - IProjectPropertiesFactory.CreateWithPropertyAndValue("PreBuildEvent", "echo $(ProjectDir)"); - var success = await systemUnderTest.TrySetPropertyAsync(" ", prebuildEventProjectProperties); - Assert.True(success); - - var result = await prebuildEventProjectProperties.GetUnevaluatedPropertyValueAsync("PreBuildEvent"); - Assert.Null(result); - } - - [Fact] - public static void SetPropertyAsync_WrongTargetName() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - systemUnderTest.SetProperty(@"echo ""post build $(OutDir)""", root); - - var expected = - """ - - - Exe - netcoreapp1.1 - - - - - - - - - """; - - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Read_CheckEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - - const string expected = "echo %DATE%"; - string? actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Read_CheckNotDoubleEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - - - - """.AsProjectRootElement(); - - const string expected = "echo %25DATE%"; - string? actual = systemUnderTest.TryGetValueFromTarget(root); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Write_CheckEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - - const string expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - systemUnderTest.SetProperty("echo %DATE%", root); - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } - - [Fact] - public static void EscapeValue_Write_CheckNotDoubleEscaped() - { - var root = - """ - - - Exe - netcoreapp1.1 - - - """.AsProjectRootElement(); - - const string expected = - """ - - - Exe - netcoreapp1.1 - - - - - - """; - - systemUnderTest.SetProperty("echo %25DATE%", root); - var actual = root.SaveAndGetChanges(); - Assert.Equal(expected, actual); - } + var root = + """ + + + Exe + netcoreapp1.1 + + + """.AsProjectRootElement(); + + const string expected = + """ + + + Exe + netcoreapp1.1 + + + + + + """; + + systemUnderTest.SetProperty("echo %25DATE%", root); + var actual = root.SaveAndGetChanges(); + Assert.Equal(expected, actual); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerPageMetadataTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerPageMetadataTests.cs index 899433ff46..01d0783347 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerPageMetadataTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerPageMetadataTests.cs @@ -1,63 +1,62 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class ProjectDesignerPageMetadataTests { - public class ProjectDesignerPageMetadataTests + [Fact] + public void Constructor_GuidEmptyAsPageGuid_ThrowsArgumentException() { - [Fact] - public void Constructor_GuidEmptyAsPageGuid_ThrowsArgumentException() - { - Assert.Throws("pageGuid", () => - { - new ProjectDesignerPageMetadata(Guid.Empty, 0, hasConfigurationCondition: false); - }); - } - - [Fact] - public void Constructor_ValueAsPageGuid_SetsPageGuidProperty() - { - var guid = Guid.NewGuid(); - var metadata = new ProjectDesignerPageMetadata(guid, 0, hasConfigurationCondition: false); - - Assert.Equal(guid, metadata.PageGuid); - } - - [Theory] - [InlineData(int.MinValue)] - [InlineData(-10)] - [InlineData(-1)] - [InlineData(0)] - [InlineData(1)] - [InlineData(10)] - [InlineData(int.MaxValue)] - public void Constructor_ValueAsPageOrder_SetsPageOrderProperty(int pageOrder) + Assert.Throws("pageGuid", () => { - var metadata = new ProjectDesignerPageMetadata(Guid.NewGuid(), pageOrder, hasConfigurationCondition: false); + new ProjectDesignerPageMetadata(Guid.Empty, 0, hasConfigurationCondition: false); + }); + } - Assert.Equal(pageOrder, metadata.PageOrder); - } + [Fact] + public void Constructor_ValueAsPageGuid_SetsPageGuidProperty() + { + var guid = Guid.NewGuid(); + var metadata = new ProjectDesignerPageMetadata(guid, 0, hasConfigurationCondition: false); - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Constructor_ValueAsHasConfigurationCondition_SetsHasConfigurationConditionProperty(bool hasConfigurationCondition) - { - var metadata = new ProjectDesignerPageMetadata(Guid.NewGuid(), 0, hasConfigurationCondition: hasConfigurationCondition); + Assert.Equal(guid, metadata.PageGuid); + } - Assert.Equal(hasConfigurationCondition, metadata.HasConfigurationCondition); - } + [Theory] + [InlineData(int.MinValue)] + [InlineData(-10)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(int.MaxValue)] + public void Constructor_ValueAsPageOrder_SetsPageOrderProperty(int pageOrder) + { + var metadata = new ProjectDesignerPageMetadata(Guid.NewGuid(), pageOrder, hasConfigurationCondition: false); - [Fact] - public void Name_ReturnsNull() - { - var metadata = CreateInstance(); + Assert.Equal(pageOrder, metadata.PageOrder); + } - Assert.Null(metadata.Name); - } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Constructor_ValueAsHasConfigurationCondition_SetsHasConfigurationConditionProperty(bool hasConfigurationCondition) + { + var metadata = new ProjectDesignerPageMetadata(Guid.NewGuid(), 0, hasConfigurationCondition: hasConfigurationCondition); - private static ProjectDesignerPageMetadata CreateInstance() - { - return new ProjectDesignerPageMetadata(Guid.NewGuid(), 0, false); - } + Assert.Equal(hasConfigurationCondition, metadata.HasConfigurationCondition); + } + + [Fact] + public void Name_ReturnsNull() + { + var metadata = CreateInstance(); + + Assert.Null(metadata.Name); + } + + private static ProjectDesignerPageMetadata CreateInstance() + { + return new ProjectDesignerPageMetadata(Guid.NewGuid(), 0, false); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerServiceTests.cs index b29d2e18d0..9b3b77df70 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/ProjectDesignerServiceTests.cs @@ -5,160 +5,159 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +public class ProjectDesignerServiceTests { - public class ProjectDesignerServiceTests + [Theory] + [InlineData(true)] + [InlineData(false)] + public void SupportsProjectDesigner_ReturnsResultIsProjectDesignerSupported(bool supportsProjectDesigner) { - [Theory] - [InlineData(true)] - [InlineData(false)] - public void SupportsProjectDesigner_ReturnsResultIsProjectDesignerSupported(bool supportsProjectDesigner) - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => supportsProjectDesigner); + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => supportsProjectDesigner); - var designerService = CreateInstance(vsProjectDesignerPageService); + var designerService = CreateInstance(vsProjectDesignerPageService); - var result = designerService.SupportsProjectDesigner; + var result = designerService.SupportsProjectDesigner; - Assert.Equal(supportsProjectDesigner, result); - } + Assert.Equal(supportsProjectDesigner, result); + } - [Fact] - public void ShowProjectDesignerAsync_WhenSupportsProjectDesignerFalse_ThrowsInvalidOperation() - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => false); + [Fact] + public void ShowProjectDesignerAsync_WhenSupportsProjectDesignerFalse_ThrowsInvalidOperation() + { + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => false); - var designerService = CreateInstance(vsProjectDesignerPageService); + var designerService = CreateInstance(vsProjectDesignerPageService); - Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); - } + Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); + } - [Fact] - public async Task ShowProjectDesignerAsync_WhenGetGuidPropertyForProjectDesignerEditorReturnsHResult_Throws() - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); + [Fact] + public async Task ShowProjectDesignerAsync_WhenGetGuidPropertyForProjectDesignerEditorReturnsHResult_Throws() + { + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); - var hierarchy = IVsHierarchyFactory.Create(); - hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, VSConstants.E_FAIL); + var hierarchy = IVsHierarchyFactory.Create(); + hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, VSConstants.E_FAIL); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy); + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy); - var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); + var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); - await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); - } + await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); + } - [Fact] - public async Task ShowProjectDesignerAsync_WhenProjectDesignerEditorReturnsHResult_Throws() - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); + [Fact] + public async Task ShowProjectDesignerAsync_WhenProjectDesignerEditorReturnsHResult_Throws() + { + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); - var hierarchy = IVsHierarchyFactory.Create(); - hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, VSConstants.E_FAIL); + var hierarchy = IVsHierarchyFactory.Create(); + hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, VSConstants.E_FAIL); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy); + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy); - var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); + var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); - await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); - } + await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); + } - [Fact] - public async Task ShowProjectDesignerAsync_WhenOpenItemWithSpecificEditorReturnsHResult_Throws() - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); + [Fact] + public async Task ShowProjectDesignerAsync_WhenOpenItemWithSpecificEditorReturnsHResult_Throws() + { + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); - var editorGuid = Guid.NewGuid(); + var editorGuid = Guid.NewGuid(); - var hierarchy = IVsHierarchyFactory.Create(); - hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); + var hierarchy = IVsHierarchyFactory.Create(); + hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); - var project = (IVsProject4)hierarchy; - project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, VSConstants.E_FAIL); + var project = (IVsProject4)hierarchy; + project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, VSConstants.E_FAIL); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); - var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); + var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); - await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); - } + await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); + } - [Fact] - public Task ShowProjectDesignerAsync_WhenOpenedInExternalEditor_DoesNotAttemptToShowWindow() - { // OpenItemWithSpecific returns null frame when opened in external editor - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); + [Fact] + public Task ShowProjectDesignerAsync_WhenOpenedInExternalEditor_DoesNotAttemptToShowWindow() + { // OpenItemWithSpecific returns null frame when opened in external editor + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); - var editorGuid = Guid.NewGuid(); + var editorGuid = Guid.NewGuid(); - var hierarchy = IVsHierarchyFactory.Create(); - hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); + var hierarchy = IVsHierarchyFactory.Create(); + hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); - var project = (IVsProject4)hierarchy; - project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, null); + var project = (IVsProject4)hierarchy; + project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, null); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); - var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); + var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); - return designerService.ShowProjectDesignerAsync(); - } + return designerService.ShowProjectDesignerAsync(); + } - [Fact] - public async Task ShowProjectDesignerAsync_WhenWindowShowReturnsHResult_Throws() - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); + [Fact] + public async Task ShowProjectDesignerAsync_WhenWindowShowReturnsHResult_Throws() + { + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); - var editorGuid = Guid.NewGuid(); + var editorGuid = Guid.NewGuid(); - var hierarchy = IVsHierarchyFactory.Create(); - hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); - var project = (IVsProject4)hierarchy; + var hierarchy = IVsHierarchyFactory.Create(); + hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); + var project = (IVsProject4)hierarchy; - var frame = IVsWindowFrameFactory.ImplementShow(() => VSConstants.E_FAIL); - project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, frame); + var frame = IVsWindowFrameFactory.ImplementShow(() => VSConstants.E_FAIL); + project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, frame); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); - var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); + var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); - await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); - } + await Assert.ThrowsAsync(designerService.ShowProjectDesignerAsync); + } - [Fact] - public async Task ShowProjectDesignerAsync_WhenOpenedInInternalEditor_ShowsWindow() - { - var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); + [Fact] + public async Task ShowProjectDesignerAsync_WhenOpenedInInternalEditor_ShowsWindow() + { + var vsProjectDesignerPageService = IVsProjectDesignerPageServiceFactory.ImplementIsProjectDesignerSupported(() => true); - var editorGuid = Guid.NewGuid(); + var editorGuid = Guid.NewGuid(); - var hierarchy = IVsHierarchyFactory.Create(); - hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); - var project = (IVsProject4)hierarchy; + var hierarchy = IVsHierarchyFactory.Create(); + hierarchy.ImplementGetGuid(VsHierarchyPropID.ProjectDesignerEditor, result: editorGuid); + var project = (IVsProject4)hierarchy; - int callCount = 0; - var frame = IVsWindowFrameFactory.ImplementShow(() => { callCount++; return 0; }); - project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, frame); + int callCount = 0; + var frame = IVsWindowFrameFactory.ImplementShow(() => { callCount++; return 0; }); + project.ImplementOpenItemWithSpecific(editorGuid, VSConstants.LOGVIEWID_Primary, frame); - var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); + var projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy, () => project); - var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); + var designerService = CreateInstance(projectVsServices, vsProjectDesignerPageService); - await designerService.ShowProjectDesignerAsync(); + await designerService.ShowProjectDesignerAsync(); - Assert.Equal(1, callCount); - } + Assert.Equal(1, callCount); + } - private static ProjectDesignerService CreateInstance(IVsProjectDesignerPageService vsProjectDesignerPageService) - { - return CreateInstance(null, vsProjectDesignerPageService); - } + private static ProjectDesignerService CreateInstance(IVsProjectDesignerPageService vsProjectDesignerPageService) + { + return CreateInstance(null, vsProjectDesignerPageService); + } - private static ProjectDesignerService CreateInstance(IUnconfiguredProjectVsServices? projectVsServices, IVsProjectDesignerPageService? vsProjectDesignerPageService) - { - projectVsServices ??= IUnconfiguredProjectVsServicesFactory.Create(); - vsProjectDesignerPageService ??= IVsProjectDesignerPageServiceFactory.Create(); + private static ProjectDesignerService CreateInstance(IUnconfiguredProjectVsServices? projectVsServices, IVsProjectDesignerPageService? vsProjectDesignerPageService) + { + projectVsServices ??= IUnconfiguredProjectVsServicesFactory.Create(); + vsProjectDesignerPageService ??= IVsProjectDesignerPageServiceFactory.Create(); - return new ProjectDesignerService(projectVsServices, vsProjectDesignerPageService); - } + return new ProjectDesignerService(projectVsServices, vsProjectDesignerPageService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VBDefineConstantsEncodingTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VBDefineConstantsEncodingTests.cs index 2581641bb1..4a48cdb470 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VBDefineConstantsEncodingTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VBDefineConstantsEncodingTests.cs @@ -2,134 +2,133 @@ using Microsoft.VisualStudio.ProjectSystem.Debug; -namespace Microsoft.VisualStudio.ProjectSystem.Properties.InterceptingProjectProperties.BuildPropertyPage +namespace Microsoft.VisualStudio.ProjectSystem.Properties.InterceptingProjectProperties.BuildPropertyPage; + +public class VBDefineConstantsEncodingTests { - public class VBDefineConstantsEncodingTests + [Theory] + [InlineData("key1=\"value1\"", "key1=/\"value1/\"")] + [InlineData("key1=\"value1\",key2=\"value2\"", "key1=/\"value1/\",key2=/\"value2/\"")] + [InlineData("a=\"b\",c=\"easy\",as=123", "a=/\"b/\",c=/\"easy/\",as=123")] + [InlineData("key1=\"value1\",key2=\"value2\",key3=\"value3\"", "key1=/\"value1/\",key2=/\"value2/\",key3=/\"value3/\"")] + [InlineData("something=\"equals=this\"", "something=/\"equals/=this/\"")] + [InlineData("oh=\"hey=there\",hi=\"didnt=see\",you=\"there\"", "oh=/\"hey/=there/\",hi=/\"didnt/=see/\",you=/\"there/\"")] + [InlineData("path=\"/path/to/somewhere/\"", "path=/\"//path//to//somewhere/\"")] + [InlineData("file=\"/path/is/here/\"", "file=/\"//path//is//here/\"")] + [InlineData("files=\"path=/path/to/somewhere/\"", "files=/\"path/=//path//to//somewhere/\"")] + [InlineData("a=1,a=2", "a=1,a=2")] + [InlineData("key1 =\"value1 \"", "key1 =/\"value1 /\"")] + [InlineData(" key1=\" value1\"", " key1=/\" value1/\"")] + [InlineData(" key1 =\" value1 \"", " key1 =/\" value1 /\"")] + [InlineData(" =\" \"", " =/\" /\"")] + [InlineData("key1=\"a b c d\"", "key1=/\"a b c d/\"")] + [InlineData("key1=\"a=b=c=d\"", "key1=/\"a/=b/=c/=d/\"")] + [InlineData("key1=\"a=b\"\"c=d\"", "key1=/\"a/=b/\"/\"c/=d/\"")] + [InlineData("key1=\"ab\"\"cd\"", "key1=/\"ab/\"/\"cd/\"")] + [InlineData("key1=\"\"ab\"\"cd\"\"ef\"", "key1=/\"/\"ab/\"/\"cd/\"/\"ef/\"")] + [InlineData("key1=\"\"ab\"\"cd\"", "key1=/\"/\"ab/\"/\"cd/\"")] + [InlineData("a b c d=\"value1\"", "a b c d=/\"value1/\"")] + [InlineData("a=b=c=d=\"value1\"", "a=b/=c/=d/=/\"value1/\"")] + [InlineData("a=\"b\"\"c=d=value1\"", "a=/\"b/\"/\"c/=d/=value1/\"")] + [InlineData("\"ab\"\"cd=\"value1\"", "/\"ab/\"/\"cd=/\"value1/\"")] + [InlineData("\"\"ab\"\"cd\"\"ef=\"value1\"", "/\"/\"ab/\"/\"cd/\"/\"ef=/\"value1/\"")] + [InlineData("\"\"ab\"\"cd=\"value1\"", "/\"/\"ab/\"/\"cd=/\"value1/\"")] + [InlineData("\"\"ab\"\"cd=\" value1 \"", "/\"/\"ab/\"/\"cd=/\" value1 /\"")] + [InlineData("\"\"ab\"\"cd=\"value1 \"", "/\"/\"ab/\"/\"cd=/\"value1 /\"")] + [InlineData("\"\"ab\"\"cd=\" value1\"", "/\"/\"ab/\"/\"cd=/\" value1/\"")] + [InlineData("", "")] + [InlineData("val/=ue", "val/=ue")] + [InlineData("value","value")] + [InlineData("value1,value2,value3", "value1,value2,value3")] + [InlineData("value1,value2,value3,key4=\"value4\"", "value1,value2,value3,key4=/\"value4/\"")] + [InlineData("value1,key2=\"value2\",value3,value4", "value1,key2=/\"value2/\",value3,value4")] + [InlineData("value1,key2=\"value2\",value3,key4=\"value4\"", "value1,key2=/\"value2/\",value3,key4=/\"value4/\"")] + [InlineData("key1=\"value1\",value2, value3, value4", "key1=/\"value1/\",value2, value3, value4")] + public void VerifyValidEncoding(string input, string expectedOutput) { - [Theory] - [InlineData("key1=\"value1\"", "key1=/\"value1/\"")] - [InlineData("key1=\"value1\",key2=\"value2\"", "key1=/\"value1/\",key2=/\"value2/\"")] - [InlineData("a=\"b\",c=\"easy\",as=123", "a=/\"b/\",c=/\"easy/\",as=123")] - [InlineData("key1=\"value1\",key2=\"value2\",key3=\"value3\"", "key1=/\"value1/\",key2=/\"value2/\",key3=/\"value3/\"")] - [InlineData("something=\"equals=this\"", "something=/\"equals/=this/\"")] - [InlineData("oh=\"hey=there\",hi=\"didnt=see\",you=\"there\"", "oh=/\"hey/=there/\",hi=/\"didnt/=see/\",you=/\"there/\"")] - [InlineData("path=\"/path/to/somewhere/\"", "path=/\"//path//to//somewhere/\"")] - [InlineData("file=\"/path/is/here/\"", "file=/\"//path//is//here/\"")] - [InlineData("files=\"path=/path/to/somewhere/\"", "files=/\"path/=//path//to//somewhere/\"")] - [InlineData("a=1,a=2", "a=1,a=2")] - [InlineData("key1 =\"value1 \"", "key1 =/\"value1 /\"")] - [InlineData(" key1=\" value1\"", " key1=/\" value1/\"")] - [InlineData(" key1 =\" value1 \"", " key1 =/\" value1 /\"")] - [InlineData(" =\" \"", " =/\" /\"")] - [InlineData("key1=\"a b c d\"", "key1=/\"a b c d/\"")] - [InlineData("key1=\"a=b=c=d\"", "key1=/\"a/=b/=c/=d/\"")] - [InlineData("key1=\"a=b\"\"c=d\"", "key1=/\"a/=b/\"/\"c/=d/\"")] - [InlineData("key1=\"ab\"\"cd\"", "key1=/\"ab/\"/\"cd/\"")] - [InlineData("key1=\"\"ab\"\"cd\"\"ef\"", "key1=/\"/\"ab/\"/\"cd/\"/\"ef/\"")] - [InlineData("key1=\"\"ab\"\"cd\"", "key1=/\"/\"ab/\"/\"cd/\"")] - [InlineData("a b c d=\"value1\"", "a b c d=/\"value1/\"")] - [InlineData("a=b=c=d=\"value1\"", "a=b/=c/=d/=/\"value1/\"")] - [InlineData("a=\"b\"\"c=d=value1\"", "a=/\"b/\"/\"c/=d/=value1/\"")] - [InlineData("\"ab\"\"cd=\"value1\"", "/\"ab/\"/\"cd=/\"value1/\"")] - [InlineData("\"\"ab\"\"cd\"\"ef=\"value1\"", "/\"/\"ab/\"/\"cd/\"/\"ef=/\"value1/\"")] - [InlineData("\"\"ab\"\"cd=\"value1\"", "/\"/\"ab/\"/\"cd=/\"value1/\"")] - [InlineData("\"\"ab\"\"cd=\" value1 \"", "/\"/\"ab/\"/\"cd=/\" value1 /\"")] - [InlineData("\"\"ab\"\"cd=\"value1 \"", "/\"/\"ab/\"/\"cd=/\"value1 /\"")] - [InlineData("\"\"ab\"\"cd=\" value1\"", "/\"/\"ab/\"/\"cd=/\" value1/\"")] - [InlineData("", "")] - [InlineData("val/=ue", "val/=ue")] - [InlineData("value","value")] - [InlineData("value1,value2,value3", "value1,value2,value3")] - [InlineData("value1,value2,value3,key4=\"value4\"", "value1,value2,value3,key4=/\"value4/\"")] - [InlineData("value1,key2=\"value2\",value3,value4", "value1,key2=/\"value2/\",value3,value4")] - [InlineData("value1,key2=\"value2\",value3,key4=\"value4\"", "value1,key2=/\"value2/\",value3,key4=/\"value4/\"")] - [InlineData("key1=\"value1\",value2, value3, value4", "key1=/\"value1/\",value2, value3, value4")] - public void VerifyValidEncoding(string input, string expectedOutput) - { - VBDefineConstantsEncoding _encoding = new(); - Assert.Equal(expected: expectedOutput, actual: _encoding.Format(_encoding.Parse(input))); - } + VBDefineConstantsEncoding _encoding = new(); + Assert.Equal(expected: expectedOutput, actual: _encoding.Format(_encoding.Parse(input))); + } - [Theory] - [InlineData("key1=/\"value1/\"", new[] { "key1", "\"value1\"" })] - [InlineData("key1=/\"value1/\",key2=/\"value2/\"", new[] { "key1", "\"value1\"", "key2", "\"value2\"" })] - [InlineData("a=/\"b/\",c=/\"easy/\",as=123", new[] { "a", "\"b\"", "c", "\"easy\"", "as", "123" })] - [InlineData("key1=/\"value1/\",key2=/\"value2/\",key3=/\"value3/\"", new[] { "key1", "\"value1\"", "key2", "\"value2\"", "key3", "\"value3\"" })] - [InlineData("something=/\"equals/=this/\"", new[] { "something", "\"equals=this\"" })] - [InlineData("oh=/\"hey/=there/\",hi=/\"didnt/=see/\",you=/\"there/\"", new[] { "oh", "\"hey=there\"", "hi", "\"didnt=see\"", "you", "\"there\"" })] - [InlineData("path=/\"//path//to//somewhere/\"", new[] { "path", "\"/path/to/somewhere\"" })] - [InlineData("file=/\"//path//is//here/\"", new[] { "file", "\"/path/is/here\"" })] - [InlineData("files=/\"path/=//path//to//somewhere/\"", new[] { "files", "\"path=/path/to/somewhere\"" })] - [InlineData("a=1,a=2", new[] { "a", "1", "a", "2" })] - [InlineData("key1 =/\"value1 /\"", new[] { "key1 ", "\"value1 \"" })] - [InlineData(" key1=/\" value1/\"", new[] { " key1", "\" value1\"" })] - [InlineData("key1=/\"a b c d/\"", new[] { "key1", "\"a b c d\"" })] - [InlineData("key1=/\"a/=b/=c/=d/\"", new[] { "key1", "\"a=b=c=d\"" })] - [InlineData("key1=/\"a/=b/\"/\"c/=d/\"", new[] { "key1", "\"a=b\"\"c=d\"" })] - [InlineData("key1=/\"/\"ab/\"/\"cd/\"", new[] { "key1", "\"\"ab\"\"cd\"" })] - [InlineData("key1=/\"/\"/\"ab/\"/\"cd/\"/\"ef/\"", new[] { "key1", "\"\"\"ab\"\"cd\"\"ef\"" })] - [InlineData("key1=/\"/\"/\"ab/\"/\"cd/\"", new[] { "key1", "\"\"\"ab\"\"cd\"" })] - [InlineData("a b c d=/\"value1/\"", new[] { "a b c d", "\"value1\"" })] - [InlineData("a=/\"b/=c/=d/=value1/\"", new[] { "a", "\"b=c=d=value1\"" })] - [InlineData("a=/\"b/\"/\"c/=d/=value1/\"", new[] { "a", "\"b\"\"c=d=value1\"" })] - [InlineData("/\"ab/\"/\"cd=/\"value1/\"", new[] { "\"ab\"\"cd", "\"value1\"" })] - [InlineData("/\"/\"ab/\"/\"cd/\"/\"ef=/\"value1/\"", new[] { "\"\"ab\"\"cd\"\"ef", "\"value1\"" })] - [InlineData("/\"/\"ab/\"/\"cd=/\"value1/\"", new[] { "\"\"ab\"\"cd", "\"value1\"" })] - [InlineData("a//bc=/\"value1/\"", new[] { "a/bc", "\"value1\"" })] - [InlineData("", new string[0])] + [Theory] + [InlineData("key1=/\"value1/\"", new[] { "key1", "\"value1\"" })] + [InlineData("key1=/\"value1/\",key2=/\"value2/\"", new[] { "key1", "\"value1\"", "key2", "\"value2\"" })] + [InlineData("a=/\"b/\",c=/\"easy/\",as=123", new[] { "a", "\"b\"", "c", "\"easy\"", "as", "123" })] + [InlineData("key1=/\"value1/\",key2=/\"value2/\",key3=/\"value3/\"", new[] { "key1", "\"value1\"", "key2", "\"value2\"", "key3", "\"value3\"" })] + [InlineData("something=/\"equals/=this/\"", new[] { "something", "\"equals=this\"" })] + [InlineData("oh=/\"hey/=there/\",hi=/\"didnt/=see/\",you=/\"there/\"", new[] { "oh", "\"hey=there\"", "hi", "\"didnt=see\"", "you", "\"there\"" })] + [InlineData("path=/\"//path//to//somewhere/\"", new[] { "path", "\"/path/to/somewhere\"" })] + [InlineData("file=/\"//path//is//here/\"", new[] { "file", "\"/path/is/here\"" })] + [InlineData("files=/\"path/=//path//to//somewhere/\"", new[] { "files", "\"path=/path/to/somewhere\"" })] + [InlineData("a=1,a=2", new[] { "a", "1", "a", "2" })] + [InlineData("key1 =/\"value1 /\"", new[] { "key1 ", "\"value1 \"" })] + [InlineData(" key1=/\" value1/\"", new[] { " key1", "\" value1\"" })] + [InlineData("key1=/\"a b c d/\"", new[] { "key1", "\"a b c d\"" })] + [InlineData("key1=/\"a/=b/=c/=d/\"", new[] { "key1", "\"a=b=c=d\"" })] + [InlineData("key1=/\"a/=b/\"/\"c/=d/\"", new[] { "key1", "\"a=b\"\"c=d\"" })] + [InlineData("key1=/\"/\"ab/\"/\"cd/\"", new[] { "key1", "\"\"ab\"\"cd\"" })] + [InlineData("key1=/\"/\"/\"ab/\"/\"cd/\"/\"ef/\"", new[] { "key1", "\"\"\"ab\"\"cd\"\"ef\"" })] + [InlineData("key1=/\"/\"/\"ab/\"/\"cd/\"", new[] { "key1", "\"\"\"ab\"\"cd\"" })] + [InlineData("a b c d=/\"value1/\"", new[] { "a b c d", "\"value1\"" })] + [InlineData("a=/\"b/=c/=d/=value1/\"", new[] { "a", "\"b=c=d=value1\"" })] + [InlineData("a=/\"b/\"/\"c/=d/=value1/\"", new[] { "a", "\"b\"\"c=d=value1\"" })] + [InlineData("/\"ab/\"/\"cd=/\"value1/\"", new[] { "\"ab\"\"cd", "\"value1\"" })] + [InlineData("/\"/\"ab/\"/\"cd/\"/\"ef=/\"value1/\"", new[] { "\"\"ab\"\"cd\"\"ef", "\"value1\"" })] + [InlineData("/\"/\"ab/\"/\"cd=/\"value1/\"", new[] { "\"\"ab\"\"cd", "\"value1\"" })] + [InlineData("a//bc=/\"value1/\"", new[] { "a/bc", "\"value1\"" })] + [InlineData("", new string[0])] - [InlineData("a=1,b=2", new[] { "a", "1", "b", "2" })] - [InlineData("a=1123,b=456", new[] { "a", "1123", "b", "456" })] - [InlineData("a=1,b=/\"apple/\"", new[] { "a", "1", "b", "\"apple\"" })] - [InlineData("a=1,b=/\"apple/\",c=456", new[] { "a", "1", "b", "\"apple\"", "c", "456" })] - [InlineData("a=1,b=/\"apple/\",c=456,d=True", new[] { "a", "1", "b", "\"apple\"", "c", "456", "d", "True" })] + [InlineData("a=1,b=2", new[] { "a", "1", "b", "2" })] + [InlineData("a=1123,b=456", new[] { "a", "1123", "b", "456" })] + [InlineData("a=1,b=/\"apple/\"", new[] { "a", "1", "b", "\"apple\"" })] + [InlineData("a=1,b=/\"apple/\",c=456", new[] { "a", "1", "b", "\"apple\"", "c", "456" })] + [InlineData("a=1,b=/\"apple/\",c=456,d=True", new[] { "a", "1", "b", "\"apple\"", "c", "456", "d", "True" })] - [InlineData("a=True", new[] { "a", "True" })] - [InlineData("a=False", new[] { "a", "False" })] - [InlineData("a=True,b=False", new[] { "a", "True", "b", "False" })] - [InlineData("a=True,b=False,c=123", new[] { "a", "True", "b", "False", "c", "123" })] - public void ValidateValuesParsedFormatted(string encodedPairs, string[] pairs) - { - VBDefineConstantsEncoding _encoding = new(); + [InlineData("a=True", new[] { "a", "True" })] + [InlineData("a=False", new[] { "a", "False" })] + [InlineData("a=True,b=False", new[] { "a", "True", "b", "False" })] + [InlineData("a=True,b=False,c=123", new[] { "a", "True", "b", "False", "c", "123" })] + public void ValidateValuesParsedFormatted(string encodedPairs, string[] pairs) + { + VBDefineConstantsEncoding _encoding = new(); - Assert.Equal(expected: pairs, actual: _encoding.Parse(encodedPairs).SelectMany(pair => new[] { pair.Name, pair.Value })); + Assert.Equal(expected: pairs, actual: _encoding.Parse(encodedPairs).SelectMany(pair => new[] { pair.Name, pair.Value })); - Assert.Equal(encodedPairs, _encoding.Format(ToNameValues(pairs))); + Assert.Equal(encodedPairs, _encoding.Format(ToNameValues(pairs))); - static IEnumerable<(string Name, string Value)> ToNameValues(IEnumerable pairs) + static IEnumerable<(string Name, string Value)> ToNameValues(IEnumerable pairs) + { + using var e = pairs.GetEnumerator(); + while (e.MoveNext()) { - using var e = pairs.GetEnumerator(); - while (e.MoveNext()) - { - var name = e.Current; - Assert.True(e.MoveNext()); - var value = e.Current; - yield return (name, value); - } + var name = e.Current; + Assert.True(e.MoveNext()); + var value = e.Current; + yield return (name, value); } } + } - [Theory] - [InlineData("=key1=")] - [InlineData("=key1")] - [InlineData(",key1")] - [InlineData(",,,key1")] - [InlineData("key1,")] - [InlineData("key1=value1,")] - [InlineData("key1,,,")] - [InlineData("=")] - [InlineData("==")] - [InlineData("===")] - [InlineData("key1,=abcd")] - [InlineData("key1,=,abcd")] - [InlineData("=\"\"")] - [InlineData(",")] - [InlineData(",,,")] - public void DetectInvalidInput(string input) - { - VBDefineConstantsEncoding _encoding = new(); - FormatException exception = Assert.Throws(() => (_encoding.Format(_encoding.Parse(input)))); - Assert.Equal("Expected valid name value pair for defining custom constants.", exception.Message); - } + [Theory] + [InlineData("=key1=")] + [InlineData("=key1")] + [InlineData(",key1")] + [InlineData(",,,key1")] + [InlineData("key1,")] + [InlineData("key1=value1,")] + [InlineData("key1,,,")] + [InlineData("=")] + [InlineData("==")] + [InlineData("===")] + [InlineData("key1,=abcd")] + [InlineData("key1,=,abcd")] + [InlineData("=\"\"")] + [InlineData(",")] + [InlineData(",,,")] + public void DetectInvalidInput(string input) + { + VBDefineConstantsEncoding _encoding = new(); + FormatException exception = Assert.Throws(() => (_encoding.Format(_encoding.Parse(input)))); + Assert.Equal("Expected valid name value pair for defining custom constants.", exception.Message); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProviderTests.cs index 236f93a37d..51e1a921b4 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/MapDynamicEnumValuesProviderTests.cs @@ -4,138 +4,137 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using VSLangProj; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +public class MapDynamicEnumValuesProviderTests { - public class MapDynamicEnumValuesProviderTests + [Fact] + public void MapDynamicEnumValuesProvider_AssertNull() { - [Fact] - public void MapDynamicEnumValuesProvider_AssertNull() + Assert.Throws("valueMap", () => { - Assert.Throws("valueMap", () => - { - new MapDynamicEnumValuesProvider(null!); - }); - } + new MapDynamicEnumValuesProvider(null!); + }); + } + + [Fact] + public async Task OptionStrictEnumProviderTest() + { + var dynamicEnumValuesGenerator = await new OptionStrictEnumProvider().GetProviderAsync(null); + var values = await dynamicEnumValuesGenerator.GetListedValuesAsync(); - [Fact] - public async Task OptionStrictEnumProviderTest() + var pageEnumRawValues = new List> { - var dynamicEnumValuesGenerator = await new OptionStrictEnumProvider().GetProviderAsync(null); - var values = await dynamicEnumValuesGenerator.GetListedValuesAsync(); + Tuple.Create("0", "Off", true), + Tuple.Create("1", "On", false) + }; + var pageEnumValues = CreateEnumValueInstances(pageEnumRawValues); - var pageEnumRawValues = new List> - { - Tuple.Create("0", "Off", true), - Tuple.Create("1", "On", false) - }; - var pageEnumValues = CreateEnumValueInstances(pageEnumRawValues); + VerifySameValue(values, pageEnumValues); - VerifySameValue(values, pageEnumValues); + var persistencePageEnumRawValues = new List> + { + Tuple.Create("Off", "", false), + Tuple.Create("On", "", false) + }; + var persistencePageEnumValues = CreateEnumValueInstances(persistencePageEnumRawValues); - var persistencePageEnumRawValues = new List> - { - Tuple.Create("Off", "", false), - Tuple.Create("On", "", false) - }; - var persistencePageEnumValues = CreateEnumValueInstances(persistencePageEnumRawValues); + var keys = new List() { "0", "1" }; + var persistencePageEnumMap = CreateEnumValueMap(keys, persistencePageEnumValues); - var keys = new List() { "0", "1" }; - var persistencePageEnumMap = CreateEnumValueMap(keys, persistencePageEnumValues); + await VerifySameValueOnQueryAsync(dynamicEnumValuesGenerator, persistencePageEnumMap, checkMapNameOnly: true); + } - await VerifySameValueOnQueryAsync(dynamicEnumValuesGenerator, persistencePageEnumMap, checkMapNameOnly: true); - } + [Fact] + public async Task WarningLevelEnumProviderTest() + { + var dynamicEnumValuesGenerator = await new WarningLevelEnumProvider().GetProviderAsync(null); + var values = await dynamicEnumValuesGenerator.GetListedValuesAsync(); - [Fact] - public async Task WarningLevelEnumProviderTest() + var pageEnumRawValues = new List> { - var dynamicEnumValuesGenerator = await new WarningLevelEnumProvider().GetProviderAsync(null); - var values = await dynamicEnumValuesGenerator.GetListedValuesAsync(); - - var pageEnumRawValues = new List> - { - Tuple.Create("0", "", false), - Tuple.Create("1", "", false), - Tuple.Create("2", "", false), - Tuple.Create("3", "", false), - Tuple.Create("4", "", false) - }; - var pageEnumValues = CreateEnumValueInstances(pageEnumRawValues); - - VerifySameValue(values, pageEnumValues, checkMapNameOnly: true); - - var keys = new List() - { - nameof(prjWarningLevel.prjWarningLevel0), - nameof(prjWarningLevel.prjWarningLevel1), - nameof(prjWarningLevel.prjWarningLevel2), - nameof(prjWarningLevel.prjWarningLevel3), - nameof(prjWarningLevel.prjWarningLevel4), - }; - var persistencePageEnumMap = CreateEnumValueMap(keys, pageEnumValues); - - await VerifySameValueOnQueryAsync(dynamicEnumValuesGenerator, persistencePageEnumMap, checkMapNameOnly: true); - } + Tuple.Create("0", "", false), + Tuple.Create("1", "", false), + Tuple.Create("2", "", false), + Tuple.Create("3", "", false), + Tuple.Create("4", "", false) + }; + var pageEnumValues = CreateEnumValueInstances(pageEnumRawValues); + + VerifySameValue(values, pageEnumValues, checkMapNameOnly: true); + + var keys = new List() + { + nameof(prjWarningLevel.prjWarningLevel0), + nameof(prjWarningLevel.prjWarningLevel1), + nameof(prjWarningLevel.prjWarningLevel2), + nameof(prjWarningLevel.prjWarningLevel3), + nameof(prjWarningLevel.prjWarningLevel4), + }; + var persistencePageEnumMap = CreateEnumValueMap(keys, pageEnumValues); + + await VerifySameValueOnQueryAsync(dynamicEnumValuesGenerator, persistencePageEnumMap, checkMapNameOnly: true); + } - private static void VerifySameValue(IEnumValue? actual, IEnumValue expected, bool checkMapNameOnly = false) - { - Assert.NotNull(actual); - Assert.Equal(expected.Name, actual.Name); - - if (!checkMapNameOnly) - { - Assert.Equal(expected.DisplayName, actual.DisplayName); - Assert.True(actual.IsDefault == expected.IsDefault); - } - } + private static void VerifySameValue(IEnumValue? actual, IEnumValue expected, bool checkMapNameOnly = false) + { + Assert.NotNull(actual); + Assert.Equal(expected.Name, actual.Name); - private static void VerifySameValue(IEnumerable actual, IEnumerable expected, bool checkMapNameOnly = false) + if (!checkMapNameOnly) { - var actualAsArray = actual.ToArray(); - var expectedAsArray = expected.ToArray(); + Assert.Equal(expected.DisplayName, actual.DisplayName); + Assert.True(actual.IsDefault == expected.IsDefault); + } + } - Assert.Equal(actualAsArray.Length, expectedAsArray.Length); + private static void VerifySameValue(IEnumerable actual, IEnumerable expected, bool checkMapNameOnly = false) + { + var actualAsArray = actual.ToArray(); + var expectedAsArray = expected.ToArray(); - for (var i = 0; i < actualAsArray.Length; ++i) - { - VerifySameValue(actualAsArray[i], expectedAsArray[i], checkMapNameOnly); - } - } + Assert.Equal(actualAsArray.Length, expectedAsArray.Length); - private static async Task VerifySameValueOnQueryAsync(IDynamicEnumValuesGenerator generator, Dictionary persistencePageEnumMap, bool checkMapNameOnly = false) + for (var i = 0; i < actualAsArray.Length; ++i) { - foreach (var key in persistencePageEnumMap.Keys) - { - VerifySameValue(await generator.TryCreateEnumValueAsync(key), persistencePageEnumMap[key], checkMapNameOnly); - } + VerifySameValue(actualAsArray[i], expectedAsArray[i], checkMapNameOnly); } + } - private static PageEnumValue CreateEnumValueInstance(string name, string displayName, bool isDefault = false) + private static async Task VerifySameValueOnQueryAsync(IDynamicEnumValuesGenerator generator, Dictionary persistencePageEnumMap, bool checkMapNameOnly = false) + { + foreach (var key in persistencePageEnumMap.Keys) { - return new PageEnumValue(new EnumValue { Name = name, DisplayName = displayName, IsDefault = isDefault }); + VerifySameValue(await generator.TryCreateEnumValueAsync(key), persistencePageEnumMap[key], checkMapNameOnly); } + } + + private static PageEnumValue CreateEnumValueInstance(string name, string displayName, bool isDefault = false) + { + return new PageEnumValue(new EnumValue { Name = name, DisplayName = displayName, IsDefault = isDefault }); + } - private static IEnumerable CreateEnumValueInstances(List> pageEnumValues) + private static IEnumerable CreateEnumValueInstances(List> pageEnumValues) + { + foreach ((string name, string displayName, bool isDefault) in pageEnumValues) { - foreach ((string name, string displayName, bool isDefault) in pageEnumValues) - { - yield return CreateEnumValueInstance(name, displayName, isDefault); - } + yield return CreateEnumValueInstance(name, displayName, isDefault); } + } - private static Dictionary CreateEnumValueMap(List keys, IEnumerable pageEnumValues) - { - int index = 0; - Dictionary map = new(); + private static Dictionary CreateEnumValueMap(List keys, IEnumerable pageEnumValues) + { + int index = 0; + Dictionary map = new(); - foreach (var item in pageEnumValues) - { - map[keys[index]] = item; - index++; - } + foreach (var item in pageEnumValues) + { + map[keys[index]] = item; + index++; + } - Assert.Equal(index, keys.Count); + Assert.Equal(index, keys.Count); - return map; - } + return map; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationPropertiesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationPropertiesTests.cs index f016e19bb5..229caf930b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationPropertiesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectConfigurationPropertiesTests.cs @@ -1,81 +1,80 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +public class VisualBasicProjectConfigurationPropertiesTests { - public class VisualBasicProjectConfigurationPropertiesTests + [Fact] + public void Constructor_NullAsProjectProperties_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsProjectProperties_ThrowsArgumentNull() - { - Assert.Throws("projectProperties", () => - { - new VisualBasicProjectConfigurationProperties(null!, null!); - }); - } - - [Fact] - public void Constructor_NullAsThreadingService_ThrowsArgumentNull() + Assert.Throws("projectProperties", () => { - Assert.Throws("threadingService", () => - { - new VisualBasicProjectConfigurationProperties(ProjectPropertiesFactory.CreateEmpty(), null!); - }); - } - - [Fact] - public void VisualBasicProjectConfigurationProperties_CodeAnalysisRuleSet() + new VisualBasicProjectConfigurationProperties(null!, null!); + }); + } + + [Fact] + public void Constructor_NullAsThreadingService_ThrowsArgumentNull() + { + Assert.Throws("threadingService", () => { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.CodeAnalysisRuleSetProperty, "Blah", setValues); + new VisualBasicProjectConfigurationProperties(ProjectPropertiesFactory.CreateEmpty(), null!); + }); + } - var projectProperties = ProjectPropertiesFactory.Create(project, data); + [Fact] + public void VisualBasicProjectConfigurationProperties_CodeAnalysisRuleSet() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.CodeAnalysisRuleSetProperty, "Blah", setValues); - var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); - Assert.Equal("Blah", vsLangProjectProperties.CodeAnalysisRuleSet); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var testValue = "Testing"; - vsLangProjectProperties.CodeAnalysisRuleSet = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); + Assert.Equal("Blah", vsLangProjectProperties.CodeAnalysisRuleSet); - [Fact] - public void VisualBasicProjectConfigurationProperties_LangVersion() - { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.LangVersionProperty, "9", setValues); + var testValue = "Testing"; + vsLangProjectProperties.CodeAnalysisRuleSet = testValue; + Assert.Equal(setValues.Single(), testValue); + } - var projectProperties = ProjectPropertiesFactory.Create(project, data); + [Fact] + public void VisualBasicProjectConfigurationProperties_LangVersion() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.LangVersionProperty, "9", setValues); - var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); - Assert.Equal("9", vsLangProjectProperties.LanguageVersion); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var testValue = "10"; - vsLangProjectProperties.LanguageVersion = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); + Assert.Equal("9", vsLangProjectProperties.LanguageVersion); - [Fact] - public void VisualBasicProjectConfigurationProperties_OutputPath() - { - var setValues = new List(); - var project = UnconfiguredProjectFactory.Create(); - var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.OutputPathProperty, "OldPath", setValues); + var testValue = "10"; + vsLangProjectProperties.LanguageVersion = testValue; + Assert.Equal(setValues.Single(), testValue); + } + + [Fact] + public void VisualBasicProjectConfigurationProperties_OutputPath() + { + var setValues = new List(); + var project = UnconfiguredProjectFactory.Create(); + var data = new PropertyPageData(ConfiguredBrowseObject.SchemaName, ConfiguredBrowseObject.OutputPathProperty, "OldPath", setValues); - var projectProperties = ProjectPropertiesFactory.Create(project, data); + var projectProperties = ProjectPropertiesFactory.Create(project, data); - var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); - Assert.Equal("OldPath", vsLangProjectProperties.OutputPath); + var vsLangProjectProperties = CreateInstance(projectProperties, IProjectThreadingServiceFactory.Create()); + Assert.Equal("OldPath", vsLangProjectProperties.OutputPath); - var testValue = "NewPath"; - vsLangProjectProperties.OutputPath = testValue; - Assert.Equal(setValues.Single(), testValue); - } + var testValue = "NewPath"; + vsLangProjectProperties.OutputPath = testValue; + Assert.Equal(setValues.Single(), testValue); + } - private static VisualBasicProjectConfigurationProperties CreateInstance(ProjectProperties projectProperties, IProjectThreadingService projectThreadingService) - { - return new VisualBasicProjectConfigurationProperties(projectProperties, projectThreadingService); - } + private static VisualBasicProjectConfigurationProperties CreateInstance(ProjectProperties projectProperties, IProjectThreadingService projectThreadingService) + { + return new VisualBasicProjectConfigurationProperties(projectProperties, projectThreadingService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProviderTests.cs index 1198331ed7..2eb4d1df19 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Properties/VisualBasic/VisualBasicProjectDesignerPageProviderTests.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties.VisualBasic; + +public class VisualBasicProjectDesignerPageProviderTests { - public class VisualBasicProjectDesignerPageProviderTests + [Fact] + public void Constructor_DoesNotThrow() + { + CreateInstance(); + } + + [Fact] + public async Task GetPagesAsync_WhenAllCapabilitiesPresent_ReturnsPagesInOrder() + { + var provider = CreateInstance(ProjectCapability.LaunchProfiles, ProjectCapability.Pack); + var result = await provider.GetPagesAsync(); + + var expected = ImmutableArray.Create( + VisualBasicProjectDesignerPage.Application, + VisualBasicProjectDesignerPage.Compile, + VisualBasicProjectDesignerPage.Package, + VisualBasicProjectDesignerPage.References, + VisualBasicProjectDesignerPage.Debug, + VisualBasicProjectDesignerPage.Signing, + VisualBasicProjectDesignerPage.CodeAnalysis + ); + + Assert.Equal(expected, result); + } + + [Fact] + public async Task GetPagesAsync_WhenNoLaunchProfilesCapability_DoesNotContainDebugPage() + { + var provider = CreateInstance(); + var result = await provider.GetPagesAsync(); + + Assert.DoesNotContain(VisualBasicProjectDesignerPage.Debug, result); + } + + [Fact] + public async Task GetPagesAsync_WhenNoPackCapability_DoesNotContainPackagePage() + { + var provider = CreateInstance(); + var result = await provider.GetPagesAsync(); + + Assert.DoesNotContain(VisualBasicProjectDesignerPage.Package, result); + } + + private static VisualBasicProjectDesignerPageProvider CreateInstance(params string[] capabilities) { - [Fact] - public void Constructor_DoesNotThrow() - { - CreateInstance(); - } - - [Fact] - public async Task GetPagesAsync_WhenAllCapabilitiesPresent_ReturnsPagesInOrder() - { - var provider = CreateInstance(ProjectCapability.LaunchProfiles, ProjectCapability.Pack); - var result = await provider.GetPagesAsync(); - - var expected = ImmutableArray.Create( - VisualBasicProjectDesignerPage.Application, - VisualBasicProjectDesignerPage.Compile, - VisualBasicProjectDesignerPage.Package, - VisualBasicProjectDesignerPage.References, - VisualBasicProjectDesignerPage.Debug, - VisualBasicProjectDesignerPage.Signing, - VisualBasicProjectDesignerPage.CodeAnalysis - ); - - Assert.Equal(expected, result); - } - - [Fact] - public async Task GetPagesAsync_WhenNoLaunchProfilesCapability_DoesNotContainDebugPage() - { - var provider = CreateInstance(); - var result = await provider.GetPagesAsync(); - - Assert.DoesNotContain(VisualBasicProjectDesignerPage.Debug, result); - } - - [Fact] - public async Task GetPagesAsync_WhenNoPackCapability_DoesNotContainPackagePage() - { - var provider = CreateInstance(); - var result = await provider.GetPagesAsync(); - - Assert.DoesNotContain(VisualBasicProjectDesignerPage.Package, result); - } - - private static VisualBasicProjectDesignerPageProvider CreateInstance(params string[] capabilities) - { - bool containsCapability(string c) => capabilities.Contains(c); - var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(containsCapability); - return new VisualBasicProjectDesignerPageProvider(capabilitiesService); - } + bool containsCapability(string c) => capabilities.Contains(c); + var capabilitiesService = IProjectCapabilitiesServiceFactory.ImplementsContains(containsCapability); + return new VisualBasicProjectDesignerPageProvider(capabilitiesService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/BuildMacroInfoTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/BuildMacroInfoTests.cs index 4325adf347..075f02c91e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/BuildMacroInfoTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/BuildMacroInfoTests.cs @@ -1,44 +1,43 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +public class BuildMacroInfoTests { - public class BuildMacroInfoTests + [Theory] + [InlineData("MyBuildMacro", "MyBuildMacroValue", VSConstants.S_OK)] + [InlineData("NonExistantMacro", "", VSConstants.E_FAIL)] + public void GetBuildMacroValue(string macroName, string expectedValue, int expectedRetVal) + { + var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("MyBuildMacro", "MyBuildMacroValue"); + var propertiesProvider = IProjectPropertiesProviderFactory.Create(commonProps: projectProperties); + var configuredProjectServices = Mock.Of(o => + o.ProjectPropertiesProvider == propertiesProvider); + var configuredProject = ConfiguredProjectFactory.Create(services: configuredProjectServices); + + var buildMacroInfo = CreateInstance(configuredProject); + int retVal = buildMacroInfo.GetBuildMacroValue(macroName, out string? macroValue); + Assert.Equal(expectedRetVal, retVal); + Assert.Equal(expectedValue, macroValue); + } + + [Fact] + public void GetBuildMacroValue_WhenDisposed_ReturnsUnexpected() { - [Theory] - [InlineData("MyBuildMacro", "MyBuildMacroValue", VSConstants.S_OK)] - [InlineData("NonExistantMacro", "", VSConstants.E_FAIL)] - public void GetBuildMacroValue(string macroName, string expectedValue, int expectedRetVal) - { - var projectProperties = IProjectPropertiesFactory.CreateWithPropertyAndValue("MyBuildMacro", "MyBuildMacroValue"); - var propertiesProvider = IProjectPropertiesProviderFactory.Create(commonProps: projectProperties); - var configuredProjectServices = Mock.Of(o => - o.ProjectPropertiesProvider == propertiesProvider); - var configuredProject = ConfiguredProjectFactory.Create(services: configuredProjectServices); - - var buildMacroInfo = CreateInstance(configuredProject); - int retVal = buildMacroInfo.GetBuildMacroValue(macroName, out string? macroValue); - Assert.Equal(expectedRetVal, retVal); - Assert.Equal(expectedValue, macroValue); - } - - [Fact] - public void GetBuildMacroValue_WhenDisposed_ReturnsUnexpected() - { - var buildMacroInfo = CreateInstance(); - buildMacroInfo.Dispose(); - - var result = buildMacroInfo.GetBuildMacroValue("Macro", out _); - - Assert.Equal(VSConstants.E_UNEXPECTED, result); - } - - private static BuildMacroInfo CreateInstance(ConfiguredProject? configuredProject = null) - { - configuredProject ??= ConfiguredProjectFactory.Create(); - - var threadingService = IProjectThreadingServiceFactory.Create(); - - return new BuildMacroInfo(IActiveConfiguredValueFactory.ImplementValue(() => configuredProject), threadingService); - } + var buildMacroInfo = CreateInstance(); + buildMacroInfo.Dispose(); + + var result = buildMacroInfo.GetBuildMacroValue("Macro", out _); + + Assert.Equal(VSConstants.E_UNEXPECTED, result); + } + + private static BuildMacroInfo CreateInstance(ConfiguredProject? configuredProject = null) + { + configuredProject ??= ConfiguredProjectFactory.Create(); + + var threadingService = IProjectThreadingServiceFactory.Create(); + + return new BuildMacroInfo(IActiveConfiguredValueFactory.ImplementValue(() => configuredProject), threadingService); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/PropertyPageTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/PropertyPageTests.cs index c9dbaf9fc7..bf71d628ca 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/PropertyPageTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/PropertyPages/PropertyPageTests.cs @@ -4,74 +4,73 @@ using Microsoft.VisualStudio.OLE.Interop; using Moq.Protected; -namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages +namespace Microsoft.VisualStudio.ProjectSystem.VS.PropertyPages; + +public class PropertyPageTests { - public class PropertyPageTests + [Fact] + public void GetPageInfoAndHelp() { - [Fact] - public void GetPageInfoAndHelp() - { - Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(UIPermissionAttribute)); + Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(UIPermissionAttribute)); - var page = new Mock(); - page.Protected().Setup("PropertyPageName").Returns("MyPage"); - var pageInfoArray = new PROPPAGEINFO[1]; - page.Object.GetPageInfo(pageInfoArray); - page.Object.Help(string.Empty); + var page = new Mock(); + page.Protected().Setup("PropertyPageName").Returns("MyPage"); + var pageInfoArray = new PROPPAGEINFO[1]; + page.Object.GetPageInfo(pageInfoArray); + page.Object.Help(string.Empty); - PROPPAGEINFO info = pageInfoArray[0]; - Assert.Equal("MyPage", info.pszTitle); - Assert.Equal(0u, info.dwHelpContext); - Assert.Null(info.pszDocString); - Assert.Null(info.pszHelpFile); - Assert.Equal(page.Object.Size.Width, info.SIZE.cx); - Assert.Equal(page.Object.Size.Height, info.SIZE.cy); - } + PROPPAGEINFO info = pageInfoArray[0]; + Assert.Equal("MyPage", info.pszTitle); + Assert.Equal(0u, info.dwHelpContext); + Assert.Null(info.pszDocString); + Assert.Null(info.pszHelpFile); + Assert.Equal(page.Object.Size.Width, info.SIZE.cx); + Assert.Equal(page.Object.Size.Height, info.SIZE.cy); + } - [Fact] - public void MoveTest() - { - Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(UIPermissionAttribute)); + [Fact] + public void MoveTest() + { + Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(UIPermissionAttribute)); - var rect = new[] { new RECT() { left = 25, top = 25 } }; - var page = new Mock() - { - CallBase = true - }; - page.Object.Move(rect); + var rect = new[] { new RECT() { left = 25, top = 25 } }; + var page = new Mock() + { + CallBase = true + }; + page.Object.Move(rect); - Assert.Equal(rect[0].left, page.Object.Location.X); - Assert.Equal(rect[0].top, page.Object.Location.Y); - } + Assert.Equal(rect[0].left, page.Object.Location.X); + Assert.Equal(rect[0].top, page.Object.Location.Y); + } - [Fact] - public void MoveThrowsArgumentExceptionIfNullRect() + [Fact] + public void MoveThrowsArgumentExceptionIfNullRect() + { + Assert.Throws(() => { - Assert.Throws(() => - { - Move(null!); - }); - } + Move(null!); + }); + } - [Fact] - public void MoveThrowsArgumentExceptionIfEmptyRect() + [Fact] + public void MoveThrowsArgumentExceptionIfEmptyRect() + { + Assert.Throws(() => { - Assert.Throws(() => - { - Move(new RECT[0]); - }); - } + Move(new RECT[0]); + }); + } - private static void Move(RECT[] x) - { - Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(UIPermissionAttribute)); + private static void Move(RECT[] x) + { + Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(UIPermissionAttribute)); - var page = new Mock() - { - CallBase = true - }; + var page = new Mock() + { + CallBase = true + }; - page.Object.Move(x); - } + page.Object.Move(x); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandlerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandlerTests.cs index a698cd1b86..5ee59273a8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandlerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/LaunchProfiles/ProjectLaunchProfileHandlerTests.cs @@ -4,235 +4,234 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query.LaunchProfiles +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query.LaunchProfiles; + +public class ProjectLaunchProfileHandlerTests { - public class ProjectLaunchProfileHandlerTests + [Fact] + public async Task WhenAddingANewProfile_AndAProfileNameIsProvided_TheProvidedNameIsUsed() { - [Fact] - public async Task WhenAddingANewProfile_AndAProfileNameIsProvided_TheProvidedNameIsUsed() - { - var project = UnconfiguredProjectFactory.Create(); - ILaunchProfile? newProfile = null; + var project = UnconfiguredProjectFactory.Create(); + ILaunchProfile? newProfile = null; + + var profiles = new List(); + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); newProfile = profile; }, + getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); - var profiles = new List(); - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); newProfile = profile; }, - getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); + var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); + var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); - var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); - var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); + var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); - var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); + var context = IQueryExecutionContextFactory.Create(); + var parent = IEntityWithIdFactory.Create("Project", "MyProject"); - var context = IQueryExecutionContextFactory.Create(); - var parent = IEntityWithIdFactory.Create("Project", "MyProject"); + var newProfileId = await handler.AddLaunchProfileAsync(context, parent, commandName: "Alpha", newProfileName: "Beta"); - var newProfileId = await handler.AddLaunchProfileAsync(context, parent, commandName: "Alpha", newProfileName: "Beta"); + Assert.NotNull(newProfile); + Assert.Equal(expected: "Beta", actual: newProfile.Name); + Assert.Equal(expected: "Alpha", actual: newProfile.CommandName); + Assert.Single(profiles); - Assert.NotNull(newProfile); - Assert.Equal(expected: "Beta", actual: newProfile.Name); - Assert.Equal(expected: "Alpha", actual: newProfile.CommandName); - Assert.Single(profiles); + Assert.NotNull(newProfileId); + Assert.Equal(expected: "LaunchProfile", actual: newProfileId[ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: "Beta", actual: newProfileId[ProjectModelIdentityKeys.SourceItemName]); + } - Assert.NotNull(newProfileId); - Assert.Equal(expected: "LaunchProfile", actual: newProfileId[ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: "Beta", actual: newProfileId[ProjectModelIdentityKeys.SourceItemName]); - } + [Fact] + public async Task WhenAddingANewProfile_AndAProfileNameIsNotProvided_AUniqueNameIsGenerated() + { + var project = UnconfiguredProjectFactory.Create(); + ILaunchProfile? newProfile = null; - [Fact] - public async Task WhenAddingANewProfile_AndAProfileNameIsNotProvided_AUniqueNameIsGenerated() - { - var project = UnconfiguredProjectFactory.Create(); - ILaunchProfile? newProfile = null; + var profiles = new List(); + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); newProfile = profile; }, + getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); - var profiles = new List(); - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); newProfile = profile; }, - getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); + var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); + var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); - var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); - var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); + var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); - var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); + var context = IQueryExecutionContextFactory.Create(); + var parent = IEntityWithIdFactory.Create("Project", "MyProject"); - var context = IQueryExecutionContextFactory.Create(); - var parent = IEntityWithIdFactory.Create("Project", "MyProject"); + var newProfileId = await handler.AddLaunchProfileAsync(context, parent, commandName: "Alpha", newProfileName: null); - var newProfileId = await handler.AddLaunchProfileAsync(context, parent, commandName: "Alpha", newProfileName: null); + Assert.NotNull(newProfile); + Assert.Equal(expected: "Alpha", actual: newProfile.CommandName); + Assert.NotNull(newProfile.Name); - Assert.NotNull(newProfile); - Assert.Equal(expected: "Alpha", actual: newProfile.CommandName); - Assert.NotNull(newProfile.Name); + var firstProfileName = newProfile.Name; - var firstProfileName = newProfile.Name; + Assert.NotNull(newProfileId); + Assert.Equal(expected: "LaunchProfile", actual: newProfileId[ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: newProfile.Name, actual: newProfileId[ProjectModelIdentityKeys.SourceItemName]); - Assert.NotNull(newProfileId); - Assert.Equal(expected: "LaunchProfile", actual: newProfileId[ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: newProfile.Name, actual: newProfileId[ProjectModelIdentityKeys.SourceItemName]); + newProfileId = await handler.AddLaunchProfileAsync(context, parent, commandName: "Beta", newProfileName: null); - newProfileId = await handler.AddLaunchProfileAsync(context, parent, commandName: "Beta", newProfileName: null); + Assert.NotNull(newProfile); + Assert.Equal(expected: "Beta", actual: newProfile.CommandName); + Assert.NotNull(newProfile.Name); - Assert.NotNull(newProfile); - Assert.Equal(expected: "Beta", actual: newProfile.CommandName); - Assert.NotNull(newProfile.Name); + var secondProfileName = newProfile.Name; - var secondProfileName = newProfile.Name; + Assert.NotNull(newProfileId); + Assert.Equal(expected: "LaunchProfile", actual: newProfileId[ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: newProfile.Name, actual: newProfileId[ProjectModelIdentityKeys.SourceItemName]); - Assert.NotNull(newProfileId); - Assert.Equal(expected: "LaunchProfile", actual: newProfileId[ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: newProfile.Name, actual: newProfileId[ProjectModelIdentityKeys.SourceItemName]); + Assert.NotEqual(firstProfileName, secondProfileName); - Assert.NotEqual(firstProfileName, secondProfileName); + Assert.Equal(expected: 2, actual: profiles.Count); + } - Assert.Equal(expected: 2, actual: profiles.Count); - } + [Fact] + public async Task WhenDuplicatingAProfile_AndNameAndCommandAreProvided_TheNameAndCommandAreUsed() + { + var project = UnconfiguredProjectFactory.Create(); + ILaunchProfile? duplicatedProfile = null; - [Fact] - public async Task WhenDuplicatingAProfile_AndNameAndCommandAreProvided_TheNameAndCommandAreUsed() + var profiles = new List { - var project = UnconfiguredProjectFactory.Create(); - ILaunchProfile? duplicatedProfile = null; + new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: profiles, + addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); duplicatedProfile = profile; }, + getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); - var profiles = new List - { - new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: profiles, - addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); duplicatedProfile = profile; }, - getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); + var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); + var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); - var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); - var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); + var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); - var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); + var context = IQueryExecutionContextFactory.Create(); + var parent = IEntityWithIdFactory.Create("Project", "MyProject"); - var context = IQueryExecutionContextFactory.Create(); - var parent = IEntityWithIdFactory.Create("Project", "MyProject"); + var duplicatedProfileId = await handler.DuplicateLaunchProfileAsync(context, parent, currentProfileName: "Alpha", newProfileName: "Gamma", newProfileCommandName: "Delta"); - var duplicatedProfileId = await handler.DuplicateLaunchProfileAsync(context, parent, currentProfileName: "Alpha", newProfileName: "Gamma", newProfileCommandName: "Delta"); + Assert.Equal(expected: "Gamma", actual: duplicatedProfile!.Name); + Assert.Equal(expected: "Delta", actual: duplicatedProfile!.CommandName); + Assert.Equal(expected: @"C:\iguana\aardvark.exe", actual: duplicatedProfile!.ExecutablePath); + Assert.Equal(expected: 2, actual: profiles.Count); - Assert.Equal(expected: "Gamma", actual: duplicatedProfile!.Name); - Assert.Equal(expected: "Delta", actual: duplicatedProfile!.CommandName); - Assert.Equal(expected: @"C:\iguana\aardvark.exe", actual: duplicatedProfile!.ExecutablePath); - Assert.Equal(expected: 2, actual: profiles.Count); + Assert.NotNull(duplicatedProfileId); + Assert.Equal(expected: "LaunchProfile", actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: duplicatedProfile.Name, actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemName]); + } - Assert.NotNull(duplicatedProfileId); - Assert.Equal(expected: "LaunchProfile", actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: duplicatedProfile.Name, actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemName]); - } + [Fact] + public async Task WhenDuplicatingAProfile_AndNameAndCommandAreNotProvided_DefaultsAreProvided() + { + var project = UnconfiguredProjectFactory.Create(); + ILaunchProfile? duplicatedProfile = null; - [Fact] - public async Task WhenDuplicatingAProfile_AndNameAndCommandAreNotProvided_DefaultsAreProvided() + var profiles = new List { - var project = UnconfiguredProjectFactory.Create(); - ILaunchProfile? duplicatedProfile = null; - - var profiles = new List - { - new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: profiles, - addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); duplicatedProfile = profile; }, - getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); - - var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); - var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); - - var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); - - var context = IQueryExecutionContextFactory.Create(); - var parent = IEntityWithIdFactory.Create("Project", "MyProject"); - - var duplicatedProfileId = await handler.DuplicateLaunchProfileAsync(context, parent, currentProfileName: "Alpha", newProfileName: null, newProfileCommandName: null); - - Assert.NotNull(duplicatedProfile); - Assert.NotNull(duplicatedProfile.Name); - Assert.NotEqual(expected: "Alpha", actual: duplicatedProfile.Name); - Assert.Equal(expected: "Beta", actual: duplicatedProfile.CommandName); - Assert.Equal(expected: @"C:\iguana\aardvark.exe", actual: duplicatedProfile.ExecutablePath); - Assert.Equal(expected: 2, actual: profiles.Count); - - Assert.NotNull(duplicatedProfileId); - Assert.Equal(expected: "LaunchProfile", actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: duplicatedProfile.Name, actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemName]); - } - - [Fact] - public async Task WhenRenamingAProfile_TheOldProfileIsRemovedAndTheNewProfileIsAdded() + new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: profiles, + addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); duplicatedProfile = profile; }, + getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); + + var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); + var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); + + var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); + + var context = IQueryExecutionContextFactory.Create(); + var parent = IEntityWithIdFactory.Create("Project", "MyProject"); + + var duplicatedProfileId = await handler.DuplicateLaunchProfileAsync(context, parent, currentProfileName: "Alpha", newProfileName: null, newProfileCommandName: null); + + Assert.NotNull(duplicatedProfile); + Assert.NotNull(duplicatedProfile.Name); + Assert.NotEqual(expected: "Alpha", actual: duplicatedProfile.Name); + Assert.Equal(expected: "Beta", actual: duplicatedProfile.CommandName); + Assert.Equal(expected: @"C:\iguana\aardvark.exe", actual: duplicatedProfile.ExecutablePath); + Assert.Equal(expected: 2, actual: profiles.Count); + + Assert.NotNull(duplicatedProfileId); + Assert.Equal(expected: "LaunchProfile", actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: duplicatedProfile.Name, actual: duplicatedProfileId[ProjectModelIdentityKeys.SourceItemName]); + } + + [Fact] + public async Task WhenRenamingAProfile_TheOldProfileIsRemovedAndTheNewProfileIsAdded() + { + var project = UnconfiguredProjectFactory.Create(); + ILaunchProfile? addedProfile = null; + string? removedProfileName = null; + + var profiles = new List { - var project = UnconfiguredProjectFactory.Create(); - ILaunchProfile? addedProfile = null; - string? removedProfileName = null; + new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: profiles, + addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); addedProfile = profile; }, + removeProfileCallback: (profileName) => { removedProfileName = profileName; profiles.RemoveAll(p => p.Name == profileName); }, + getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); - var profiles = new List - { - new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: profiles, - addOrUpdateProfileCallback: (profile, addToFront) => { profiles.Add(profile); addedProfile = profile; }, - removeProfileCallback: (profileName) => { removedProfileName = profileName; profiles.RemoveAll(p => p.Name == profileName); }, - getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); + var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); + var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); - var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); - var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); + var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); - var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); + var context = IQueryExecutionContextFactory.Create(); + var parent = IEntityWithIdFactory.Create("Project", "MyProject"); - var context = IQueryExecutionContextFactory.Create(); - var parent = IEntityWithIdFactory.Create("Project", "MyProject"); + var changes = await handler.RenameLaunchProfileAsync(context, parent, currentProfileName: "Alpha", newProfileName: "Gamma"); - var changes = await handler.RenameLaunchProfileAsync(context, parent, currentProfileName: "Alpha", newProfileName: "Gamma"); + Assert.True(changes.HasValue); - Assert.True(changes.HasValue); + (EntityIdentity removedProfileId, EntityIdentity addedProfileId) = changes.Value; - (EntityIdentity removedProfileId, EntityIdentity addedProfileId) = changes.Value; + Assert.Equal(expected: "Gamma", actual: addedProfile!.Name); + Assert.Equal(expected: "Beta", actual: addedProfile!.CommandName); + Assert.Equal(expected: @"C:\iguana\aardvark.exe", actual: addedProfile!.ExecutablePath); + Assert.Single(profiles); - Assert.Equal(expected: "Gamma", actual: addedProfile!.Name); - Assert.Equal(expected: "Beta", actual: addedProfile!.CommandName); - Assert.Equal(expected: @"C:\iguana\aardvark.exe", actual: addedProfile!.ExecutablePath); - Assert.Single(profiles); + Assert.Equal(expected: "LaunchProfile", actual: removedProfileId![ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: "Alpha", actual: removedProfileId![ProjectModelIdentityKeys.SourceItemName]); - Assert.Equal(expected: "LaunchProfile", actual: removedProfileId![ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: "Alpha", actual: removedProfileId![ProjectModelIdentityKeys.SourceItemName]); + Assert.Equal(expected: "LaunchProfile", actual: addedProfileId![ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: "Gamma", actual: addedProfileId![ProjectModelIdentityKeys.SourceItemName]); + } - Assert.Equal(expected: "LaunchProfile", actual: addedProfileId![ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: "Gamma", actual: addedProfileId![ProjectModelIdentityKeys.SourceItemName]); - } + [Fact] + public async Task WhenRemovingAProfile_TheProfileIsRemoved() + { + var project = UnconfiguredProjectFactory.Create(); + string? removedProfileName = null; - [Fact] - public async Task WhenRemovingAProfile_TheProfileIsRemoved() + var profiles = new List { - var project = UnconfiguredProjectFactory.Create(); - string? removedProfileName = null; - - var profiles = new List - { - new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() - }; - var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( - launchProfiles: profiles, - removeProfileCallback: (profileName) => { profiles.RemoveAll(p => p.Name == profileName); removedProfileName = profileName; }, - getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); + new WritableLaunchProfile { Name = "Alpha", CommandName = "Beta", ExecutablePath = @"C:\iguana\aardvark.exe" }.ToLaunchProfile() + }; + var launchSettingsProvider = ILaunchSettingsProviderFactory.Create( + launchProfiles: profiles, + removeProfileCallback: (profileName) => { profiles.RemoveAll(p => p.Name == profileName); removedProfileName = profileName; }, + getProfilesCallback: (p) => ImmutableList.CreateRange(profiles)); - var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); - var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); + var queryVersionProvider = new LaunchSettingsQueryVersionProvider(); + var tracker = new LaunchSettingsTracker(project, launchSettingsProvider, queryVersionProvider); - var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); + var handler = new ProjectLaunchProfileHandler(project, launchSettingsProvider, tracker); - var context = IQueryExecutionContextFactory.Create(); - var parent = IEntityWithIdFactory.Create("Project", "MyProject"); + var context = IQueryExecutionContextFactory.Create(); + var parent = IEntityWithIdFactory.Create("Project", "MyProject"); - var removedProfileId = await handler.RemoveLaunchProfileAsync(context, parent, profileName: "Alpha"); + var removedProfileId = await handler.RemoveLaunchProfileAsync(context, parent, profileName: "Alpha"); - Assert.Empty(profiles); - Assert.Equal(expected: "Alpha", actual: removedProfileName); + Assert.Empty(profiles); + Assert.Equal(expected: "Alpha", actual: removedProfileName); - Assert.NotNull(removedProfileId); - Assert.Equal(expected: "LaunchProfile", actual: removedProfileId[ProjectModelIdentityKeys.SourceItemType]); - Assert.Equal(expected: "Alpha", actual: removedProfileId[ProjectModelIdentityKeys.SourceItemName]); - } + Assert.NotNull(removedProfileId); + Assert.Equal(expected: "LaunchProfile", actual: removedProfileId[ProjectModelIdentityKeys.SourceItemType]); + Assert.Equal(expected: "Alpha", actual: removedProfileId[ProjectModelIdentityKeys.SourceItemName]); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducerTests.cs index 61dcb3d205..593dd53563 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/CategoryDataProducerTests.cs @@ -4,100 +4,99 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class CategoryDataProducerTests { - public class CategoryDataProducerTests + [Fact] + public void WhenPropertiesAreRequested_PropertyValuesAreReturned() { - [Fact] - public void WhenPropertiesAreRequested_PropertyValuesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); + var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "KeyValue"); - var rule = new Rule(); - var category = new Category { DisplayName = "CategoryDisplayName", Name = "CategoryName" }; - var order = 42; + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "KeyValue"); + var rule = new Rule(); + var category = new Category { DisplayName = "CategoryDisplayName", Name = "CategoryName" }; + var order = 42; - var result = (CategorySnapshot)CategoryDataProducer.CreateCategoryValue(context, parentEntity, rule, category, order, properties); + var result = (CategorySnapshot)CategoryDataProducer.CreateCategoryValue(context, parentEntity, rule, category, order, properties); - Assert.Equal(expected: "CategoryDisplayName", actual: result.DisplayName); - Assert.Equal(expected: "CategoryName", actual: result.Name); - Assert.Equal(expected: 42, actual: result.Order); - } + Assert.Equal(expected: "CategoryDisplayName", actual: result.DisplayName); + Assert.Equal(expected: "CategoryName", actual: result.Name); + Assert.Equal(expected: 42, actual: result.Order); + } - [Fact] - public void WhenCategoryValueCreated_TheCategoryIsTheProviderState() - { - var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); + [Fact] + public void WhenCategoryValueCreated_TheCategoryIsTheProviderState() + { + var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "KeyValue"); - var rule = new Rule(); - var category = new Category { DisplayName = "CategoryDisplayName", Name = "CategoryName" }; - var order = 42; + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "KeyValue"); + var rule = new Rule(); + var category = new Category { DisplayName = "CategoryDisplayName", Name = "CategoryName" }; + var order = 42; - var result = (CategorySnapshot)CategoryDataProducer.CreateCategoryValue(context, parentEntity, rule, category, order, properties); + var result = (CategorySnapshot)CategoryDataProducer.CreateCategoryValue(context, parentEntity, rule, category, order, properties); - Assert.Equal(expected: category, actual: ((IEntityValueFromProvider)result).ProviderState); - } + Assert.Equal(expected: category, actual: ((IEntityValueFromProvider)result).ProviderState); + } - [Fact] - public void WhenCreatingACategory_TheIdIsTheCategoryName() - { - var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); + [Fact] + public void WhenCreatingACategory_TheIdIsTheCategoryName() + { + var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "KeyValue"); - var rule = new Rule(); - var category = new Category { DisplayName = "CategoryDisplayName", Name = "MyCategoryName" }; - var order = 42; + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "KeyValue"); + var rule = new Rule(); + var category = new Category { DisplayName = "CategoryDisplayName", Name = "MyCategoryName" }; + var order = 42; - var result = (CategorySnapshot)CategoryDataProducer.CreateCategoryValue(context, parentEntity, rule, category, order, properties); + var result = (CategorySnapshot)CategoryDataProducer.CreateCategoryValue(context, parentEntity, rule, category, order, properties); - Assert.True(result.Id.TryGetValue(ProjectModelIdentityKeys.CategoryName, out string? name)); - Assert.Equal(expected: "MyCategoryName", actual: name); - } + Assert.True(result.Id.TryGetValue(ProjectModelIdentityKeys.CategoryName, out string? name)); + Assert.Equal(expected: "MyCategoryName", actual: name); + } - [Fact] - public void WhenCreatingCategoriesFromARule_OneEntityIsCreatedPerCategory() - { - var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); + [Fact] + public void WhenCreatingCategoriesFromARule_OneEntityIsCreatedPerCategory() + { + var properties = PropertiesAvailableStatusFactory.CreateCategoryPropertiesAvailableStatus(includeAllProperties: true); - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "A", value: "B"); - var rule = new Rule + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "A", value: "B"); + var rule = new Rule + { + Categories = { - Categories = + new() { - new() - { - Name = "Alpha", - DisplayName = "First category" - - }, - new() - { - Name = "Beta", - DisplayName = "Second category" - } + Name = "Alpha", + DisplayName = "First category" + + }, + new() + { + Name = "Beta", + DisplayName = "Second category" } - }; + } + }; - var result = CategoryDataProducer.CreateCategoryValues(context, parentEntity, rule, properties); + var result = CategoryDataProducer.CreateCategoryValues(context, parentEntity, rule, properties); - Assert.Collection(result, new Action[] - { - entity => { assertEqual(entity, expectedName: "Alpha", expectedDisplayName: "First category"); }, - entity => { assertEqual(entity, expectedName: "Beta", expectedDisplayName: "Second category"); } - }); + Assert.Collection(result, new Action[] + { + entity => { assertEqual(entity, expectedName: "Alpha", expectedDisplayName: "First category"); }, + entity => { assertEqual(entity, expectedName: "Beta", expectedDisplayName: "Second category"); } + }); - static void assertEqual(IEntityValue entity, string expectedName, string expectedDisplayName) - { - var categoryEntity = (CategorySnapshot)entity; - Assert.Equal(expectedName, categoryEntity.Name); - Assert.Equal(expectedDisplayName, categoryEntity.DisplayName); - } + static void assertEqual(IEntityValue entity, string expectedName, string expectedDisplayName) + { + var categoryEntity = (CategorySnapshot)entity; + Assert.Equal(expectedName, categoryEntity.Name); + Assert.Equal(expectedDisplayName, categoryEntity.DisplayName); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducerTests.cs index 27bf32cfa5..cb8a142795 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ConfigurationDimensionDataProducerTests.cs @@ -3,69 +3,68 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class ConfigurationDimensionDataProducerTests { - public class ConfigurationDimensionDataProducerTests + [Fact] + public void WhenPropertiesAreRequested_PropertyValuesAreReturned() { - [Fact] - public void WhenPropertiesAreRequested_PropertyValuesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(includeAllProperties: true); + var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(includeAllProperties: true); - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var dimension = new KeyValuePair("AlphaDimension", "AlphaDimensionValue"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var dimension = new KeyValuePair("AlphaDimension", "AlphaDimensionValue"); - var result = (ConfigurationDimensionSnapshot)ConfigurationDimensionDataProducer.CreateProjectConfigurationDimension(entityRuntime, dimension, properties); + var result = (ConfigurationDimensionSnapshot)ConfigurationDimensionDataProducer.CreateProjectConfigurationDimension(entityRuntime, dimension, properties); - Assert.Equal(expected: "AlphaDimension", actual: result.Name); - Assert.Equal(expected: "AlphaDimensionValue", actual: result.Value); - } + Assert.Equal(expected: "AlphaDimension", actual: result.Name); + Assert.Equal(expected: "AlphaDimensionValue", actual: result.Value); + } - [Fact] - public void WhenPropertiesAreNotRequested_PropertyValuesAreNotReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(); + [Fact] + public void WhenPropertiesAreNotRequested_PropertyValuesAreNotReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(); - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var dimension = new KeyValuePair("AlphaDimension", "AlphaDimensionValue"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var dimension = new KeyValuePair("AlphaDimension", "AlphaDimensionValue"); - var result = (ConfigurationDimensionSnapshot)ConfigurationDimensionDataProducer.CreateProjectConfigurationDimension(entityRuntime, dimension, properties); + var result = (ConfigurationDimensionSnapshot)ConfigurationDimensionDataProducer.CreateProjectConfigurationDimension(entityRuntime, dimension, properties); - Assert.Throws(() => result.Name); - Assert.Throws(() => result.Value); - } + Assert.Throws(() => result.Name); + Assert.Throws(() => result.Value); + } - [Fact] - public void WhenThePropertyIsConfigurationDependent_OneEntityIsCreatedPerDimension() - { - var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(includeAllProperties: true); + [Fact] + public void WhenThePropertyIsConfigurationDependent_OneEntityIsCreatedPerDimension() + { + var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(includeAllProperties: true); - var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); - var configuration = ProjectConfigurationFactory.Create("Alpha|Beta|Gamma", "A|B|C"); - var property = IPropertyFactory.Create( - dataSource: IDataSourceFactory.Create(hasConfigurationCondition: true)); + var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); + var configuration = ProjectConfigurationFactory.Create("Alpha|Beta|Gamma", "A|B|C"); + var property = IPropertyFactory.Create( + dataSource: IDataSourceFactory.Create(hasConfigurationCondition: true)); - var results = ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions(parentEntity, configuration, property, properties); + var results = ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions(parentEntity, configuration, property, properties); - // We can't guarantee an order for the dimensions, so just check that all the expected values are present. - Assert.Contains(results, entity => entity is ConfigurationDimensionSnapshot { Name: "Alpha", Value: "A" }); - Assert.Contains(results, entity => entity is ConfigurationDimensionSnapshot { Name: "Beta", Value: "B" }); - Assert.Contains(results, entity => entity is ConfigurationDimensionSnapshot { Name: "Gamma", Value: "C" }); - } + // We can't guarantee an order for the dimensions, so just check that all the expected values are present. + Assert.Contains(results, entity => entity is ConfigurationDimensionSnapshot { Name: "Alpha", Value: "A" }); + Assert.Contains(results, entity => entity is ConfigurationDimensionSnapshot { Name: "Beta", Value: "B" }); + Assert.Contains(results, entity => entity is ConfigurationDimensionSnapshot { Name: "Gamma", Value: "C" }); + } - [Fact] - public void WhenThePropertyIsConfigurationIndependent_ThenNoDimensionsAreProduced() - { - var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(includeAllProperties: true); + [Fact] + public void WhenThePropertyIsConfigurationIndependent_ThenNoDimensionsAreProduced() + { + var properties = PropertiesAvailableStatusFactory.CreateConfigurationDimensionAvailableStatus(includeAllProperties: true); - var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); - var configuration = ProjectConfigurationFactory.Create("Alpha|Beta|Gamma", "A|B|C"); - var property = IPropertyFactory.Create( - dataSource: IDataSourceFactory.Create(hasConfigurationCondition: false)); + var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); + var configuration = ProjectConfigurationFactory.Create("Alpha|Beta|Gamma", "A|B|C"); + var property = IPropertyFactory.Create( + dataSource: IDataSourceFactory.Create(hasConfigurationCondition: false)); - var results = ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions(parentEntity, configuration, property, properties); + var results = ConfigurationDimensionDataProducer.CreateProjectConfigurationDimensions(parentEntity, configuration, property, properties); - Assert.Empty(results); - } + Assert.Empty(results); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ProjectActionProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ProjectActionProviderTests.cs index bdcf6cde16..cd9cc2002c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ProjectActionProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/ProjectActionProviderTests.cs @@ -2,172 +2,171 @@ using Microsoft.Build.Framework.XamlTypes; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class ProjectActionProviderTests { - public class ProjectActionProviderTests - { - private class TestProperty : BaseProperty { } + private class TestProperty : BaseProperty { } - // Creates a ConfiguredProject with a given ProjectConfiguration, and a method to - // call when a property is set in that configuration. - private static ConfiguredProject CreateConfiguredProject(ProjectConfiguration configuration, Action setValueCallback) - { - return ConfiguredProjectFactory.Create( - projectConfiguration: configuration, - services: ConfiguredProjectServicesFactory.Create( - IPropertyPagesCatalogProviderFactory.Create(new() + // Creates a ConfiguredProject with a given ProjectConfiguration, and a method to + // call when a property is set in that configuration. + private static ConfiguredProject CreateConfiguredProject(ProjectConfiguration configuration, Action setValueCallback) + { + return ConfiguredProjectFactory.Create( + projectConfiguration: configuration, + services: ConfiguredProjectServicesFactory.Create( + IPropertyPagesCatalogProviderFactory.Create(new() + { { + "Project", + IPropertyPagesCatalogFactory.Create(new Dictionary() { - "Project", - IPropertyPagesCatalogFactory.Create(new Dictionary() - { - { "MyPage", IRuleFactory.CreateFromRule(new Rule + { "MyPage", IRuleFactory.CreateFromRule(new Rule + { + Name = "MyPage", + Properties = new() { - Name = "MyPage", - Properties = new() + new TestProperty { - new TestProperty - { - Name = "MyProperty", - DataSource = new() { HasConfigurationCondition = true } - }, - new TestProperty - { - Name = "MyOtherProperty", - DataSource = new() { HasConfigurationCondition = true } - } + Name = "MyProperty", + DataSource = new() { HasConfigurationCondition = true } + }, + new TestProperty + { + Name = "MyOtherProperty", + DataSource = new() { HasConfigurationCondition = true } } - }, - properties: new[] - { - IPropertyFactory.Create( - "MyProperty", - dataSource: IDataSourceFactory.Create(hasConfigurationCondition: true), - setValue: v => setValueCallback("MyProperty", v)), - IPropertyFactory.Create( - "MyOtherProperty", - dataSource: IDataSourceFactory.Create(hasConfigurationCondition: true), - setValue: v => setValueCallback("MyOtherProperty", v)) - }) - } - }) - } - }))); - } + } + }, + properties: new[] + { + IPropertyFactory.Create( + "MyProperty", + dataSource: IDataSourceFactory.Create(hasConfigurationCondition: true), + setValue: v => setValueCallback("MyProperty", v)), + IPropertyFactory.Create( + "MyOtherProperty", + dataSource: IDataSourceFactory.Create(hasConfigurationCondition: true), + setValue: v => setValueCallback("MyOtherProperty", v)) + }) + } + }) + } + }))); + } - private static UnconfiguredProject CreateUnconfiguredProject(ImmutableHashSet projectConfigurations, IEnumerable configuredProjects) - { - var threadingService = IProjectThreadingServiceFactory.Create(); - var project = UnconfiguredProjectFactory.Create( - fullPath: @"C:\alpha\beta\MyProject.csproj", - configuredProjects: configuredProjects, - unconfiguredProjectServices: UnconfiguredProjectServicesFactory.Create( - threadingService: threadingService, - projectService: IProjectServiceFactory.Create( - services: ProjectServicesFactory.Create( - threadingService: threadingService)), - projectConfigurationsService: IProjectConfigurationsServiceFactory.ImplementGetKnownProjectConfigurationsAsync(projectConfigurations))); - return project; - } + private static UnconfiguredProject CreateUnconfiguredProject(ImmutableHashSet projectConfigurations, IEnumerable configuredProjects) + { + var threadingService = IProjectThreadingServiceFactory.Create(); + var project = UnconfiguredProjectFactory.Create( + fullPath: @"C:\alpha\beta\MyProject.csproj", + configuredProjects: configuredProjects, + unconfiguredProjectServices: UnconfiguredProjectServicesFactory.Create( + threadingService: threadingService, + projectService: IProjectServiceFactory.Create( + services: ProjectServicesFactory.Create( + threadingService: threadingService)), + projectConfigurationsService: IProjectConfigurationsServiceFactory.ImplementGetKnownProjectConfigurationsAsync(projectConfigurations))); + return project; + } - [Fact] - public async Task WhenNoDimensionsAreGiven_ThenThePropertyIsSetInAllConfigurations() + [Fact] + public async Task WhenNoDimensionsAreGiven_ThenThePropertyIsSetInAllConfigurations() + { + var affectedConfigs = new List(); + var projectConfigurations = GetConfigurations(); + var configuredProjects = projectConfigurations.Select(config => CreateConfiguredProject(config, (p, v) => affectedConfigs.Add(config.Name))); + var project = CreateUnconfiguredProject(projectConfigurations, configuredProjects); + + var emptyTargetDimensions = Enumerable.Empty<(string dimension, string value)>(); + + var coreActionExecutor = new ProjectSetUIPropertyValueActionCore( + pageName: "MyPage", + propertyName: "MyProperty", + emptyTargetDimensions, + prop => prop.SetValueAsync("new value")); + + await coreActionExecutor.OnBeforeExecutingBatchAsync(new[] { project }); + bool propertyUpdated = await coreActionExecutor.ExecuteAsync(project); + coreActionExecutor.OnAfterExecutingBatch(); + + Assert.True(propertyUpdated); + Assert.Equal(expected: 4, actual: affectedConfigs.Count); + foreach (var configuration in projectConfigurations) { - var affectedConfigs = new List(); - var projectConfigurations = GetConfigurations(); - var configuredProjects = projectConfigurations.Select(config => CreateConfiguredProject(config, (p, v) => affectedConfigs.Add(config.Name))); - var project = CreateUnconfiguredProject(projectConfigurations, configuredProjects); - - var emptyTargetDimensions = Enumerable.Empty<(string dimension, string value)>(); - - var coreActionExecutor = new ProjectSetUIPropertyValueActionCore( - pageName: "MyPage", - propertyName: "MyProperty", - emptyTargetDimensions, - prop => prop.SetValueAsync("new value")); - - await coreActionExecutor.OnBeforeExecutingBatchAsync(new[] { project }); - bool propertyUpdated = await coreActionExecutor.ExecuteAsync(project); - coreActionExecutor.OnAfterExecutingBatch(); - - Assert.True(propertyUpdated); - Assert.Equal(expected: 4, actual: affectedConfigs.Count); - foreach (var configuration in projectConfigurations) - { - Assert.Contains(configuration.Name, affectedConfigs); - } + Assert.Contains(configuration.Name, affectedConfigs); } + } + + [Fact] + public async Task WhenDimensionsAreGiven_ThenThePropertyIsOnlySetInTheMatchingConfigurations() + { + var affectedConfigs = new List(); + var projectConfigurations = GetConfigurations(); + var configuredProjects = projectConfigurations.Select(config => CreateConfiguredProject(config, (p, v) => affectedConfigs.Add(config.Name))); + var unconfiguredProject = CreateUnconfiguredProject(projectConfigurations, configuredProjects); - [Fact] - public async Task WhenDimensionsAreGiven_ThenThePropertyIsOnlySetInTheMatchingConfigurations() + var targetDimensions = new List<(string dimension, string value)> { - var affectedConfigs = new List(); - var projectConfigurations = GetConfigurations(); - var configuredProjects = projectConfigurations.Select(config => CreateConfiguredProject(config, (p, v) => affectedConfigs.Add(config.Name))); - var unconfiguredProject = CreateUnconfiguredProject(projectConfigurations, configuredProjects); - - var targetDimensions = new List<(string dimension, string value)> - { - ("Configuration", "Release"), - ("Platform", "x86") - }; - - var coreActionExecutor = new ProjectSetUIPropertyValueActionCore( - pageName: "MyPage", - propertyName: "MyProperty", - targetDimensions, - prop => prop.SetValueAsync("new value")); - - await coreActionExecutor.OnBeforeExecutingBatchAsync(new[] { unconfiguredProject }); - bool propertyUpdated = await coreActionExecutor.ExecuteAsync(unconfiguredProject); - coreActionExecutor.OnAfterExecutingBatch(); - - Assert.True(propertyUpdated); - Assert.Single(affectedConfigs); - Assert.Contains("Release|x86", affectedConfigs); - } + ("Configuration", "Release"), + ("Platform", "x86") + }; + + var coreActionExecutor = new ProjectSetUIPropertyValueActionCore( + pageName: "MyPage", + propertyName: "MyProperty", + targetDimensions, + prop => prop.SetValueAsync("new value")); + + await coreActionExecutor.OnBeforeExecutingBatchAsync(new[] { unconfiguredProject }); + bool propertyUpdated = await coreActionExecutor.ExecuteAsync(unconfiguredProject); + coreActionExecutor.OnAfterExecutingBatch(); + + Assert.True(propertyUpdated); + Assert.Single(affectedConfigs); + Assert.Contains("Release|x86", affectedConfigs); + } - + - [Fact] - public async Task WhenARuleContainsMultipleProperties_ThenOnlyTheSpecifiedPropertyIsSet() - { - var affectedProperties = new List(); - var projectConfigurations = GetConfigurations(); - var configuredProjects = projectConfigurations.Select(config => CreateConfiguredProject(config, (p, v) => affectedProperties.Add(p))); - var project = CreateUnconfiguredProject(projectConfigurations, configuredProjects); - - var targetDimensions = new List<(string dimension, string value)> - { - ("Configuration", "Release"), - ("Platform", "x86") - }; - - var coreActionExecutor = new ProjectSetUIPropertyValueActionCore( - pageName: "MyPage", - propertyName: "MyProperty", - targetDimensions, - prop => prop.SetValueAsync("new value")); - - await coreActionExecutor.OnBeforeExecutingBatchAsync(new[] { project }); - bool propertyUpdated = await coreActionExecutor.ExecuteAsync(project); - coreActionExecutor.OnAfterExecutingBatch(); - - Assert.True(propertyUpdated); - Assert.Single(affectedProperties); - Assert.Contains("MyProperty", affectedProperties); - } + [Fact] + public async Task WhenARuleContainsMultipleProperties_ThenOnlyTheSpecifiedPropertyIsSet() + { + var affectedProperties = new List(); + var projectConfigurations = GetConfigurations(); + var configuredProjects = projectConfigurations.Select(config => CreateConfiguredProject(config, (p, v) => affectedProperties.Add(p))); + var project = CreateUnconfiguredProject(projectConfigurations, configuredProjects); - /// - /// Returns a set of s for testing purposes. - /// - private static ImmutableHashSet GetConfigurations() + var targetDimensions = new List<(string dimension, string value)> { - return ImmutableHashSet.Empty - .Add(ProjectConfigurationFactory.Create("Debug|x86")) - .Add(ProjectConfigurationFactory.Create("Debug|x64")) - .Add(ProjectConfigurationFactory.Create("Release|x86")) - .Add(ProjectConfigurationFactory.Create("Release|x64")); - } + ("Configuration", "Release"), + ("Platform", "x86") + }; + + var coreActionExecutor = new ProjectSetUIPropertyValueActionCore( + pageName: "MyPage", + propertyName: "MyProperty", + targetDimensions, + prop => prop.SetValueAsync("new value")); + + await coreActionExecutor.OnBeforeExecutingBatchAsync(new[] { project }); + bool propertyUpdated = await coreActionExecutor.ExecuteAsync(project); + coreActionExecutor.OnAfterExecutingBatch(); + + Assert.True(propertyUpdated); + Assert.Single(affectedProperties); + Assert.Contains("MyProperty", affectedProperties); + } + + /// + /// Returns a set of s for testing purposes. + /// + private static ImmutableHashSet GetConfigurations() + { + return ImmutableHashSet.Empty + .Add(ProjectConfigurationFactory.Create("Debug|x86")) + .Add(ProjectConfigurationFactory.Create("Debug|x64")) + .Add(ProjectConfigurationFactory.Create("Release|x86")) + .Add(ProjectConfigurationFactory.Create("Release|x64")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducerTests.cs index ec1c5cadad..f2440086b2 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/PropertyPageDataProducerTests.cs @@ -3,59 +3,58 @@ using Microsoft.Build.Framework.XamlTypes; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class PropertyPageDataProducerTests { - public class PropertyPageDataProducerTests + [Fact] + public void WhenPropertyValuesAreRequested_PropertyValuesAreReturned() + { + var properties = PropertiesAvailableStatusFactory.CreatePropertyPagePropertiesAvailableStatus(includeAllProperties: true); + + var propertyPage = (PropertyPageSnapshot)PropertyPageDataProducer.CreatePropertyPageValue( + IQueryExecutionContextFactory.Create(), + IEntityWithIdFactory.Create(key: "A", value: "B"), + IProjectStateFactory.Create(), + QueryProjectPropertiesContext.ProjectFile, + new Rule { Name = "MyRule", DisplayName = "My Rule Display Name", Order = 42, PageTemplate = "generic" }, + requestedProperties: properties); + + Assert.Equal(expected: "MyRule", actual: propertyPage.Name); + Assert.Equal(expected: "My Rule Display Name", actual: propertyPage.DisplayName); + Assert.Equal(expected: 42, actual: propertyPage.Order); + Assert.Equal(expected: "generic", actual: propertyPage.Kind); + } + + [Fact] + public void WhenCreatingAModel_ProviderStateIsTheCorrectType() { - [Fact] - public void WhenPropertyValuesAreRequested_PropertyValuesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreatePropertyPagePropertiesAvailableStatus(includeAllProperties: true); - - var propertyPage = (PropertyPageSnapshot)PropertyPageDataProducer.CreatePropertyPageValue( - IQueryExecutionContextFactory.Create(), - IEntityWithIdFactory.Create(key: "A", value: "B"), - IProjectStateFactory.Create(), - QueryProjectPropertiesContext.ProjectFile, - new Rule { Name = "MyRule", DisplayName = "My Rule Display Name", Order = 42, PageTemplate = "generic" }, - requestedProperties: properties); - - Assert.Equal(expected: "MyRule", actual: propertyPage.Name); - Assert.Equal(expected: "My Rule Display Name", actual: propertyPage.DisplayName); - Assert.Equal(expected: 42, actual: propertyPage.Order); - Assert.Equal(expected: "generic", actual: propertyPage.Kind); - } - - [Fact] - public void WhenCreatingAModel_ProviderStateIsTheCorrectType() - { - var properties = PropertiesAvailableStatusFactory.CreatePropertyPagePropertiesAvailableStatus(includeAllProperties: true); - - var propertyPage = (IEntityValueFromProvider)PropertyPageDataProducer.CreatePropertyPageValue( - IQueryExecutionContextFactory.Create(), - IEntityWithIdFactory.Create(key: "A", value: "B"), - IProjectStateFactory.Create(), - QueryProjectPropertiesContext.ProjectFile, - new Rule { Name = "MyRule", DisplayName = "My Rule Display Name", Order = 42, PageTemplate = "generic" }, - requestedProperties: properties); - - Assert.IsType(propertyPage.ProviderState); - } - - [Fact] - public void WhenCreatingFromAParentAndRule_TheRuleNameIsTheEntityId() - { - var properties = PropertiesAvailableStatusFactory.CreatePropertyPagePropertiesAvailableStatus(includeAllProperties: true); - - var propertyPage = (PropertyPageSnapshot)PropertyPageDataProducer.CreatePropertyPageValue( - IQueryExecutionContextFactory.Create(), - IEntityWithIdFactory.Create(key: "ParentKey", value: "ParentValue"), - IProjectStateFactory.Create(), - QueryProjectPropertiesContext.ProjectFile, - new Rule { Name = "MyRule", DisplayName = "My Rule Display Name", Order = 42, PageTemplate = "generic" }, - requestedProperties: properties); - - Assert.Equal(expected: "MyRule", actual: propertyPage.Id[ProjectModelIdentityKeys.PropertyPageName]); - } + var properties = PropertiesAvailableStatusFactory.CreatePropertyPagePropertiesAvailableStatus(includeAllProperties: true); + + var propertyPage = (IEntityValueFromProvider)PropertyPageDataProducer.CreatePropertyPageValue( + IQueryExecutionContextFactory.Create(), + IEntityWithIdFactory.Create(key: "A", value: "B"), + IProjectStateFactory.Create(), + QueryProjectPropertiesContext.ProjectFile, + new Rule { Name = "MyRule", DisplayName = "My Rule Display Name", Order = 42, PageTemplate = "generic" }, + requestedProperties: properties); + + Assert.IsType(propertyPage.ProviderState); + } + + [Fact] + public void WhenCreatingFromAParentAndRule_TheRuleNameIsTheEntityId() + { + var properties = PropertiesAvailableStatusFactory.CreatePropertyPagePropertiesAvailableStatus(includeAllProperties: true); + + var propertyPage = (PropertyPageSnapshot)PropertyPageDataProducer.CreatePropertyPageValue( + IQueryExecutionContextFactory.Create(), + IEntityWithIdFactory.Create(key: "ParentKey", value: "ParentValue"), + IProjectStateFactory.Create(), + QueryProjectPropertiesContext.ProjectFile, + new Rule { Name = "MyRule", DisplayName = "My Rule Display Name", Order = 42, PageTemplate = "generic" }, + requestedProperties: properties); + + Assert.Equal(expected: "MyRule", actual: propertyPage.Id[ProjectModelIdentityKeys.PropertyPageName]); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducerTests.cs index 42296e85bf..45583dbc95 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/SupportedValueDataProducerTests.cs @@ -3,52 +3,51 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class SupportedValueDataProducerTests { - public class SupportedValueDataProducerTests + [Fact] + public void WhenPropertiesAreRequested_PropertyValuesAreReturned() { - [Fact] - public void WhenPropertiesAreRequested_PropertyValuesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateSupportedValuesPropertiesAvailableStatus(includeAllProperties: true); + var properties = PropertiesAvailableStatusFactory.CreateSupportedValuesPropertiesAvailableStatus(includeAllProperties: true); - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var enumValue = IEnumValueFactory.Create(displayName: "Hello", name: "MyValue"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var enumValue = IEnumValueFactory.Create(displayName: "Hello", name: "MyValue"); - var result = (SupportedValueSnapshot)SupportedValueDataProducer.CreateSupportedValue(entityRuntime, enumValue, properties); + var result = (SupportedValueSnapshot)SupportedValueDataProducer.CreateSupportedValue(entityRuntime, enumValue, properties); - Assert.Equal(expected: "Hello", actual: result.DisplayName); - Assert.Equal(expected: "MyValue", actual: result.Value); - } + Assert.Equal(expected: "Hello", actual: result.DisplayName); + Assert.Equal(expected: "MyValue", actual: result.Value); + } + + [Fact] + public async Task WhenCreatingValuesFromAnIProperty_WeGetOneValuePerIEnumValue() + { + var properties = PropertiesAvailableStatusFactory.CreateSupportedValuesPropertiesAvailableStatus(includeAllProperties: true); + + var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); + var iproperty = IPropertyFactory.CreateEnum(new[] + { + IEnumValueFactory.Create(displayName: "Alpha", name: "a"), + IEnumValueFactory.Create(displayName: "Beta", name: "b"), + IEnumValueFactory.Create(displayName: "Gamma", name: "c") + }); + + var result = await SupportedValueDataProducer.CreateSupportedValuesAsync(parentEntity, iproperty, properties); + + Assert.Collection(result, new Action[] + { + entity => assertEqual(entity, expectedDisplayName: "Alpha", expectedValue: "a"), + entity => assertEqual(entity, expectedDisplayName: "Beta", expectedValue: "b"), + entity => assertEqual(entity, expectedDisplayName: "Gamma", expectedValue: "c") + }); - [Fact] - public async Task WhenCreatingValuesFromAnIProperty_WeGetOneValuePerIEnumValue() + static void assertEqual(IEntityValue entity, string expectedDisplayName, string expectedValue) { - var properties = PropertiesAvailableStatusFactory.CreateSupportedValuesPropertiesAvailableStatus(includeAllProperties: true); - - var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); - var iproperty = IPropertyFactory.CreateEnum(new[] - { - IEnumValueFactory.Create(displayName: "Alpha", name: "a"), - IEnumValueFactory.Create(displayName: "Beta", name: "b"), - IEnumValueFactory.Create(displayName: "Gamma", name: "c") - }); - - var result = await SupportedValueDataProducer.CreateSupportedValuesAsync(parentEntity, iproperty, properties); - - Assert.Collection(result, new Action[] - { - entity => assertEqual(entity, expectedDisplayName: "Alpha", expectedValue: "a"), - entity => assertEqual(entity, expectedDisplayName: "Beta", expectedValue: "b"), - entity => assertEqual(entity, expectedDisplayName: "Gamma", expectedValue: "c") - }); - - static void assertEqual(IEntityValue entity, string expectedDisplayName, string expectedValue) - { - var supportedValueEntity = (SupportedValueSnapshot)entity; - Assert.Equal(expectedDisplayName, supportedValueEntity.DisplayName); - Assert.Equal(expectedValue, supportedValueEntity.Value); - } + var supportedValueEntity = (SupportedValueSnapshot)entity; + Assert.Equal(expectedDisplayName, supportedValueEntity.DisplayName); + Assert.Equal(expectedValue, supportedValueEntity.Value); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducerTests.cs index b2eb79e5ea..79f4cdaed5 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIEditorMetadataProducerTests.cs @@ -4,64 +4,63 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class UIEditorMetadataProducerTests { - public class UIEditorMetadataProducerTests + [Fact] + public void WhenPropertiesAreRequested_PropertyValuesAreReturned() { - [Fact] - public void WhenPropertiesAreRequested_PropertyValuesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIEditorMetadataAvailableStatus( - includeName: true, - includeValue: true); + var properties = PropertiesAvailableStatusFactory.CreateUIEditorMetadataAvailableStatus( + includeName: true, + includeValue: true); - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var metadata = new NameValuePair { Name = "Alpha", Value = "AlphaValue" }; - - var result = (UIEditorMetadataSnapshot)UIEditorMetadataProducer.CreateMetadataValue(entityRuntime, metadata, properties); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var metadata = new NameValuePair { Name = "Alpha", Value = "AlphaValue" }; + + var result = (UIEditorMetadataSnapshot)UIEditorMetadataProducer.CreateMetadataValue(entityRuntime, metadata, properties); - Assert.Equal(expected: "Alpha", actual: result.Name); - Assert.Equal(expected: "AlphaValue", actual: result.Value); - } + Assert.Equal(expected: "Alpha", actual: result.Name); + Assert.Equal(expected: "AlphaValue", actual: result.Value); + } - [Fact] - public void WhenPropertiesAreNotRequested_PropertyValuesAreNotReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIEditorMetadataAvailableStatus(includeAllProperties: false); + [Fact] + public void WhenPropertiesAreNotRequested_PropertyValuesAreNotReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIEditorMetadataAvailableStatus(includeAllProperties: false); - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var metadata = new NameValuePair { Name = "Alpha", Value = "AlphaValue" }; + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var metadata = new NameValuePair { Name = "Alpha", Value = "AlphaValue" }; - var result = (UIEditorMetadataSnapshot)UIEditorMetadataProducer.CreateMetadataValue(entityRuntime, metadata, properties); + var result = (UIEditorMetadataSnapshot)UIEditorMetadataProducer.CreateMetadataValue(entityRuntime, metadata, properties); - Assert.Throws(() => result.Name); - Assert.Throws(() => result.Value); - } + Assert.Throws(() => result.Name); + Assert.Throws(() => result.Value); + } - [Fact] - public void WhenCreatingMetadataFromAnEditor_AllMetadataIsReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIEditorMetadataAvailableStatus( - includeName: true, - includeValue: true); + [Fact] + public void WhenCreatingMetadataFromAnEditor_AllMetadataIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIEditorMetadataAvailableStatus( + includeName: true, + includeValue: true); - var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); - var editor = new ValueEditor - { - Metadata = - { - new() { Name = "Alpha", Value = "A" }, - new() { Name = "Beta", Value = "B" } - } - }; + var parentEntity = IEntityWithIdFactory.Create("ParentKey", "ParentKeyValue"); + var editor = new ValueEditor + { + Metadata = + { + new() { Name = "Alpha", Value = "A" }, + new() { Name = "Beta", Value = "B" } + } + }; - var results = UIEditorMetadataProducer.CreateMetadataValues( - parentEntity, - editor, - properties); + var results = UIEditorMetadataProducer.CreateMetadataValues( + parentEntity, + editor, + properties); - Assert.Contains(results, entity => entity is UIEditorMetadataSnapshot { Name: "Alpha", Value: "A" }); - Assert.Contains(results, entity => entity is UIEditorMetadataSnapshot { Name: "Beta", Value: "B" }); - } + Assert.Contains(results, entity => entity is UIEditorMetadataSnapshot { Name: "Alpha", Value: "A" }); + Assert.Contains(results, entity => entity is UIEditorMetadataSnapshot { Name: "Beta", Value: "B" }); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducerTests.cs index 80c3c98140..436f8e43a3 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyDataProducerTests.cs @@ -4,297 +4,296 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class UIPropertyDataProducerTests { - public class UIPropertyDataProducerTests + [Fact] + public void WhenCreatingFromAParentAndProperty_ThePropertyNameIsTheEntityId() { - [Fact] - public void WhenCreatingFromAParentAndProperty_ThePropertyNameIsTheEntityId() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(); + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(); - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "parent", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty { Name = "MyProperty" }; - var order = 42; - InitializeFakeRuleForProperty(property); + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "parent", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { Name = "MyProperty" }; + var order = 42; + InitializeFakeRuleForProperty(property); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, parentEntity, cache, QueryProjectPropertiesContext.ProjectFile, property, order, properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, parentEntity, cache, QueryProjectPropertiesContext.ProjectFile, property, order, properties); - Assert.Equal(expected: "MyProperty", actual: result.Id[ProjectModelIdentityKeys.UIPropertyName]); - } + Assert.Equal(expected: "MyProperty", actual: result.Id[ProjectModelIdentityKeys.UIPropertyName]); + } + + [Fact] + public void WhenPropertiesAreRequested_PropertyValuesAreReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeAllProperties: true); - [Fact] - public void WhenPropertiesAreRequested_PropertyValuesAreReturned() + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeAllProperties: true); + Name = "A", + DisplayName = "Page A", + Description = "This is the description for Page A", + HelpUrl = "https://mypage", + Category = "general", + Visible = false, + DataSource = new DataSource { HasConfigurationCondition = false } + }; + InitializeFakeRuleForProperty(property); + + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + + Assert.Equal(expected: "A", actual: result.Name); + Assert.Equal(expected: "Page A", actual: result.DisplayName); + Assert.Equal(expected: "This is the description for Page A", actual: result.Description); + Assert.True(result.ConfigurationIndependent); + Assert.Equal(expected: "general", actual: result.CategoryName); + Assert.False(result.IsVisible); + Assert.Equal(expected: 42, actual: result.Order); + Assert.Equal(expected: "string", actual: result.Type); + } - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty - { - Name = "A", - DisplayName = "Page A", - Description = "This is the description for Page A", - HelpUrl = "https://mypage", - Category = "general", - Visible = false, - DataSource = new DataSource { HasConfigurationCondition = false } - }; - InitializeFakeRuleForProperty(property); - - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - - Assert.Equal(expected: "A", actual: result.Name); - Assert.Equal(expected: "Page A", actual: result.DisplayName); - Assert.Equal(expected: "This is the description for Page A", actual: result.Description); - Assert.True(result.ConfigurationIndependent); - Assert.Equal(expected: "general", actual: result.CategoryName); - Assert.False(result.IsVisible); - Assert.Equal(expected: 42, actual: result.Order); - Assert.Equal(expected: "string", actual: result.Type); - } + [Fact] + public void WhenTheEntityIsCreated_TheProviderStateIsTheExpectedType() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(); - [Fact] - public void WhenTheEntityIsCreated_TheProviderStateIsTheExpectedType() + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(); + Name = "A" + }; + var rule = new Rule(); + rule.BeginInit(); + rule.Properties.Add(property); + rule.EndInit(); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty - { - Name = "A" - }; - var rule = new Rule(); - rule.BeginInit(); - rule.Properties.Add(property); - rule.EndInit(); - - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.IsType(((IEntityValueFromProvider)result).ProviderState); - } + Assert.IsType(((IEntityValueFromProvider)result).ProviderState); + } - [Fact] - public void WhenCreatingPropertiesFromARule_OneEntityIsCreatedPerProperty() + [Fact] + public void WhenCreatingPropertiesFromARule_OneEntityIsCreatedPerProperty() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeAllProperties: true); + + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "ParentRule"); + var cache = IProjectStateFactory.Create(); + var rule = new Rule(); + rule.BeginInit(); + rule.Properties.AddRange(new[] { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeAllProperties: true); - - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "Parent", value: "ParentRule"); - var cache = IProjectStateFactory.Create(); - var rule = new Rule(); - rule.BeginInit(); - rule.Properties.AddRange(new[] - { - new TestProperty { Name = "Alpha" }, - new TestProperty { Name = "Beta" }, - new TestProperty { Name = "Gamma" }, - }); - rule.EndInit(); + new TestProperty { Name = "Alpha" }, + new TestProperty { Name = "Beta" }, + new TestProperty { Name = "Gamma" }, + }); + rule.EndInit(); - var result = UIPropertyDataProducer.CreateUIPropertyValues(context, parentEntity, cache, QueryProjectPropertiesContext.ProjectFile, rule, properties); + var result = UIPropertyDataProducer.CreateUIPropertyValues(context, parentEntity, cache, QueryProjectPropertiesContext.ProjectFile, rule, properties); - Assert.Collection(result, new Action[] - { - entity => { assertEqual(entity, expectedName: "Alpha"); }, - entity => { assertEqual(entity, expectedName: "Beta"); }, - entity => { assertEqual(entity, expectedName: "Gamma"); } - }); + Assert.Collection(result, new Action[] + { + entity => { assertEqual(entity, expectedName: "Alpha"); }, + entity => { assertEqual(entity, expectedName: "Beta"); }, + entity => { assertEqual(entity, expectedName: "Gamma"); } + }); - static void assertEqual(IEntityValue entity, string expectedName) - { - var propertyEntity = (UIPropertySnapshot)entity; - Assert.Equal(expectedName, propertyEntity.Name); - } + static void assertEqual(IEntityValue entity, string expectedName) + { + var propertyEntity = (UIPropertySnapshot)entity; + Assert.Equal(expectedName, propertyEntity.Name); } + } + + [Fact] + public void WhenAPropertyHasNoSearchTerms_AnEmptyStringIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeSearchTerms: true); - [Fact] - public void WhenAPropertyHasNoSearchTerms_AnEmptyStringIsReturned() + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeSearchTerms: true); + Metadata = new List() + }; - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty - { - Metadata = new List() - }; + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + Assert.Equal(expected: "", actual: result.SearchTerms); + } - Assert.Equal(expected: "", actual: result.SearchTerms); - } + [Fact] + public void WhenAPropertyHasAnEmptyStringOfSearchTerms_AnEmptyStringIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeSearchTerms: true); - [Fact] - public void WhenAPropertyHasAnEmptyStringOfSearchTerms_AnEmptyStringIsReturned() + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeSearchTerms: true); - - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty + Metadata = new() { - Metadata = new() - { - new() { Name = "SearchTerms", Value = "" } - } - }; + new() { Name = "SearchTerms", Value = "" } + } + }; - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.Equal(expected: "", actual: result.SearchTerms); - } + Assert.Equal(expected: "", actual: result.SearchTerms); + } - [Fact] - public void WhenAPropertyHasSearchTerms_ThenTheSearchTermsAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeSearchTerms: true); + [Fact] + public void WhenAPropertyHasSearchTerms_ThenTheSearchTermsAreReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeSearchTerms: true); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty + { + Metadata = new() { - Metadata = new() - { - new() { Name = "SearchTerms", Value = "Alpha;Beta;Gamma" } - } - }; + new() { Name = "SearchTerms", Value = "Alpha;Beta;Gamma" } + } + }; - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.Equal(expected: "Alpha;Beta;Gamma", actual: result.SearchTerms); - } + Assert.Equal(expected: "Alpha;Beta;Gamma", actual: result.SearchTerms); + } + + [Fact] + public void WhenAPropertyHasNoDependencies_AnEmptyStringIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeDependsOn: true); - [Fact] - public void WhenAPropertyHasNoDependencies_AnEmptyStringIsReturned() + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeDependsOn: true); + Metadata = new List() + }; + InitializeFakeRuleForProperty(property); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty - { - Metadata = new List() - }; - InitializeFakeRuleForProperty(property); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + Assert.Equal(expected: "", actual: result.DependsOn); + } - Assert.Equal(expected: "", actual: result.DependsOn); - } + [Fact] + public void WhenAPropertyHasAnEmptyStringOfDependencies_AnEmptyStringIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeDependsOn: true); - [Fact] - public void WhenAPropertyHasAnEmptyStringOfDependencies_AnEmptyStringIsReturned() + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeDependsOn: true); - - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty + Metadata = new() { - Metadata = new() - { - new() { Name = "DependsOn", Value = "" } - } - }; - InitializeFakeRuleForProperty(property); + new() { Name = "DependsOn", Value = "" } + } + }; + InitializeFakeRuleForProperty(property); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.Equal(expected: "", actual: result.DependsOn); - } + Assert.Equal(expected: "", actual: result.DependsOn); + } - [Fact] - public void WhenAPropertyHasDependencies_TheDependenciesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeDependsOn: true); + [Fact] + public void WhenAPropertyHasDependencies_TheDependenciesAreReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeDependsOn: true); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty + { + Metadata = new() { - Metadata = new() - { - new() { Name = "DependsOn", Value = "Alpha;Beta;Gamma" } - } - }; - InitializeFakeRuleForProperty(property); + new() { Name = "DependsOn", Value = "Alpha;Beta;Gamma" } + } + }; + InitializeFakeRuleForProperty(property); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.Equal(expected: "Alpha;Beta;Gamma", actual: result.DependsOn); - } + Assert.Equal(expected: "Alpha;Beta;Gamma", actual: result.DependsOn); + } - [Fact] - public void WhenAPropertyHasNoVisibilityCondition_AnEmptyStringIsReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeVisibilityCondition: true); + [Fact] + public void WhenAPropertyHasNoVisibilityCondition_AnEmptyStringIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeVisibilityCondition: true); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); - var property = new TestProperty - { - Metadata = new List() - }; - InitializeFakeRuleForProperty(property); + var property = new TestProperty + { + Metadata = new List() + }; + InitializeFakeRuleForProperty(property); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.Equal(expected: string.Empty, actual: result.VisibilityCondition); - } + Assert.Equal(expected: string.Empty, actual: result.VisibilityCondition); + } - + - [Fact] - public void WhenAPropertyHasAVisibilityCondition_ItIsReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeVisibilityCondition: true); + [Fact] + public void WhenAPropertyHasAVisibilityCondition_ItIsReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyPropertiesAvailableStatus(includeVisibilityCondition: true); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "PropertyName", value: "A"); - var cache = IProjectStateFactory.Create(); - var property = new TestProperty + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "PropertyName", value: "A"); + var cache = IProjectStateFactory.Create(); + var property = new TestProperty + { + Metadata = new() { - Metadata = new() - { - new() { Name = "VisibilityCondition", Value = "true or false"} - } - }; - InitializeFakeRuleForProperty(property); + new() { Name = "VisibilityCondition", Value = "true or false"} + } + }; + InitializeFakeRuleForProperty(property); - var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); + var result = (UIPropertySnapshot)UIPropertyDataProducer.CreateUIPropertyValue(context, id, cache, QueryProjectPropertiesContext.ProjectFile, property, order: 42, requestedProperties: properties); - Assert.Equal(expected: "true or false", actual: result.VisibilityCondition); - } + Assert.Equal(expected: "true or false", actual: result.VisibilityCondition); + } - /// - /// The only way to set the for a property - /// is to actually create a rule, add the property to the rule, and go through the - /// initialization for the rule. - /// - private static void InitializeFakeRuleForProperty(TestProperty property) - { - var rule = new Rule(); - rule.BeginInit(); - rule.Properties.Add(property); - rule.EndInit(); - } + /// + /// The only way to set the for a property + /// is to actually create a rule, add the property to the rule, and go through the + /// initialization for the rule. + /// + private static void InitializeFakeRuleForProperty(TestProperty property) + { + var rule = new Rule(); + rule.BeginInit(); + rule.Properties.Add(property); + rule.EndInit(); + } - private class TestProperty : BaseProperty - { - } + private class TestProperty : BaseProperty + { } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducerTests.cs index 32d878551f..e99f792753 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyEditorDataProducerTests.cs @@ -4,148 +4,147 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class UIPropertyEditorDataProducerTests { - public class UIPropertyEditorDataProducerTests + [Fact] + public void WhenCreatingAnEntityFromAParentAndEditor_TheIdIsTheEditorType() { - [Fact] - public void WhenCreatingAnEntityFromAParentAndEditor_TheIdIsTheEditorType() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: false); + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: false); - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "parentId", value: "aaa"); - var editor = new ValueEditor { EditorType = "My.Editor.Type" }; + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "parentId", value: "aaa"); + var editor = new ValueEditor { EditorType = "My.Editor.Type" }; - var result = (UIPropertyEditorSnapshot)UIPropertyEditorDataProducer.CreateEditorValue(context, parentEntity, editor, properties); + var result = (UIPropertyEditorSnapshot)UIPropertyEditorDataProducer.CreateEditorValue(context, parentEntity, editor, properties); - Assert.Equal(expected: "My.Editor.Type", actual: result.Id[ProjectModelIdentityKeys.EditorName]); - } + Assert.Equal(expected: "My.Editor.Type", actual: result.Id[ProjectModelIdentityKeys.EditorName]); + } - [Fact] - public void WhenPropertiesAreRequested_PropertyValuesAreReturned() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); + [Fact] + public void WhenPropertiesAreRequested_PropertyValuesAreReturned() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "EditorKey", value: "bbb"); - var editor = new ValueEditor { EditorType = "My.Editor.Type" }; + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "EditorKey", value: "bbb"); + var editor = new ValueEditor { EditorType = "My.Editor.Type" }; - var result = (UIPropertyEditorSnapshot)UIPropertyEditorDataProducer.CreateEditorValue(context, id, editor, properties); + var result = (UIPropertyEditorSnapshot)UIPropertyEditorDataProducer.CreateEditorValue(context, id, editor, properties); - Assert.Equal(expected: "My.Editor.Type", actual: result.Name); - } + Assert.Equal(expected: "My.Editor.Type", actual: result.Name); + } - [Fact] - public void WhenAnEditorValueIsCreated_TheEditorIsTheProviderState() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); + [Fact] + public void WhenAnEditorValueIsCreated_TheEditorIsTheProviderState() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); - var context = IQueryExecutionContextFactory.Create(); - var id = new EntityIdentity(key: "EditorKey", value: "bbb"); - var editor = new ValueEditor { EditorType = "My.Editor.Type" }; + var context = IQueryExecutionContextFactory.Create(); + var id = new EntityIdentity(key: "EditorKey", value: "bbb"); + var editor = new ValueEditor { EditorType = "My.Editor.Type" }; - var result = (UIPropertyEditorSnapshot)UIPropertyEditorDataProducer.CreateEditorValue(context, id, editor, properties); + var result = (UIPropertyEditorSnapshot)UIPropertyEditorDataProducer.CreateEditorValue(context, id, editor, properties); - Assert.Equal(expected: editor, actual: ((IEntityValueFromProvider)result).ProviderState); - } + Assert.Equal(expected: editor, actual: ((IEntityValueFromProvider)result).ProviderState); + } - [Fact] - public void WhenCreatingEditorsFromAProperty_OneEntityIsReturnedPerEditor() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); - - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "parentKey", value: "parentId"); - var rule = new Rule(); - rule.BeginInit(); - rule.Properties.Add( - new TestProperty - { - Name = "MyProperty", - ValueEditors = - { - new ValueEditor { EditorType = "Alpha" }, - new ValueEditor { EditorType = "Beta" }, - new ValueEditor { EditorType = "Gamma" } - } - }); - rule.EndInit(); - - var results = UIPropertyEditorDataProducer.CreateEditorValues(context, parentEntity, rule, "MyProperty", properties); - - Assert.Collection(results, new Action[] + [Fact] + public void WhenCreatingEditorsFromAProperty_OneEntityIsReturnedPerEditor() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); + + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "parentKey", value: "parentId"); + var rule = new Rule(); + rule.BeginInit(); + rule.Properties.Add( + new TestProperty { - entity => assertEqual(entity, expectedName: "Alpha"), - entity => assertEqual(entity, expectedName: "Beta"), - entity => assertEqual(entity, expectedName: "Gamma") + Name = "MyProperty", + ValueEditors = + { + new ValueEditor { EditorType = "Alpha" }, + new ValueEditor { EditorType = "Beta" }, + new ValueEditor { EditorType = "Gamma" } + } }); + rule.EndInit(); - static void assertEqual(IEntityValue entity, string expectedName) - { - var editorEntity = (UIPropertyEditorSnapshot)entity; - Assert.Equal(expectedName, editorEntity.Name); - } - } + var results = UIPropertyEditorDataProducer.CreateEditorValues(context, parentEntity, rule, "MyProperty", properties); - [Fact] - public void WhenCreatingEditorsFromAStringPropertyWithFileSubtype_FileBrowseEditorIsCreated() + Assert.Collection(results, new Action[] { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); - - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "parentKey", value: "parentId"); - var rule = new Rule(); - rule.BeginInit(); - rule.Properties.Add( - new StringProperty - { - Name = "MyProperty", - Subtype = "file" - }); - rule.EndInit(); - - var results = UIPropertyEditorDataProducer.CreateEditorValues(context, parentEntity, rule, "MyProperty", properties); + entity => assertEqual(entity, expectedName: "Alpha"), + entity => assertEqual(entity, expectedName: "Beta"), + entity => assertEqual(entity, expectedName: "Gamma") + }); - Assert.Collection(results, entity => assertEqual(entity, expectedName: "FilePath")); + static void assertEqual(IEntityValue entity, string expectedName) + { + var editorEntity = (UIPropertyEditorSnapshot)entity; + Assert.Equal(expectedName, editorEntity.Name); + } + } - static void assertEqual(IEntityValue entity, string expectedName) + [Fact] + public void WhenCreatingEditorsFromAStringPropertyWithFileSubtype_FileBrowseEditorIsCreated() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); + + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "parentKey", value: "parentId"); + var rule = new Rule(); + rule.BeginInit(); + rule.Properties.Add( + new StringProperty { - var editorEntity = (UIPropertyEditorSnapshot)entity; - Assert.Equal(expectedName, editorEntity.Name); - } - } + Name = "MyProperty", + Subtype = "file" + }); + rule.EndInit(); - [Fact] - public void WhenCreatingEditorsFromAStringPropertyWithPathSubtype_PathBrowseEditorIsCreated() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); - - var context = IQueryExecutionContextFactory.Create(); - var parentEntity = IEntityWithIdFactory.Create(key: "parentKey", value: "parentId"); - var rule = new Rule(); - rule.BeginInit(); - rule.Properties.Add( - new StringProperty - { - Name = "MyProperty", - Subtype = "directory" - }); - rule.EndInit(); + var results = UIPropertyEditorDataProducer.CreateEditorValues(context, parentEntity, rule, "MyProperty", properties); - var results = UIPropertyEditorDataProducer.CreateEditorValues(context, parentEntity, rule, "MyProperty", properties); + Assert.Collection(results, entity => assertEqual(entity, expectedName: "FilePath")); - Assert.Collection(results, entity => assertEqual(entity, expectedName: "DirectoryPath")); + static void assertEqual(IEntityValue entity, string expectedName) + { + var editorEntity = (UIPropertyEditorSnapshot)entity; + Assert.Equal(expectedName, editorEntity.Name); + } + } - static void assertEqual(IEntityValue entity, string expectedName) + [Fact] + public void WhenCreatingEditorsFromAStringPropertyWithPathSubtype_PathBrowseEditorIsCreated() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyEditorPropertiesAvailableStatus(includeName: true); + + var context = IQueryExecutionContextFactory.Create(); + var parentEntity = IEntityWithIdFactory.Create(key: "parentKey", value: "parentId"); + var rule = new Rule(); + rule.BeginInit(); + rule.Properties.Add( + new StringProperty { - var editorEntity = (UIPropertyEditorSnapshot)entity; - Assert.Equal(expectedName, editorEntity.Name); - } - } + Name = "MyProperty", + Subtype = "directory" + }); + rule.EndInit(); + + var results = UIPropertyEditorDataProducer.CreateEditorValues(context, parentEntity, rule, "MyProperty", properties); - private class TestProperty : BaseProperty + Assert.Collection(results, entity => assertEqual(entity, expectedName: "DirectoryPath")); + + static void assertEqual(IEntityValue entity, string expectedName) { + var editorEntity = (UIPropertyEditorSnapshot)entity; + Assert.Equal(expectedName, editorEntity.Name); } } + + private class TestProperty : BaseProperty + { + } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducerTests.cs index 08fae3ad19..815ca07925 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/PropertyPages/UIPropertyValueDataProducerTests.cs @@ -5,165 +5,164 @@ using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.ProjectSystem.Query.Framework; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class UIPropertyValueDataProducerTests { - public class UIPropertyValueDataProducerTests + [Fact] + public async Task WhenPropertyIsAnIEvaluatedProperty_GetUnevaluatedValueAsyncIsCalled() { - [Fact] - public async Task WhenPropertyIsAnIEvaluatedProperty_GetUnevaluatedValueAsyncIsCalled() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeUnevaluatedValue: true); + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeUnevaluatedValue: true); - var mockEvaluatedProperty = new Mock(); - mockEvaluatedProperty.Setup(m => m.GetUnevaluatedValueAsync()).ReturnsAsync("unevaluated value"); - var property = mockEvaluatedProperty.Object; + var mockEvaluatedProperty = new Mock(); + mockEvaluatedProperty.Setup(m => m.GetUnevaluatedValueAsync()).ReturnsAsync("unevaluated value"); + var property = mockEvaluatedProperty.Object; - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var id = new EntityIdentity(key: "A", value: "B"); - var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var id = new EntityIdentity(key: "A", value: "B"); + var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); - var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( - entityRuntime, - id, - configuration, - property, - properties); + var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( + entityRuntime, + id, + configuration, + property, + properties); - Assert.Equal(expected: "unevaluated value", actual: result.UnevaluatedValue); - mockEvaluatedProperty.Verify(m => m.GetUnevaluatedValueAsync()); - } + Assert.Equal(expected: "unevaluated value", actual: result.UnevaluatedValue); + mockEvaluatedProperty.Verify(m => m.GetUnevaluatedValueAsync()); + } - [Fact] - public async Task WhenThePropertyIsAnIBoolProperty_ThenTheEvaluatedValueIsABool() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); + [Fact] + public async Task WhenThePropertyIsAnIBoolProperty_ThenTheEvaluatedValueIsABool() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); - var mockBoolProperty = new Mock(); - mockBoolProperty.Setup(m => m.GetValueAsBoolAsync()).ReturnsAsync(true); - var property = mockBoolProperty.Object; + var mockBoolProperty = new Mock(); + mockBoolProperty.Setup(m => m.GetValueAsBoolAsync()).ReturnsAsync(true); + var property = mockBoolProperty.Object; - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var id = new EntityIdentity(key: "A", value: "B"); - var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var id = new EntityIdentity(key: "A", value: "B"); + var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); - var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( - entityRuntime, - id, - configuration, - property, - properties); + var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( + entityRuntime, + id, + configuration, + property, + properties); - Assert.Equal(expected: true, actual: result.EvaluatedValue); - } + Assert.Equal(expected: true, actual: result.EvaluatedValue); + } - [Fact] - public async Task WhenThePropertyIsAnIStringProperty_ThenTheEvaluatedValuesIsAString() - { + [Fact] + public async Task WhenThePropertyIsAnIStringProperty_ThenTheEvaluatedValuesIsAString() + { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); - var mockStringProperty = new Mock(); - mockStringProperty.Setup(m => m.GetValueAsStringAsync()).ReturnsAsync("string value"); - var property = mockStringProperty.Object; + var mockStringProperty = new Mock(); + mockStringProperty.Setup(m => m.GetValueAsStringAsync()).ReturnsAsync("string value"); + var property = mockStringProperty.Object; - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var id = new EntityIdentity(key: "A", value: "B"); - var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var id = new EntityIdentity(key: "A", value: "B"); + var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); - var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( - entityRuntime, - id, - configuration, - property, - properties); + var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( + entityRuntime, + id, + configuration, + property, + properties); - Assert.Equal(expected: "string value", actual: result.EvaluatedValue); - } + Assert.Equal(expected: "string value", actual: result.EvaluatedValue); + } - [Fact] - public async Task WhenThePropertyIsAnIIntProperty_ThenTheEvaluatedValueIsAnInt() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); + [Fact] + public async Task WhenThePropertyIsAnIIntProperty_ThenTheEvaluatedValueIsAnInt() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); - var mockIntProperty = new Mock(); - mockIntProperty.Setup(m => m.GetValueAsIntAsync()).ReturnsAsync(42); - var property = mockIntProperty.Object; + var mockIntProperty = new Mock(); + mockIntProperty.Setup(m => m.GetValueAsIntAsync()).ReturnsAsync(42); + var property = mockIntProperty.Object; - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var id = new EntityIdentity(key: "A", value: "B"); - var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var id = new EntityIdentity(key: "A", value: "B"); + var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); - var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( - entityRuntime, - id, - configuration, - property, - properties); + var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( + entityRuntime, + id, + configuration, + property, + properties); - Assert.Equal(expected: 42, actual: result.EvaluatedValue); - } + Assert.Equal(expected: 42, actual: result.EvaluatedValue); + } - [Fact] - public async Task WhenThePropertyIsAnIEnumProperty_ThenTheEvaluatedValueIsAString() - { - var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); + [Fact] + public async Task WhenThePropertyIsAnIEnumProperty_ThenTheEvaluatedValueIsAString() + { + var properties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(includeEvaluatedValue: true); - var enumValue = IEnumValueFactory.Create(name: "enum value"); - var mockEnumProperty = new Mock(); - mockEnumProperty.Setup(m => m.GetValueAsIEnumValueAsync()).ReturnsAsync(enumValue); - var property = mockEnumProperty.Object; + var enumValue = IEnumValueFactory.Create(name: "enum value"); + var mockEnumProperty = new Mock(); + mockEnumProperty.Setup(m => m.GetValueAsIEnumValueAsync()).ReturnsAsync(enumValue); + var property = mockEnumProperty.Object; - var entityRuntime = IEntityRuntimeModelFactory.Create(); - var id = new EntityIdentity(key: "A", value: "B"); - var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); + var entityRuntime = IEntityRuntimeModelFactory.Create(); + var id = new EntityIdentity(key: "A", value: "B"); + var configuration = ProjectConfigurationFactory.Create(configuration: "Alpha|Beta|Gamma"); - var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( - entityRuntime, - id, - configuration, - property, - properties); + var result = (UIPropertyValueSnapshot)await UIPropertyValueDataProducer.CreateUIPropertyValueValueAsync( + entityRuntime, + id, + configuration, + property, + properties); - Assert.Equal(expected: "enum value", actual: result.EvaluatedValue); - } + Assert.Equal(expected: "enum value", actual: result.EvaluatedValue); + } - [Fact] - public async Task WhenThePropertyIsConfigurationIndependent_ThenOnlyOneValueIsReturned() + [Fact] + public async Task WhenThePropertyIsConfigurationIndependent_ThenOnlyOneValueIsReturned() + { + var parent = IEntityWithIdFactory.Create(key: "ParentName", value: "Aardvark"); + + var defaultConfiguration = ProjectConfigurationFactory.Create("Alpha|Beta"); + var otherConfiguration = ProjectConfigurationFactory.Create("Delta|Gamma"); + var cache = IProjectStateFactory.Create( + projectConfigurations: ImmutableHashSet.Empty.Add(defaultConfiguration).Add(otherConfiguration), + defaultConfiguration: defaultConfiguration, + bindToRule: (config, schemaName, context) => IRuleFactory.Create( + name: "ParentName", + properties: new[] { IPropertyFactory.Create("MyProperty") })); + + var schema = new Rule { - var parent = IEntityWithIdFactory.Create(key: "ParentName", value: "Aardvark"); - - var defaultConfiguration = ProjectConfigurationFactory.Create("Alpha|Beta"); - var otherConfiguration = ProjectConfigurationFactory.Create("Delta|Gamma"); - var cache = IProjectStateFactory.Create( - projectConfigurations: ImmutableHashSet.Empty.Add(defaultConfiguration).Add(otherConfiguration), - defaultConfiguration: defaultConfiguration, - bindToRule: (config, schemaName, context) => IRuleFactory.Create( - name: "ParentName", - properties: new[] { IPropertyFactory.Create("MyProperty") })); - - var schema = new Rule + Properties = { - Properties = - { - new TestProperty { Name = "MyProperty", DataSource = new() { HasConfigurationCondition = false }} - } - }; - var propertyName = "MyProperty"; - var requestedProperties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(); - var results = await UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync( - IQueryExecutionContextFactory.Create(), - parent, - cache, - schema, - propertiesContext: QueryProjectPropertiesContext.ProjectFile, - propertyName, - requestedProperties); - - Assert.Single(results); - } - - private class TestProperty : BaseProperty - { - } + new TestProperty { Name = "MyProperty", DataSource = new() { HasConfigurationCondition = false }} + } + }; + var propertyName = "MyProperty"; + var requestedProperties = PropertiesAvailableStatusFactory.CreateUIPropertyValuePropertiesAvailableStatus(); + var results = await UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync( + IQueryExecutionContextFactory.Create(), + parent, + cache, + schema, + propertiesContext: QueryProjectPropertiesContext.ProjectFile, + propertyName, + requestedProperties); + + Assert.Single(results); + } + + private class TestProperty : BaseProperty + { } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/QueryProjectPropertiesContextTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/QueryProjectPropertiesContextTests.cs index 30c23241dc..c092710e70 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/QueryProjectPropertiesContextTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Query/QueryProjectPropertiesContextTests.cs @@ -2,48 +2,47 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Query +namespace Microsoft.VisualStudio.ProjectSystem.VS.Query; + +public class QueryProjectPropertiesContextTests { - public class QueryProjectPropertiesContextTests + [Theory] + [MemberData(nameof(ContextsThatAreEqual))] + public void Equal(IProjectPropertiesContext a, IProjectPropertiesContext b) { - [Theory] - [MemberData(nameof(ContextsThatAreEqual))] - public void Equal(IProjectPropertiesContext a, IProjectPropertiesContext b) - { - Assert.True(a.Equals(b)); - Assert.True(b.Equals(a)); - Assert.True(a.GetHashCode() == b.GetHashCode()); - } + Assert.True(a.Equals(b)); + Assert.True(b.Equals(a)); + Assert.True(a.GetHashCode() == b.GetHashCode()); + } - [Theory] - [MemberData(nameof(ContextsThatAreNotEqual))] - public void NotEqual(IProjectPropertiesContext a, IProjectPropertiesContext b) - { - Assert.False(a.Equals(b)); - Assert.False(a.GetHashCode() == b.GetHashCode()); - } + [Theory] + [MemberData(nameof(ContextsThatAreNotEqual))] + public void NotEqual(IProjectPropertiesContext a, IProjectPropertiesContext b) + { + Assert.False(a.Equals(b)); + Assert.False(a.GetHashCode() == b.GetHashCode()); + } - public static IEnumerable ContextsThatAreEqual() + public static IEnumerable ContextsThatAreEqual() + { + return new QueryProjectPropertiesContext[][] { - return new QueryProjectPropertiesContext[][] - { - new QueryProjectPropertiesContext[] { QueryProjectPropertiesContext.ProjectFile, QueryProjectPropertiesContext.ProjectFile }, - new QueryProjectPropertiesContext[] { new(true, string.Empty, null, null), QueryProjectPropertiesContext.ProjectFile }, - new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, null), new(true, @"c:\ALPHA\Beta", null, null) }, - new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", "myItemType", null), new(true, @"C:\alpha\beta", "MyItemType", null) }, - new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, "MyItemName"), new(true, @"C:\alpha\beta", null, "MYITEMNAME") } - }; - } + new QueryProjectPropertiesContext[] { QueryProjectPropertiesContext.ProjectFile, QueryProjectPropertiesContext.ProjectFile }, + new QueryProjectPropertiesContext[] { new(true, string.Empty, null, null), QueryProjectPropertiesContext.ProjectFile }, + new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, null), new(true, @"c:\ALPHA\Beta", null, null) }, + new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", "myItemType", null), new(true, @"C:\alpha\beta", "MyItemType", null) }, + new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, "MyItemName"), new(true, @"C:\alpha\beta", null, "MYITEMNAME") } + }; + } - public static IEnumerable ContextsThatAreNotEqual() + public static IEnumerable ContextsThatAreNotEqual() + { + return new QueryProjectPropertiesContext[][] { - return new QueryProjectPropertiesContext[][] - { - new QueryProjectPropertiesContext[] { new(false, string.Empty, null, null), QueryProjectPropertiesContext.ProjectFile }, - new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, null), new(true, @"C:\alpha\gamma", null, null) }, - new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", "myItemType", null), new(true, @"C:\alpha\beta", "MyOtherItemType", null) }, - new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, "MyItemName"), new(true, @"C:\alpha\beta", null, "MyOtherItemName") } - }; - } + new QueryProjectPropertiesContext[] { new(false, string.Empty, null, null), QueryProjectPropertiesContext.ProjectFile }, + new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, null), new(true, @"C:\alpha\gamma", null, null) }, + new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", "myItemType", null), new(true, @"C:\alpha\beta", "MyOtherItemType", null) }, + new QueryProjectPropertiesContext[] { new(true, @"C:\alpha\beta", null, "MyItemName"), new(true, @"C:\alpha\beta", null, "MyOtherItemName") } + }; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/DesignTimeAssemblyResolutionTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/DesignTimeAssemblyResolutionTests.cs index bd32f330e6..896b636674 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/DesignTimeAssemblyResolutionTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/DesignTimeAssemblyResolutionTests.cs @@ -6,289 +6,288 @@ using VSLangProj110; using VSLangProj80; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +public class DesignTimeAssemblyResolutionTests { - public class DesignTimeAssemblyResolutionTests + [Fact] + public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsHResult_ReturnsHResult() + { + var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.E_INVALIDARG); + var resolution = CreateInstance(hierarchy); + + var result = resolution.GetTargetFramework(out _); + + Assert.Equal(VSConstants.E_INVALIDARG, result); + } + + [Fact] + public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsHResult_SetsTargetFrameworkToNull() { - [Fact] - public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsHResult_ReturnsHResult() - { - var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.E_INVALIDARG); - var resolution = CreateInstance(hierarchy); + var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.E_INVALIDARG); + var resolution = CreateInstance(hierarchy); + + resolution.GetTargetFramework(out string? result); - var result = resolution.GetTargetFramework(out _); + Assert.Null(result); + } + + [Fact] + public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsDISP_E_MEMBERNOTFOUND_ReturnsOK() + { + var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.DISP_E_MEMBERNOTFOUND); + var resolution = CreateInstance(hierarchy); - Assert.Equal(VSConstants.E_INVALIDARG, result); - } + var result = resolution.GetTargetFramework(out _); + + Assert.Equal(VSConstants.S_OK, result); + } + + [Fact] + public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsDISP_E_MEMBERNOTFOUND_SetsTargetFrameworkToNull() + { + var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.DISP_E_MEMBERNOTFOUND); + var resolution = CreateInstance(hierarchy); + + resolution.GetTargetFramework(out string? result); + + Assert.Null(result); + } + + [Fact] + public void GetTargetFramework_WhenDisposed_ReturnUnexpected() + { + var resolution = CreateInstance(); + resolution.Dispose(); - [Fact] - public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsHResult_SetsTargetFrameworkToNull() - { - var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.E_INVALIDARG); - var resolution = CreateInstance(hierarchy); + var result = resolution.GetTargetFramework(out _); - resolution.GetTargetFramework(out string? result); + Assert.Equal(VSConstants.E_UNEXPECTED, result); + } - Assert.Null(result); - } + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + [InlineData(".NETFramework, Version=v4.5")] + [InlineData(".NETFramework, Version=v4.5, Profile=Client")] + public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsValue_SetsTargetFramework(string input) + { + var hierarchy = IVsHierarchyFactory.ImplementGetProperty(input); + var resolution = CreateInstance(hierarchy); - [Fact] - public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsDISP_E_MEMBERNOTFOUND_ReturnsOK() - { - var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.DISP_E_MEMBERNOTFOUND); - var resolution = CreateInstance(hierarchy); + var hr = resolution.GetTargetFramework(out string? result); - var result = resolution.GetTargetFramework(out _); + Assert.Equal(input, result); + Assert.Equal(VSConstants.S_OK, hr); + } - Assert.Equal(VSConstants.S_OK, result); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_NullAsAssemblySpecs_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Fact] - public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsDISP_E_MEMBERNOTFOUND_SetsTargetFrameworkToNull() - { - var hierarchy = IVsHierarchyFactory.ImplementGetProperty(VSConstants.DISP_E_MEMBERNOTFOUND); - var resolution = CreateInstance(hierarchy); + var result = resolution.ResolveAssemblyPathInTargetFx(null, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - resolution.GetTargetFramework(out string? result); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Null(result); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_ZeroAsAssembliesToResolve_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Fact] - public void GetTargetFramework_WhenDisposed_ReturnUnexpected() - { - var resolution = CreateInstance(); - resolution.Dispose(); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 0, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - var result = resolution.GetTargetFramework(out _); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Equal(VSConstants.E_UNEXPECTED, result); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_NullAsResolvedAssemblyPaths_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - [InlineData(".NETFramework, Version=v4.5")] - [InlineData(".NETFramework, Version=v4.5, Profile=Client")] - public void GetTargetFramework_WhenUnderlyingGetPropertyReturnsValue_SetsTargetFramework(string input) - { - var hierarchy = IVsHierarchyFactory.ImplementGetProperty(input); - var resolution = CreateInstance(hierarchy); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, null, out uint resolvedAssemblyPaths); - var hr = resolution.GetTargetFramework(out string? result); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Equal(input, result); - Assert.Equal(VSConstants.S_OK, hr); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_MoreAssemblySpecsThanAssembliesToResolve_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Fact] - public void ResolveAssemblyPathInTargetFx_NullAsAssemblySpecs_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib", "System" }, 1, new VsResolvedAssemblyPath[2], out uint resolvedAssemblyPaths); - var result = resolution.ResolveAssemblyPathInTargetFx(null, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_MoreAssemblySpecsThanResolvedAssemblyPaths_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Fact] - public void ResolveAssemblyPathInTargetFx_ZeroAsAssembliesToResolve_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib", "System" }, 2, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 0, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_MoreAssembliesToResolveThanAssemblySpecs_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Fact] - public void ResolveAssemblyPathInTargetFx_NullAsResolvedAssemblyPaths_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 2, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, null, out uint resolvedAssemblyPaths); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_MoreResolvedAssemblyPathsThanAssemblySpecs_ReturnsE_INVALIDARG() + { + var resolution = CreateInstance(); - [Fact] - public void ResolveAssemblyPathInTargetFx_MoreAssemblySpecsThanAssembliesToResolve_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[2], out uint resolvedAssemblyPaths); - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib", "System" }, 1, new VsResolvedAssemblyPath[2], out uint resolvedAssemblyPaths); + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } + [Fact] + public void ResolveAssemblyPathInTargetFx_UnresolvedAssembly_SetsResolvedAssemblyPathsToZero() + { // BUG: https://devdiv.visualstudio.com/DevDiv/_workitems?id=368836 + var reference = Reference3Factory.CreateAssemblyReference("mscorlib", "1.0.0.0"); - [Fact] - public void ResolveAssemblyPathInTargetFx_MoreAssemblySpecsThanResolvedAssemblyPaths_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); + var resolution = CreateInstance(reference); - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib", "System" }, 2, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } + Assert.Equal(VSConstants.S_OK, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } - [Fact] - public void ResolveAssemblyPathInTargetFx_MoreAssembliesToResolveThanAssemblySpecs_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); + [Fact] + public void ResolveAssemblyPathInTargetFx_ComReference_SetsResolvedAssemblyPathsToZero() + { + var reference = Reference3Factory.CreateAssemblyReference("mscorlib", "1.0.0.0", type: prjReferenceType.prjReferenceTypeActiveX, refType: __PROJECTREFERENCETYPE.PROJREFTYPE_ACTIVEX); - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 2, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } - - [Fact] - public void ResolveAssemblyPathInTargetFx_MoreResolvedAssemblyPathsThanAssemblySpecs_ReturnsE_INVALIDARG() - { - var resolution = CreateInstance(); - - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[2], out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } - - [Fact] - public void ResolveAssemblyPathInTargetFx_UnresolvedAssembly_SetsResolvedAssemblyPathsToZero() - { // BUG: https://devdiv.visualstudio.com/DevDiv/_workitems?id=368836 - var reference = Reference3Factory.CreateAssemblyReference("mscorlib", "1.0.0.0"); - - var resolution = CreateInstance(reference); - - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.S_OK, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } - - [Fact] - public void ResolveAssemblyPathInTargetFx_ComReference_SetsResolvedAssemblyPathsToZero() - { - var reference = Reference3Factory.CreateAssemblyReference("mscorlib", "1.0.0.0", type: prjReferenceType.prjReferenceTypeActiveX, refType: __PROJECTREFERENCETYPE.PROJREFTYPE_ACTIVEX); - - var resolution = CreateInstance(reference); - - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.S_OK, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } - - [Fact] - public void ResolveAssemblyPathInTargetFx_SdkReference_SetsResolvedAssemblyPathsToZero() - { - // SDKs say they are "assemblies" for Reference.Type, but SDK for Reference.RefType - var reference = Reference3Factory.CreateAssemblyReference("mscorlib", "1.0.0.0", type: prjReferenceType.prjReferenceTypeAssembly, refType: (__PROJECTREFERENCETYPE)__PROJECTREFERENCETYPE2.PROJREFTYPE_SDK); - - var resolution = CreateInstance(reference); + var resolution = CreateInstance(reference); + + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); + + Assert.Equal(VSConstants.S_OK, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } + + [Fact] + public void ResolveAssemblyPathInTargetFx_SdkReference_SetsResolvedAssemblyPathsToZero() + { + // SDKs say they are "assemblies" for Reference.Type, but SDK for Reference.RefType + var reference = Reference3Factory.CreateAssemblyReference("mscorlib", "1.0.0.0", type: prjReferenceType.prjReferenceTypeAssembly, refType: (__PROJECTREFERENCETYPE)__PROJECTREFERENCETYPE2.PROJREFTYPE_SDK); + + var resolution = CreateInstance(reference); + + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); + + Assert.Equal(VSConstants.S_OK, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("System, Bar")] + [InlineData("System, Version=NotAVersion")] + [InlineData("System, PublicKeyToken=ABC")] + public void ResolveAssemblyPathInTargetFx_InvalidNameAsAssemblySpec_ReturnsE_INVALIDARG(string? input) + { + var resolution = CreateInstance(); + + var result = resolution.ResolveAssemblyPathInTargetFx(new string?[] { input }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); + + Assert.Equal(VSConstants.E_INVALIDARG, result); + Assert.Equal(0u, resolvedAssemblyPaths); + } + + [Theory] // Input // Name // Version // Path + [InlineData("System", "System", "", @"C:\System.dll")] + [InlineData("System", "System", "1", @"C:\System.dll")] + [InlineData("System", "System", "1.0", @"C:\System.dll")] + [InlineData("System", "System", "1.0.0", @"C:\System.dll")] + [InlineData("System", "System", "1.0.0.0", @"C:\System.dll")] + [InlineData("System.Foo", "System.Foo", "1.0.0.0", @"C:\System.Foo.dll")] + [InlineData("System, Version=1.0.0.0", "System", "1.0.0.0", @"C:\System.dll")] + [InlineData("System, Version=1.0.0.0", "System", "2.0.0.0", @"C:\System.dll")] // We let a later version satisfy an earlier version + [InlineData("System, Version=1.0", "System", "2.0.0.0", @"C:\System.dll")] + [InlineData("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System", "4.0.0.0", @"C:\System.dll")] + public void ResolveAssemblyPathInTargetFx_NameThatMatches_ReturnsResolvedPaths(string input, string name, string version, string path) + { + var reference = Reference3Factory.CreateAssemblyReference(name, version, path); + + var resolution = CreateInstance(reference); + + var resolvedPaths = new VsResolvedAssemblyPath[1]; + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { input }, 1, resolvedPaths, out uint resolvedAssemblyPaths); + + Assert.Equal(VSConstants.S_OK, result); + Assert.Equal(1u, resolvedAssemblyPaths); + Assert.Equal(input, resolvedPaths[0].bstrOrigAssemblySpec); + Assert.Equal(path, resolvedPaths[0].bstrResolvedAssemblyPath); + } + + [Theory] // Input // Name // Version // Path + [InlineData("System", "System.Core", "", @"C:\System.Core.dll")] + [InlineData("System", "system", "", @"C:\System.dll")] + [InlineData("encyclopædia", "encyclopaedia", "", @"C:\System.dll")] + [InlineData("System, Version=1.0.0.0", "System", "", @"C:\System.dll")] + [InlineData("System, Version=2.0.0.0", "System", "1.0.0.0", @"C:\System.dll")] + public void ResolveAssemblyPathInTargetFx_NameThatDoesNotMatch_SetsResolvedAssemblyPathsToZero(string input, string name, string version, string path) + { + var reference = Reference3Factory.CreateAssemblyReference(name, version, path); + + var resolution = CreateInstance(reference); + + var resolvedPaths = new VsResolvedAssemblyPath[1]; + var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { input }, 1, resolvedPaths, out uint resolvedAssemblyPaths); + + Assert.Equal(VSConstants.S_OK, result); + Assert.Equal(0u, resolvedAssemblyPaths); + Assert.Null(resolvedPaths[0].bstrOrigAssemblySpec); + Assert.Null(resolvedPaths[0].bstrResolvedAssemblyPath); + } + + [Fact] + public void ResolveAssemblyPathInTargetFx_WhenDisposed_ReturnUnexpected() + { + var resolution = CreateInstance(); + resolution.Dispose(); + + var result = resolution.ResolveAssemblyPathInTargetFx(new[] { "System" }, 1, new VsResolvedAssemblyPath[1], out _); + + Assert.Equal(VSConstants.E_UNEXPECTED, result); + } + + private static DesignTimeAssemblyResolution CreateInstance(params Reference[] references) + { + VSProject vsProject = VSProjectFactory.ImplementReferences(references); + Project project = ProjectFactory.ImplementObject(() => vsProject); + IVsHierarchy hierarchy = IVsHierarchyFactory.ImplementGetProperty(project); + + return CreateInstance(hierarchy); + } + + private static DesignTimeAssemblyResolution CreateInstance(IVsHierarchy? hierarchy = null) + { + hierarchy ??= IVsHierarchyFactory.Create(); - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { "mscorlib" }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.S_OK, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData("System, Bar")] - [InlineData("System, Version=NotAVersion")] - [InlineData("System, PublicKeyToken=ABC")] - public void ResolveAssemblyPathInTargetFx_InvalidNameAsAssemblySpec_ReturnsE_INVALIDARG(string? input) - { - var resolution = CreateInstance(); + IUnconfiguredProjectVsServices projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy); - var result = resolution.ResolveAssemblyPathInTargetFx(new string?[] { input }, 1, new VsResolvedAssemblyPath[1], out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.E_INVALIDARG, result); - Assert.Equal(0u, resolvedAssemblyPaths); - } - - [Theory] // Input // Name // Version // Path - [InlineData("System", "System", "", @"C:\System.dll")] - [InlineData("System", "System", "1", @"C:\System.dll")] - [InlineData("System", "System", "1.0", @"C:\System.dll")] - [InlineData("System", "System", "1.0.0", @"C:\System.dll")] - [InlineData("System", "System", "1.0.0.0", @"C:\System.dll")] - [InlineData("System.Foo", "System.Foo", "1.0.0.0", @"C:\System.Foo.dll")] - [InlineData("System, Version=1.0.0.0", "System", "1.0.0.0", @"C:\System.dll")] - [InlineData("System, Version=1.0.0.0", "System", "2.0.0.0", @"C:\System.dll")] // We let a later version satisfy an earlier version - [InlineData("System, Version=1.0", "System", "2.0.0.0", @"C:\System.dll")] - [InlineData("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System", "4.0.0.0", @"C:\System.dll")] - public void ResolveAssemblyPathInTargetFx_NameThatMatches_ReturnsResolvedPaths(string input, string name, string version, string path) - { - var reference = Reference3Factory.CreateAssemblyReference(name, version, path); - - var resolution = CreateInstance(reference); - - var resolvedPaths = new VsResolvedAssemblyPath[1]; - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { input }, 1, resolvedPaths, out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.S_OK, result); - Assert.Equal(1u, resolvedAssemblyPaths); - Assert.Equal(input, resolvedPaths[0].bstrOrigAssemblySpec); - Assert.Equal(path, resolvedPaths[0].bstrResolvedAssemblyPath); - } - - [Theory] // Input // Name // Version // Path - [InlineData("System", "System.Core", "", @"C:\System.Core.dll")] - [InlineData("System", "system", "", @"C:\System.dll")] - [InlineData("encyclopædia", "encyclopaedia", "", @"C:\System.dll")] - [InlineData("System, Version=1.0.0.0", "System", "", @"C:\System.dll")] - [InlineData("System, Version=2.0.0.0", "System", "1.0.0.0", @"C:\System.dll")] - public void ResolveAssemblyPathInTargetFx_NameThatDoesNotMatch_SetsResolvedAssemblyPathsToZero(string input, string name, string version, string path) - { - var reference = Reference3Factory.CreateAssemblyReference(name, version, path); - - var resolution = CreateInstance(reference); - - var resolvedPaths = new VsResolvedAssemblyPath[1]; - var result = resolution.ResolveAssemblyPathInTargetFx(new string[] { input }, 1, resolvedPaths, out uint resolvedAssemblyPaths); - - Assert.Equal(VSConstants.S_OK, result); - Assert.Equal(0u, resolvedAssemblyPaths); - Assert.Null(resolvedPaths[0].bstrOrigAssemblySpec); - Assert.Null(resolvedPaths[0].bstrResolvedAssemblyPath); - } - - [Fact] - public void ResolveAssemblyPathInTargetFx_WhenDisposed_ReturnUnexpected() - { - var resolution = CreateInstance(); - resolution.Dispose(); - - var result = resolution.ResolveAssemblyPathInTargetFx(new[] { "System" }, 1, new VsResolvedAssemblyPath[1], out _); - - Assert.Equal(VSConstants.E_UNEXPECTED, result); - } - - private static DesignTimeAssemblyResolution CreateInstance(params Reference[] references) - { - VSProject vsProject = VSProjectFactory.ImplementReferences(references); - Project project = ProjectFactory.ImplementObject(() => vsProject); - IVsHierarchy hierarchy = IVsHierarchyFactory.ImplementGetProperty(project); - - return CreateInstance(hierarchy); - } - - private static DesignTimeAssemblyResolution CreateInstance(IVsHierarchy? hierarchy = null) - { - hierarchy ??= IVsHierarchyFactory.Create(); - - IUnconfiguredProjectVsServices projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(() => hierarchy); - - return new DesignTimeAssemblyResolution(projectVsServices); - } + return new DesignTimeAssemblyResolution(projectVsServices); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/ReferenceCleanupServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/ReferenceCleanupServiceTests.cs index 9f41a4fbab..1a8af62bea 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/ReferenceCleanupServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/References/ReferenceCleanupServiceTests.cs @@ -5,195 +5,194 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.References; -namespace Microsoft.VisualStudio.ProjectSystem.VS.References +namespace Microsoft.VisualStudio.ProjectSystem.VS.References; + +public class ReferenceCleanupServiceTests { - public class ReferenceCleanupServiceTests - { - private const string _projectPath1 = "C:\\Dev\\Solution\\Project\\Project1.csproj"; - private const string _projectPath2 = "C:\\Dev\\Solution\\Project\\Project2.csproj"; - private const string _projectPath3 = "C:\\Dev\\Solution\\Project\\Project3.csproj"; - private const string _package3 = "package3"; + private const string _projectPath1 = "C:\\Dev\\Solution\\Project\\Project1.csproj"; + private const string _projectPath2 = "C:\\Dev\\Solution\\Project\\Project2.csproj"; + private const string _projectPath3 = "C:\\Dev\\Solution\\Project\\Project3.csproj"; + private const string _package3 = "package3"; - private const string _assembly1 = "assembly1"; - private const string _assembly2 = "assembly2"; - private const string _assembly3 = "assembly3"; + private const string _assembly1 = "assembly1"; + private const string _assembly2 = "assembly2"; + private const string _assembly3 = "assembly3"; - private static Mock? s_item; + private static Mock? s_item; - [Fact] - public async Task GetProjectReferencesAsync_NoValidProjectFound_ThrowsException() - { - var referenceCleanupService = Setup(); + [Fact] + public async Task GetProjectReferencesAsync_NoValidProjectFound_ThrowsException() + { + var referenceCleanupService = Setup(); - await Assert.ThrowsAsync(() => - referenceCleanupService.GetProjectReferencesAsync("UnknownProject", CancellationToken.None)); - } + await Assert.ThrowsAsync(() => + referenceCleanupService.GetProjectReferencesAsync("UnknownProject", CancellationToken.None)); + } - [Fact] - public async Task GetProjectReferencesAsync_FoundZeroReferences_ReturnAllReferences() - { - var referenceCleanupService = Setup(); + [Fact] + public async Task GetProjectReferencesAsync_FoundZeroReferences_ReturnAllReferences() + { + var referenceCleanupService = Setup(); - var references = await referenceCleanupService.GetProjectReferencesAsync(_projectPath3, CancellationToken.None); + var references = await referenceCleanupService.GetProjectReferencesAsync(_projectPath3, CancellationToken.None); - Assert.Empty(references); - } + Assert.Empty(references); + } - [Theory] - [InlineData(_projectPath1, 5)] - [InlineData(_projectPath2, 4)] - [InlineData(_projectPath3, 0)] - public async Task GetProjectReferencesAsync_FoundReferences_ReturnAllReferences(string projectPath, int numberOfReferences) - { - var referenceCleanupService = Setup(); + [Theory] + [InlineData(_projectPath1, 5)] + [InlineData(_projectPath2, 4)] + [InlineData(_projectPath3, 0)] + public async Task GetProjectReferencesAsync_FoundReferences_ReturnAllReferences(string projectPath, int numberOfReferences) + { + var referenceCleanupService = Setup(); - var references = await referenceCleanupService.GetProjectReferencesAsync(projectPath, CancellationToken.None); + var references = await referenceCleanupService.GetProjectReferencesAsync(projectPath, CancellationToken.None); - Assert.Equal(numberOfReferences, references.Length); - } + Assert.Equal(numberOfReferences, references.Length); + } - [Fact] - public async Task UpdateReferenceAsync_TryUpdateReferenceAsync_ShouldThrowException() - { - var referenceCleanupService = Setup(); - var referenceUpdate1 = - new ProjectSystemReferenceUpdate(ProjectSystemUpdateAction.Remove, new ProjectSystemReferenceInfo(ProjectSystemReferenceType.Package, _package3, true)); + [Fact] + public async Task UpdateReferenceAsync_TryUpdateReferenceAsync_ShouldThrowException() + { + var referenceCleanupService = Setup(); + var referenceUpdate1 = + new ProjectSystemReferenceUpdate(ProjectSystemUpdateAction.Remove, new ProjectSystemReferenceInfo(ProjectSystemReferenceType.Package, _package3, true)); - await Assert.ThrowsAsync(() => - referenceCleanupService.TryUpdateReferenceAsync(_projectPath1, referenceUpdate1, CancellationToken.None)); - } + await Assert.ThrowsAsync(() => + referenceCleanupService.TryUpdateReferenceAsync(_projectPath1, referenceUpdate1, CancellationToken.None)); + } - [Fact] - public async Task UpdateReferencesAsync_GetUpdateReferenceOperationAsync_CreateOperation() - { - var referenceCleanupService = Setup(); - var referenceUpdate1 = - new ProjectSystemReferenceUpdate(ProjectSystemUpdateAction.Remove, new ProjectSystemReferenceInfo(ProjectSystemReferenceType.Package, "UnknownPackage", true)); + [Fact] + public async Task UpdateReferencesAsync_GetUpdateReferenceOperationAsync_CreateOperation() + { + var referenceCleanupService = Setup(); + var referenceUpdate1 = + new ProjectSystemReferenceUpdate(ProjectSystemUpdateAction.Remove, new ProjectSystemReferenceInfo(ProjectSystemReferenceType.Package, "UnknownPackage", true)); - var operation = await referenceCleanupService.GetUpdateReferenceOperationAsync(_projectPath1, referenceUpdate1, CancellationToken.None); + var operation = await referenceCleanupService.GetUpdateReferenceOperationAsync(_projectPath1, referenceUpdate1, CancellationToken.None); - Assert.NotNull(operation); - } + Assert.NotNull(operation); + } - [Fact] - public async Task UpdateReferencesAsync_GetUpdateReferenceOperationAsync_FailedToCreateOperation() - { - var referenceCleanupService = Setup(); - var referenceUpdate1 = - new ProjectSystemReferenceUpdate((ProjectSystemUpdateAction)3, new ProjectSystemReferenceInfo(ProjectSystemReferenceType.Package, "UnknownPackage", true)); + [Fact] + public async Task UpdateReferencesAsync_GetUpdateReferenceOperationAsync_FailedToCreateOperation() + { + var referenceCleanupService = Setup(); + var referenceUpdate1 = + new ProjectSystemReferenceUpdate((ProjectSystemUpdateAction)3, new ProjectSystemReferenceInfo(ProjectSystemReferenceType.Package, "UnknownPackage", true)); - var operation = await referenceCleanupService.GetUpdateReferenceOperationAsync(_projectPath1, referenceUpdate1, CancellationToken.None); + var operation = await referenceCleanupService.GetUpdateReferenceOperationAsync(_projectPath1, referenceUpdate1, CancellationToken.None); - Assert.Equal(ReferenceCleanupService.NullCommand, operation); - } + Assert.Equal(ReferenceCleanupService.NullCommand, operation); + } - private ReferenceCleanupService Setup() - { - var projectServiceAccessorMock = new Mock(); + private ReferenceCleanupService Setup() + { + var projectServiceAccessorMock = new Mock(); - var projectServiceMock = new Mock(); - AddLoadedProject(_projectPath1, CreateConfiguredProjectServicesForProject1, projectServiceMock); - AddLoadedProject(_projectPath2, CreateConfiguredProjectServicesForProject2, projectServiceMock); - AddLoadedProject(_projectPath3, CreateConfiguredProjectServicesForProject3, projectServiceMock); + var projectServiceMock = new Mock(); + AddLoadedProject(_projectPath1, CreateConfiguredProjectServicesForProject1, projectServiceMock); + AddLoadedProject(_projectPath2, CreateConfiguredProjectServicesForProject2, projectServiceMock); + AddLoadedProject(_projectPath3, CreateConfiguredProjectServicesForProject3, projectServiceMock); - projectServiceAccessorMock.Setup(c => c.GetProjectService(ProjectServiceThreadingModel.Multithreaded)).Returns(projectServiceMock.Object); - return new ReferenceCleanupService(projectServiceAccessorMock.Object); - } + projectServiceAccessorMock.Setup(c => c.GetProjectService(ProjectServiceThreadingModel.Multithreaded)).Returns(projectServiceMock.Object); + return new ReferenceCleanupService(projectServiceAccessorMock.Object); + } - private void AddLoadedProject(string projectPath, Func createConfiguredProjectServicesForProject, Mock projectServiceMock) - { - var configuredProject = ConfiguredProjectFactory.Create(services: createConfiguredProjectServicesForProject()); - var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: projectPath, configuredProject: configuredProject); - projectServiceMock.Setup(c => c.GetLoadedProject(It.Is(arg => arg == projectPath))).Returns(unconfiguredProject); - } + private void AddLoadedProject(string projectPath, Func createConfiguredProjectServicesForProject, Mock projectServiceMock) + { + var configuredProject = ConfiguredProjectFactory.Create(services: createConfiguredProjectServicesForProject()); + var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: projectPath, configuredProject: configuredProject); + projectServiceMock.Setup(c => c.GetLoadedProject(It.Is(arg => arg == projectPath))).Returns(unconfiguredProject); + } - private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject1() - { - return CreateConfiguredProjectServicesForProject( - new List<(string, string)> - { - (_projectPath2, PropertySerializer.SimpleTypes.ToString(false)), - (_projectPath3, PropertySerializer.SimpleTypes.ToString(false)) - }, - new List<(string, string)> - { - (_package3 , PropertySerializer.SimpleTypes.ToString(false)) - }, - new List<(string, string)> - { - (_assembly1, PropertySerializer.SimpleTypes.ToString(false)), - (_assembly2, PropertySerializer.SimpleTypes.ToString(false)) - }); - } + private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject1() + { + return CreateConfiguredProjectServicesForProject( + new List<(string, string)> + { + (_projectPath2, PropertySerializer.SimpleTypes.ToString(false)), + (_projectPath3, PropertySerializer.SimpleTypes.ToString(false)) + }, + new List<(string, string)> + { + (_package3 , PropertySerializer.SimpleTypes.ToString(false)) + }, + new List<(string, string)> + { + (_assembly1, PropertySerializer.SimpleTypes.ToString(false)), + (_assembly2, PropertySerializer.SimpleTypes.ToString(false)) + }); + } - private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject2() - { - return CreateConfiguredProjectServicesForProject( - new List<(string, string)> - { - (_projectPath3, PropertySerializer.SimpleTypes.ToString(true)) - }, - new List<(string, string)> { }, - new List<(string, string)> - { - (_assembly1, PropertySerializer.SimpleTypes.ToString(true)), - (_assembly2, PropertySerializer.SimpleTypes.ToString(true)), - (_assembly3, PropertySerializer.SimpleTypes.ToString(true)) - }); - } + private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject2() + { + return CreateConfiguredProjectServicesForProject( + new List<(string, string)> + { + (_projectPath3, PropertySerializer.SimpleTypes.ToString(true)) + }, + new List<(string, string)> { }, + new List<(string, string)> + { + (_assembly1, PropertySerializer.SimpleTypes.ToString(true)), + (_assembly2, PropertySerializer.SimpleTypes.ToString(true)), + (_assembly3, PropertySerializer.SimpleTypes.ToString(true)) + }); + } - private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject3() - { - return CreateConfiguredProjectServicesForProject( - new List<(string, string)> { }, - new List<(string, string)> { }, - new List<(string, string)> { }); - } + private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject3() + { + return CreateConfiguredProjectServicesForProject( + new List<(string, string)> { }, + new List<(string, string)> { }, + new List<(string, string)> { }); + } - private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject( - List<(string, string)> projects, List<(string, string)> packages, List<(string, string)> assemblies) - { - var projectReferencesService = new Mock(); - IImmutableSet unresolvedProjectReferences = - CreateReferences(projects); - projectReferencesService.Setup(c => c.GetUnresolvedReferencesAsync()).ReturnsAsync(unresolvedProjectReferences); + private static ConfiguredProjectServices CreateConfiguredProjectServicesForProject( + List<(string, string)> projects, List<(string, string)> packages, List<(string, string)> assemblies) + { + var projectReferencesService = new Mock(); + IImmutableSet unresolvedProjectReferences = + CreateReferences(projects); + projectReferencesService.Setup(c => c.GetUnresolvedReferencesAsync()).ReturnsAsync(unresolvedProjectReferences); - var packageReferencesService = new Mock(); - IImmutableSet unresolvedPackageReferences = CreateReferences(packages); - packageReferencesService.Setup(c => c.GetUnresolvedReferencesAsync()).ReturnsAsync(unresolvedPackageReferences); + var packageReferencesService = new Mock(); + IImmutableSet unresolvedPackageReferences = CreateReferences(packages); + packageReferencesService.Setup(c => c.GetUnresolvedReferencesAsync()).ReturnsAsync(unresolvedPackageReferences); - var assemblyReferencesService = new Mock(); - IImmutableSet unresolvedAssemblyReferences = - CreateReferences(assemblies); - assemblyReferencesService.Setup(c => c.GetUnresolvedReferencesAsync()).ReturnsAsync(unresolvedAssemblyReferences); + var assemblyReferencesService = new Mock(); + IImmutableSet unresolvedAssemblyReferences = + CreateReferences(assemblies); + assemblyReferencesService.Setup(c => c.GetUnresolvedReferencesAsync()).ReturnsAsync(unresolvedAssemblyReferences); - var configuredProjectServices = ConfiguredProjectServicesFactory.Create( - projectReferences: projectReferencesService.Object, packageReferences: packageReferencesService.Object, - assemblyReferences: assemblyReferencesService.Object); + var configuredProjectServices = ConfiguredProjectServicesFactory.Create( + projectReferences: projectReferencesService.Object, packageReferences: packageReferencesService.Object, + assemblyReferences: assemblyReferencesService.Object); - return configuredProjectServices; - } + return configuredProjectServices; + } - private static IImmutableSet CreateReferences(List<(string, string)> assemblies) - where T : class, IProjectItem + private static IImmutableSet CreateReferences(List<(string, string)> assemblies) + where T : class, IProjectItem + { + ISet references = new HashSet(); + + foreach ((string include, string evaluatedValue) in assemblies) { - ISet references = new HashSet(); + var item = new Mock(); + item.Setup(c => c.EvaluatedInclude).Returns(include); + item.As().Setup(c => c.Metadata.GetEvaluatedPropertyValueAsync(ProjectReference.TreatAsUsedProperty)) + .ReturnsAsync(evaluatedValue); - foreach ((string include, string evaluatedValue) in assemblies) + if (s_item is null && item is Mock packageItem) { - var item = new Mock(); - item.Setup(c => c.EvaluatedInclude).Returns(include); - item.As().Setup(c => c.Metadata.GetEvaluatedPropertyValueAsync(ProjectReference.TreatAsUsedProperty)) - .ReturnsAsync(evaluatedValue); - - if (s_item is null && item is Mock packageItem) - { - s_item = packageItem; - } - references.Add(item.Object); + s_item = packageItem; } - - return references.ToImmutableHashSet(); + references.Add(item.Object); } + + return references.ToImmutableHashSet(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/CSharp/RenamerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/CSharp/RenamerTests.cs index 9ec42db70e..f326bf3d03 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/CSharp/RenamerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/CSharp/RenamerTests.cs @@ -5,148 +5,147 @@ using Microsoft.VisualStudio.ProjectSystem.LanguageServices.CSharp; using Microsoft.VisualStudio.Settings; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename.CSharp +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename.CSharp; + +public class RenamerTests : RenamerTestsBase { - public class RenamerTests : RenamerTestsBase + protected override string ProjectFileExtension => "csproj"; + + [Theory] + [InlineData("class Foo{}", "Foo.cs", "Bar.cs")] + [InlineData("interface Foo { void m1(); void m2();}", "Foo.cs", "Bar.cs")] + [InlineData("delegate int Foo(string s);", "Foo.cs", "Bar.cs")] + [InlineData("partial class Foo {} partial class Foo {}", "Foo.cs", "Bar.cs")] + [InlineData("struct Foo { decimal price; string title; string author;}", "Foo.cs", "Bar.cs")] + [InlineData("enum Foo { None, enum1, enum2, enum3, enum4 };", "Foo.cs", "Bar.cs")] + [InlineData("namespace n1 {class Foo{}} namespace n2 {class Foo{}}", "Foo.cs", "Bar.cs")] + public async Task Rename_Symbol_Should_TriggerUserConfirmationAsync(string sourceCode, string oldFilePath, string newFilePath) + { + using var _ = SynchronizationContextUtil.Suppress(); + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); + var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.CSharp, settingsManagerService); + + bool checkBoxSelection; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out checkBoxSelection), Times.Once); + } + + [Theory] + [InlineData("class Foo{}", "Foo.cs", "Bar.cs")] + [InlineData("interface Foo { void m1(); void m2();}", "Foo.cs", "Bar.cs")] + [InlineData("delegate int Foo(string s);", "Foo.cs", "Bar.cs")] + [InlineData("partial class Foo {} partial class Foo {}", "Foo.cs", "Bar.cs")] + [InlineData("struct Foo { decimal price; string title; string author;}", "Foo.cs", "Bar.cs")] + [InlineData("enum Foo { None, enum1, enum2, enum3, enum4 };", "Foo.cs", "Bar.cs")] + [InlineData("namespace n1 {class Foo{}} namespace n2 {class Foo{}}", "Foo.cs", "Bar.cs")] + public async Task Rename_Symbol_ShouldNot_TriggerUserConfirmationAsync(string sourceCode, string oldFilePath, string newFilePath) { - protected override string ProjectFileExtension => "csproj"; - - [Theory] - [InlineData("class Foo{}", "Foo.cs", "Bar.cs")] - [InlineData("interface Foo { void m1(); void m2();}", "Foo.cs", "Bar.cs")] - [InlineData("delegate int Foo(string s);", "Foo.cs", "Bar.cs")] - [InlineData("partial class Foo {} partial class Foo {}", "Foo.cs", "Bar.cs")] - [InlineData("struct Foo { decimal price; string title; string author;}", "Foo.cs", "Bar.cs")] - [InlineData("enum Foo { None, enum1, enum2, enum3, enum4 };", "Foo.cs", "Bar.cs")] - [InlineData("namespace n1 {class Foo{}} namespace n2 {class Foo{}}", "Foo.cs", "Bar.cs")] - public async Task Rename_Symbol_Should_TriggerUserConfirmationAsync(string sourceCode, string oldFilePath, string newFilePath) - { - using var _ = SynchronizationContextUtil.Suppress(); - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); - var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.CSharp, settingsManagerService); - - bool checkBoxSelection; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out checkBoxSelection), Times.Once); - } - - [Theory] - [InlineData("class Foo{}", "Foo.cs", "Bar.cs")] - [InlineData("interface Foo { void m1(); void m2();}", "Foo.cs", "Bar.cs")] - [InlineData("delegate int Foo(string s);", "Foo.cs", "Bar.cs")] - [InlineData("partial class Foo {} partial class Foo {}", "Foo.cs", "Bar.cs")] - [InlineData("struct Foo { decimal price; string title; string author;}", "Foo.cs", "Bar.cs")] - [InlineData("enum Foo { None, enum1, enum2, enum3, enum4 };", "Foo.cs", "Bar.cs")] - [InlineData("namespace n1 {class Foo{}} namespace n2 {class Foo{}}", "Foo.cs", "Bar.cs")] - public async Task Rename_Symbol_ShouldNot_TriggerUserConfirmationAsync(string sourceCode, string oldFilePath, string newFilePath) - { - using var _ = SynchronizationContextUtil.Suppress(); - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); - var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(false); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.CSharp, settingsManagerService); - - bool checkBoxSelection; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out checkBoxSelection), Times.Never); - } - - [Theory] - [InlineData("class Foo{}", "Bar.cs", "Foo.cs")] - [InlineData("class Foo1{}", "Foo.cs", "Bar.cs")] - [InlineData("interface Foo1 { void m1(); void m2();}", "Foo.cs", "Bar.cs")] - [InlineData("delegate int Foo1(string s);", "Foo.cs", "Bar.cs")] - [InlineData("partial class Foo1 {} partial class Foo2 {}", "Foo.cs", "Bar.cs")] - [InlineData("struct Foo1 { decimal price; string title; string author;}", "Foo.cs", "Bar.cs")] - [InlineData("enum Foo1 { None, enum1, enum2, enum3, enum4 };", "Foo.cs", "Bar.cs")] - [InlineData("class Foo{}", "Bar.cs", "Foo`.cs")] - [InlineData("class Foo{}", "Bar.cs", "Foo@.cs")] - [InlineData("class Foo{}", "Foo.cs", "Foo.cs")] - [InlineData("class Foo{}", "Foo.cs", "Folder1\\Foo.cs")] - public async Task Rename_Symbol_Should_Not_HappenAsync(string sourceCode, string oldFilePath, string newFilePath) - { - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); - var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.CSharp, settingsManagerService).TimeoutAfter(TimeSpan.FromSeconds(30)); - - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - [Fact] - public async Task Rename_Symbol_Should_ExitEarlyInVSOnlineAsync() - { - string sourceCode = "class Foo { }"; - string oldFilePath = "Foo.cs"; - string newFilePath = "Bar.cs"; - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); - var vsOnlineService = IVsOnlineServicesFactory.Create(online: true); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.CSharp, settingsManagerService); - - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - [Theory] - [InlineData("Foo.cs", "Foo.txt")] - [InlineData("Foo.cs", "Foo.cs2")] - [InlineData("Foo.txt", "Foo.cs")] - public async Task Rename_Symbol_Should_ExitEarlyInFileExtensionChange(string oldFilePath, string newFilePath) - { - string sourceCode = "class Foo { }"; - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); - var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.CSharp, settingsManagerService); - bool disablePromptMessage; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - [Fact] - public async Task Rename_Symbol_Should_ExitEarlyWhenFileDoesNotChangeName() - { - using var _ = SynchronizationContextUtil.Suppress(); - - string sourceCode = "class Foo { }"; - string oldFilePath = "Foo.cs"; - string newFilePath = "FOO.cs"; - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); - var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.CSharp, settingsManagerService); - bool disablePromptMessage; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - private IVsService CreateSettingsManagerService(bool enableSymbolicRename) - { - var settingsManagerMock = new Mock(); - - settingsManagerMock.Setup(f => f.GetValueOrDefault("SolutionNavigator.EnableSymbolicRename", true)) - .Returns(enableSymbolicRename); - - return IVsServiceFactory.Create(settingsManagerMock.Object); - } + using var _ = SynchronizationContextUtil.Suppress(); + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); + var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(false); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.CSharp, settingsManagerService); + + bool checkBoxSelection; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out checkBoxSelection), Times.Never); + } + + [Theory] + [InlineData("class Foo{}", "Bar.cs", "Foo.cs")] + [InlineData("class Foo1{}", "Foo.cs", "Bar.cs")] + [InlineData("interface Foo1 { void m1(); void m2();}", "Foo.cs", "Bar.cs")] + [InlineData("delegate int Foo1(string s);", "Foo.cs", "Bar.cs")] + [InlineData("partial class Foo1 {} partial class Foo2 {}", "Foo.cs", "Bar.cs")] + [InlineData("struct Foo1 { decimal price; string title; string author;}", "Foo.cs", "Bar.cs")] + [InlineData("enum Foo1 { None, enum1, enum2, enum3, enum4 };", "Foo.cs", "Bar.cs")] + [InlineData("class Foo{}", "Bar.cs", "Foo`.cs")] + [InlineData("class Foo{}", "Bar.cs", "Foo@.cs")] + [InlineData("class Foo{}", "Foo.cs", "Foo.cs")] + [InlineData("class Foo{}", "Foo.cs", "Folder1\\Foo.cs")] + public async Task Rename_Symbol_Should_Not_HappenAsync(string sourceCode, string oldFilePath, string newFilePath) + { + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); + var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.CSharp, settingsManagerService).TimeoutAfter(TimeSpan.FromSeconds(30)); + + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task Rename_Symbol_Should_ExitEarlyInVSOnlineAsync() + { + string sourceCode = "class Foo { }"; + string oldFilePath = "Foo.cs"; + string newFilePath = "Bar.cs"; + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); + var vsOnlineService = IVsOnlineServicesFactory.Create(online: true); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.CSharp, settingsManagerService); + + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData("Foo.cs", "Foo.txt")] + [InlineData("Foo.cs", "Foo.cs2")] + [InlineData("Foo.txt", "Foo.cs")] + public async Task Rename_Symbol_Should_ExitEarlyInFileExtensionChange(string oldFilePath, string newFilePath) + { + string sourceCode = "class Foo { }"; + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); + var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.CSharp, settingsManagerService); + bool disablePromptMessage; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task Rename_Symbol_Should_ExitEarlyWhenFileDoesNotChangeName() + { + using var _ = SynchronizationContextUtil.Suppress(); + + string sourceCode = "class Foo { }"; + string oldFilePath = "Foo.cs"; + string newFilePath = "FOO.cs"; + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new CSharpSyntaxFactsService()); + var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.CSharp, settingsManagerService); + bool disablePromptMessage; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + private IVsService CreateSettingsManagerService(bool enableSymbolicRename) + { + var settingsManagerMock = new Mock(); + + settingsManagerMock.Setup(f => f.GetValueOrDefault("SolutionNavigator.EnableSymbolicRename", true)) + .Returns(enableSymbolicRename); + + return IVsServiceFactory.Create(settingsManagerMock.Object); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/RenamerTestsBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/RenamerTestsBase.cs index aed1ccfcad..3e7cce294a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/RenamerTestsBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/RenamerTestsBase.cs @@ -11,122 +11,121 @@ using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename; + +public abstract class RenamerTestsBase { - public abstract class RenamerTestsBase + protected abstract string ProjectFileExtension { get; } + + protected SolutionInfo InitializeWorkspace(ProjectId projectId, string fileName, string code, string language) { - protected abstract string ProjectFileExtension { get; } + var solutionId = SolutionId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + return SolutionInfo.Create( + solutionId, + VersionStamp.Create(), + projects: new ProjectInfo[] + { + ProjectInfo.Create( + id: projectId, + version: VersionStamp.Create(), + name: "Project1", + assemblyName: "Project1", + filePath: $@"C:\project1.{ProjectFileExtension}", + language: language, + documents: new DocumentInfo[] + { + DocumentInfo.Create( + documentId, + fileName, + loader: TextLoader.From(TextAndVersion.Create(SourceText.From(code), + VersionStamp.Create())), + filePath: fileName) + }) + }); + } - protected SolutionInfo InitializeWorkspace(ProjectId projectId, string fileName, string code, string language) + internal class TestRenamerProjectTreeActionHandler : RenamerProjectTreeActionHandler + { + public TestRenamerProjectTreeActionHandler( + UnconfiguredProject unconfiguredProject, + IUnconfiguredProjectVsServices projectVsServices, + [Import(typeof(VisualStudioWorkspace))]Lazy workspace, + IEnvironmentOptions environmentOptions, + IUserNotificationServices userNotificationServices, + IRoslynServices roslynServices, + IWaitIndicator waitService, + IVsOnlineServices vsOnlineServices, + [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, + IProjectThreadingService threadingService, + IVsUIService extensibility, + IVsService operationProgressService, + IVsService settingsManagerService) + : base(unconfiguredProject, projectVsServices, workspace, environmentOptions, userNotificationServices, roslynServices, waitService, + vsOnlineServices, projectAsynchronousTasksService, threadingService, extensibility, operationProgressService, settingsManagerService) { - var solutionId = SolutionId.CreateNewId(); - var documentId = DocumentId.CreateNewId(projectId); - return SolutionInfo.Create( - solutionId, - VersionStamp.Create(), - projects: new ProjectInfo[] - { - ProjectInfo.Create( - id: projectId, - version: VersionStamp.Create(), - name: "Project1", - assemblyName: "Project1", - filePath: $@"C:\project1.{ProjectFileExtension}", - language: language, - documents: new DocumentInfo[] - { - DocumentInfo.Create( - documentId, - fileName, - loader: TextLoader.From(TextAndVersion.Create(SourceText.From(code), - VersionStamp.Create())), - filePath: fileName) - }) - }); } - internal class TestRenamerProjectTreeActionHandler : RenamerProjectTreeActionHandler + protected override async Task CpsFileRenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) { - public TestRenamerProjectTreeActionHandler( - UnconfiguredProject unconfiguredProject, - IUnconfiguredProjectVsServices projectVsServices, - [Import(typeof(VisualStudioWorkspace))]Lazy workspace, - IEnvironmentOptions environmentOptions, - IUserNotificationServices userNotificationServices, - IRoslynServices roslynServices, - IWaitIndicator waitService, - IVsOnlineServices vsOnlineServices, - [Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService projectAsynchronousTasksService, - IProjectThreadingService threadingService, - IVsUIService extensibility, - IVsService operationProgressService, - IVsService settingsManagerService) - : base(unconfiguredProject, projectVsServices, workspace, environmentOptions, userNotificationServices, roslynServices, waitService, - vsOnlineServices, projectAsynchronousTasksService, threadingService, extensibility, operationProgressService, settingsManagerService) - { - } - - protected override async Task CpsFileRenameAsync(IProjectTreeActionHandlerContext context, IProjectTree node, string value) - { - await Task.CompletedTask; - } - - protected override async Task IsAutomationFunctionAsync() - { - return await Task.FromResult(false); - } + await Task.CompletedTask; } - internal async Task RenameAsync(string sourceCode, string oldFilePath, string newFilePath, - IUserNotificationServices userNotificationServices, - IRoslynServices roslynServices, - IVsOnlineServices vsOnlineServices, - string language, - IVsService settingsManagerService) + protected override async Task IsAutomationFunctionAsync() { - // Configure mocks. - var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: $@"C:\project1.{ProjectFileExtension}"); - var projectServices = IUnconfiguredProjectVsServicesFactory.Implement( - threadingServiceCreator: () => IProjectThreadingServiceFactory.Create(), - unconfiguredProjectCreator: () => unconfiguredProject); + return await Task.FromResult(false); + } + } + + internal async Task RenameAsync(string sourceCode, string oldFilePath, string newFilePath, + IUserNotificationServices userNotificationServices, + IRoslynServices roslynServices, + IVsOnlineServices vsOnlineServices, + string language, + IVsService settingsManagerService) + { + // Configure mocks. + var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: $@"C:\project1.{ProjectFileExtension}"); + var projectServices = IUnconfiguredProjectVsServicesFactory.Implement( + threadingServiceCreator: () => IProjectThreadingServiceFactory.Create(), + unconfiguredProjectCreator: () => unconfiguredProject); - using var ws = new AdhocWorkspace(); - ws.AddSolution(InitializeWorkspace(ProjectId.CreateNewId(), oldFilePath, sourceCode, language)); + using var ws = new AdhocWorkspace(); + ws.AddSolution(InitializeWorkspace(ProjectId.CreateNewId(), oldFilePath, sourceCode, language)); - var environmentOptionsFactory = IEnvironmentOptionsFactory.Implement((_, _, _, _) => true); - var waitIndicator = Mock.Of(); - var projectAsynchronousTasksService = IProjectAsynchronousTasksServiceFactory.Create(); - var projectThreadingService = IProjectThreadingServiceFactory.Create(); - var refactorNotifyService = Mock.Of(); - var extensibility = Mock.Of>(); - var operationProgressMock = Mock.Of>(); - var context = Mock.Of(); + var environmentOptionsFactory = IEnvironmentOptionsFactory.Implement((_, _, _, _) => true); + var waitIndicator = Mock.Of(); + var projectAsynchronousTasksService = IProjectAsynchronousTasksServiceFactory.Create(); + var projectThreadingService = IProjectThreadingServiceFactory.Create(); + var refactorNotifyService = Mock.Of(); + var extensibility = Mock.Of>(); + var operationProgressMock = Mock.Of>(); + var context = Mock.Of(); - var mockNode = new Mock(); - mockNode.SetupGet(x => x.FilePath).Returns(oldFilePath); - mockNode.SetupGet(x => x.IsFolder).Returns(false); - var node = mockNode.Object; + var mockNode = new Mock(); + mockNode.SetupGet(x => x.FilePath).Returns(oldFilePath); + mockNode.SetupGet(x => x.IsFolder).Returns(false); + var node = mockNode.Object; - // Construct the renamer. - var renamer = new TestRenamerProjectTreeActionHandler( - unconfiguredProject, - projectServices, - new Lazy(() => ws), - environmentOptionsFactory, - userNotificationServices, - roslynServices, - waitIndicator, - vsOnlineServices, - projectAsynchronousTasksService, - projectThreadingService, - extensibility, - operationProgressMock, - settingsManagerService); + // Construct the renamer. + var renamer = new TestRenamerProjectTreeActionHandler( + unconfiguredProject, + projectServices, + new Lazy(() => ws), + environmentOptionsFactory, + userNotificationServices, + roslynServices, + waitIndicator, + vsOnlineServices, + projectAsynchronousTasksService, + projectThreadingService, + extensibility, + operationProgressMock, + settingsManagerService); - // Perform the rename, with a timeout in case of hangs. - await renamer - .RenameAsync(context, node, newFilePath) - .TimeoutAfter(TimeSpan.FromSeconds(30)); - } + // Perform the rename, with a timeout in case of hangs. + await renamer + .RenameAsync(context, node, newFilePath) + .TimeoutAfter(TimeSpan.FromSeconds(30)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/VisualBasic/RenamerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/VisualBasic/RenamerTests.cs index c09f92b6d2..e9df9ba68a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/VisualBasic/RenamerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rename/VisualBasic/RenamerTests.cs @@ -5,244 +5,243 @@ using Microsoft.VisualStudio.ProjectSystem.LanguageServices.VisualBasic; using Microsoft.VisualStudio.Settings; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rename.VisualBasic; + +public class RenamerTests : RenamerTestsBase { - public class RenamerTests : RenamerTestsBase - { - protected override string ProjectFileExtension => "vbproj"; - - [Theory] - [InlineData("Bar.vb", "Foo.vb", - """ - Class Foo - End Class - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Class Foo1 - End Class - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Interface Foo1 - Sub m1() - Sub m2() - End Interface - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Delegate Function Foo1(s As String) As Integer - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Partial Class Foo1 - End Class - Partial Class Foo2 - End Class - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Structure Foo1 - Private author As String - End Structure - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Enum Foo1 - None - enum1 - End Enum - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Module Foo1 - End Module - """)] - [InlineData("Bar.vb", "Foo`.vb", - """ - Class Foo - End Class - """)] - [InlineData("Bar.vb", "Foo@.vb", - """ - Class Foo - End Class - """)] - [InlineData("Foo.vb", "Foo.vb", - """ - Class Foo - End Class - """)] - [InlineData("Foo.vb", "Folder1\\Foo.vb", - """ - Class Foo - End Class - """)] - // Change in case - [InlineData("Foo.vb", "foo.vb", - """ - Class Foo - End Class - """)] - [InlineData("Foo.vb", "Folder1\\foo.vb", - """ - Class Foo - End Class - """)] - public async Task Rename_Symbol_Should_Not_HappenAsync(string oldFilePath, string newFilePath, string sourceCode) - { - using var _ = SynchronizationContextUtil.Suppress(); - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); - var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.VisualBasic, settingsManagerService); - - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - [Theory] - [InlineData("Foo.vb", "Bar.vb", - """ - Class Foo - End Class - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Interface Foo - Sub m1() - Sub m2() - End Interface - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Delegate Function Foo(s As String) As Integer - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Partial Class Foo - End Class - Partial Class Foo - End Class - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Structure Foo - Private author As String - End Structure - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Enum Foo - None - enum1 - End Enum - """)] - [InlineData("Foo.vb", "Bar.vb", - """ - Namespace n1 + protected override string ProjectFileExtension => "vbproj"; + + [Theory] + [InlineData("Bar.vb", "Foo.vb", + """ + Class Foo + End Class + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Class Foo1 + End Class + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Interface Foo1 + Sub m1() + Sub m2() + End Interface + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Delegate Function Foo1(s As String) As Integer + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Partial Class Foo1 + End Class + Partial Class Foo2 + End Class + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Structure Foo1 + Private author As String + End Structure + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Enum Foo1 + None + enum1 + End Enum + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Module Foo1 + End Module + """)] + [InlineData("Bar.vb", "Foo`.vb", + """ Class Foo End Class - End Namespace - Namespace n2 + """)] + [InlineData("Bar.vb", "Foo@.vb", + """ Class Foo End Class - End Namespace - """)] - public async Task Rename_Symbol_Should_TriggerUserConfirmationAsync(string oldFilePath, string newFilePath, string sourceCode) - { - using var _ = SynchronizationContextUtil.Suppress(); - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); - var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.VisualBasic, settingsManagerService); - - bool checkBoxSelection; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out checkBoxSelection), Times.Once); - } - - [Fact] - public async Task Rename_Symbol_Should_ExitEarlyInVSOnlineAsync() - { - string sourceCode = + """)] + [InlineData("Foo.vb", "Foo.vb", """ Class Foo End Class - """; - string oldFilePath = "Foo.vb"; - string newFilePath = "Bar.vb"; - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); - var vsOnlineService = IVsOnlineServicesFactory.Create(online: true); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.VisualBasic, settingsManagerService); - - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - [Theory] - [InlineData("Foo.vb", "Foo.txt")] - [InlineData("Foo.vb", "Foo.vb2")] - [InlineData("Foo.txt", "Foo.vb")] - public async Task Rename_Symbol_Should_ExitEarlyInFileExtensionChange(string oldFilePath, string newFilePath) - { - string sourceCode = + """)] + [InlineData("Foo.vb", "Folder1\\Foo.vb", """ Class Foo End Class - """; - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); - var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.VisualBasic, settingsManagerService); - bool disablePromptMessage; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - [Fact] - public async Task Rename_Symbol_Should_ExitEarlyWhenFileDoesNotChangeName() - { - using var _ = SynchronizationContextUtil.Suppress(); - - string sourceCode = + """)] + // Change in case + [InlineData("Foo.vb", "foo.vb", + """ + Class Foo + End Class + """)] + [InlineData("Foo.vb", "Folder1\\foo.vb", """ Class Foo End Class - """; - string oldFilePath = "Foo.vb"; - string newFilePath = "FOO.vb"; - - var userNotificationServices = IUserNotificationServicesFactory.Create(); - var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); - var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); - var settingsManagerService = CreateSettingsManagerService(true); - - await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.VisualBasic, settingsManagerService); - bool disablePromptMessage; - Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); - Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); - } - - private IVsService CreateSettingsManagerService(bool enableSymbolicRename) - { - var settingsManagerMock = new Mock(); - - settingsManagerMock.Setup(f => f.GetValueOrDefault("SolutionNavigator.EnableSymbolicRename", true)) - .Returns(enableSymbolicRename); - - return IVsServiceFactory.Create(settingsManagerMock.Object); - } + """)] + public async Task Rename_Symbol_Should_Not_HappenAsync(string oldFilePath, string newFilePath, string sourceCode) + { + using var _ = SynchronizationContextUtil.Suppress(); + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); + var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.VisualBasic, settingsManagerService); + + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData("Foo.vb", "Bar.vb", + """ + Class Foo + End Class + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Interface Foo + Sub m1() + Sub m2() + End Interface + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Delegate Function Foo(s As String) As Integer + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Partial Class Foo + End Class + Partial Class Foo + End Class + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Structure Foo + Private author As String + End Structure + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Enum Foo + None + enum1 + End Enum + """)] + [InlineData("Foo.vb", "Bar.vb", + """ + Namespace n1 + Class Foo + End Class + End Namespace + Namespace n2 + Class Foo + End Class + End Namespace + """)] + public async Task Rename_Symbol_Should_TriggerUserConfirmationAsync(string oldFilePath, string newFilePath, string sourceCode) + { + using var _ = SynchronizationContextUtil.Suppress(); + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); + var vsOnlineServices = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineServices, LanguageNames.VisualBasic, settingsManagerService); + + bool checkBoxSelection; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out checkBoxSelection), Times.Once); + } + + [Fact] + public async Task Rename_Symbol_Should_ExitEarlyInVSOnlineAsync() + { + string sourceCode = + """ + Class Foo + End Class + """; + string oldFilePath = "Foo.vb"; + string newFilePath = "Bar.vb"; + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); + var vsOnlineService = IVsOnlineServicesFactory.Create(online: true); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.VisualBasic, settingsManagerService); + + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny()), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + [Theory] + [InlineData("Foo.vb", "Foo.txt")] + [InlineData("Foo.vb", "Foo.vb2")] + [InlineData("Foo.txt", "Foo.vb")] + public async Task Rename_Symbol_Should_ExitEarlyInFileExtensionChange(string oldFilePath, string newFilePath) + { + string sourceCode = + """ + Class Foo + End Class + """; + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); + var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.VisualBasic, settingsManagerService); + bool disablePromptMessage; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task Rename_Symbol_Should_ExitEarlyWhenFileDoesNotChangeName() + { + using var _ = SynchronizationContextUtil.Suppress(); + + string sourceCode = + """ + Class Foo + End Class + """; + string oldFilePath = "Foo.vb"; + string newFilePath = "FOO.vb"; + + var userNotificationServices = IUserNotificationServicesFactory.Create(); + var roslynServices = IRoslynServicesFactory.Implement(new VisualBasicSyntaxFactsService()); + var vsOnlineService = IVsOnlineServicesFactory.Create(online: false); + var settingsManagerService = CreateSettingsManagerService(true); + + await RenameAsync(sourceCode, oldFilePath, newFilePath, userNotificationServices, roslynServices, vsOnlineService, LanguageNames.VisualBasic, settingsManagerService); + bool disablePromptMessage; + Mock.Get(userNotificationServices).Verify(h => h.Confirm(It.IsAny(), out disablePromptMessage), Times.Never); + Mock.Get(roslynServices).Verify(h => h.ApplyChangesToSolution(It.IsAny(), It.IsAny()), Times.Never); + } + + private IVsService CreateSettingsManagerService(bool enableSymbolicRename) + { + var settingsManagerMock = new Mock(); + + settingsManagerMock.Setup(f => f.GetValueOrDefault("SolutionNavigator.EnableSymbolicRename", true)) + .Returns(enableSymbolicRename); + + return IVsServiceFactory.Create(settingsManagerMock.Object); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/DependencyRuleTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/DependencyRuleTests.cs index 4f4b843d03..6e6774212b 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/DependencyRuleTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/DependencyRuleTests.cs @@ -3,282 +3,281 @@ using System.Xml.Linq; using System.Xml.XPath; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +public sealed class DependencyRuleTests : XamlRuleTestBase { - public sealed class DependencyRuleTests : XamlRuleTestBase + [Theory] + [MemberData(nameof(GetResolvedDependenciesRules))] + public void VisibleEditableResolvedDependenciesMustHaveDataSource(string ruleName, string fullPath, Type assemblyExporterType) { - [Theory] - [MemberData(nameof(GetResolvedDependenciesRules))] - public void VisibleEditableResolvedDependenciesMustHaveDataSource(string ruleName, string fullPath, Type assemblyExporterType) - { - // Resolved rules get their data from design time targets. Any editable properties need a - // property-level data source that specifies the storage for that property as the project file - // so that changes made in the properties pane are reflected in the project file and vice versa. + // Resolved rules get their data from design time targets. Any editable properties need a + // property-level data source that specifies the storage for that property as the project file + // so that changes made in the properties pane are reflected in the project file and vice versa. - XElement rule = LoadXamlRule(fullPath); + XElement rule = LoadXamlRule(fullPath); - var itemType = rule - .Element(XName.Get("Rule.DataSource", MSBuildNamespace)) - ?.Element(XName.Get("DataSource", MSBuildNamespace)) - ?.Attribute("ItemType")?.Value; + var itemType = rule + .Element(XName.Get("Rule.DataSource", MSBuildNamespace)) + ?.Element(XName.Get("DataSource", MSBuildNamespace)) + ?.Attribute("ItemType")?.Value; - Assert.NotNull(itemType); + Assert.NotNull(itemType); - foreach (var property in GetProperties(rule)) - { - // Properties are visible and non-readonly by default - string visibleValue = property.Attribute("Visible")?.Value ?? "true"; - string readOnlyValue = property.Attribute("ReadOnly")?.Value ?? "false"; + foreach (var property in GetProperties(rule)) + { + // Properties are visible and non-readonly by default + string visibleValue = property.Attribute("Visible")?.Value ?? "true"; + string readOnlyValue = property.Attribute("ReadOnly")?.Value ?? "false"; - Assert.True(bool.TryParse(visibleValue, out bool visible)); - Assert.True(bool.TryParse(readOnlyValue, out bool readOnly)); + Assert.True(bool.TryParse(visibleValue, out bool visible)); + Assert.True(bool.TryParse(readOnlyValue, out bool readOnly)); - if (!visible || readOnly) - { - continue; - } + if (!visible || readOnly) + { + continue; + } - var dataSourceElementName = $"{property.Name.LocalName}.DataSource"; + var dataSourceElementName = $"{property.Name.LocalName}.DataSource"; - var dataSource = property - .Element(XName.Get(dataSourceElementName, MSBuildNamespace)) - ?.Element(XName.Get("DataSource", MSBuildNamespace)); + var dataSource = property + .Element(XName.Get(dataSourceElementName, MSBuildNamespace)) + ?.Element(XName.Get("DataSource", MSBuildNamespace)); - Assert.True(dataSource is not null, $"Resolved dependency rule {ruleName} has visible, non-readonly property {property.Attribute("Name")} with no {dataSourceElementName} value."); - Assert.Equal("False", dataSource.Attribute("HasConfigurationCondition")?.Value, StringComparer.OrdinalIgnoreCase); - Assert.Equal("ProjectFile", dataSource.Attribute("Persistence")?.Value, StringComparer.Ordinal); - Assert.Equal("AfterContext", dataSource.Attribute("SourceOfDefaultValue")?.Value, StringComparer.Ordinal); - Assert.Equal(itemType, dataSource.Attribute("ItemType")?.Value, StringComparer.Ordinal); - } + Assert.True(dataSource is not null, $"Resolved dependency rule {ruleName} has visible, non-readonly property {property.Attribute("Name")} with no {dataSourceElementName} value."); + Assert.Equal("False", dataSource.Attribute("HasConfigurationCondition")?.Value, StringComparer.OrdinalIgnoreCase); + Assert.Equal("ProjectFile", dataSource.Attribute("Persistence")?.Value, StringComparer.Ordinal); + Assert.Equal("AfterContext", dataSource.Attribute("SourceOfDefaultValue")?.Value, StringComparer.Ordinal); + Assert.Equal(itemType, dataSource.Attribute("ItemType")?.Value, StringComparer.Ordinal); } + } - [Theory] - [MemberData(nameof(GetResolvedDependenciesRules))] - public void ResolvedDependenciesRulesMustHaveOriginalItemSpecProperty(string ruleName, string fullPath, Type assemblyExporterType) - { - // All resolved dependency items have a corresponding 'original' item spec, which contains - // the value of the item produced by evaluation. + [Theory] + [MemberData(nameof(GetResolvedDependenciesRules))] + public void ResolvedDependenciesRulesMustHaveOriginalItemSpecProperty(string ruleName, string fullPath, Type assemblyExporterType) + { + // All resolved dependency items have a corresponding 'original' item spec, which contains + // the value of the item produced by evaluation. - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - var property = rule.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""OriginalItemSpec""]", namespaceManager); + var property = rule.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""OriginalItemSpec""]", namespaceManager); - Assert.NotNull(property); - Assert.Equal(3, property.Attributes().Count()); - Assert.Equal("OriginalItemSpec", property.Attribute("Name")?.Value, StringComparer.Ordinal); - Assert.Equal("False", property.Attribute("Visible")?.Value, StringComparer.OrdinalIgnoreCase); - Assert.Equal("True", property.Attribute("ReadOnly")?.Value, StringComparer.OrdinalIgnoreCase); - } + Assert.NotNull(property); + Assert.Equal(3, property.Attributes().Count()); + Assert.Equal("OriginalItemSpec", property.Attribute("Name")?.Value, StringComparer.Ordinal); + Assert.Equal("False", property.Attribute("Visible")?.Value, StringComparer.OrdinalIgnoreCase); + Assert.Equal("True", property.Attribute("ReadOnly")?.Value, StringComparer.OrdinalIgnoreCase); + } - [Theory] - [MemberData(nameof(GetDependenciesRules))] - public void DependenciesRulesMustHaveVisibleProperty(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetDependenciesRules))] + public void DependenciesRulesMustHaveVisibleProperty(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - var property = rule.XPathSelectElement(@"/msb:Rule/msb:BoolProperty[@Name=""Visible""]", namespaceManager); + var property = rule.XPathSelectElement(@"/msb:Rule/msb:BoolProperty[@Name=""Visible""]", namespaceManager); - Assert.NotNull(property); - Assert.Equal(3, property.Attributes().Count()); - Assert.Equal("Visible", property.Attribute("Name")?.Value, StringComparer.Ordinal); - Assert.Equal("False", property.Attribute("Visible")?.Value, StringComparer.OrdinalIgnoreCase); - Assert.Equal("True", property.Attribute("ReadOnly")?.Value, StringComparer.OrdinalIgnoreCase); - } + Assert.NotNull(property); + Assert.Equal(3, property.Attributes().Count()); + Assert.Equal("Visible", property.Attribute("Name")?.Value, StringComparer.Ordinal); + Assert.Equal("False", property.Attribute("Visible")?.Value, StringComparer.OrdinalIgnoreCase); + Assert.Equal("True", property.Attribute("ReadOnly")?.Value, StringComparer.OrdinalIgnoreCase); + } - [Theory] - [MemberData(nameof(GetDependenciesRules))] - public void DependenciesRulesMustHaveIsImplicitlyDefinedProperty(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetDependenciesRules))] + public void DependenciesRulesMustHaveIsImplicitlyDefinedProperty(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - var property = rule.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""IsImplicitlyDefined""]", namespaceManager); + var property = rule.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""IsImplicitlyDefined""]", namespaceManager); - Assert.NotNull(property); - Assert.Equal(3, property.Attributes().Count()); - Assert.Equal("IsImplicitlyDefined", property.Attribute("Name")?.Value, StringComparer.Ordinal); - Assert.Equal("False", property.Attribute("Visible")?.Value, StringComparer.OrdinalIgnoreCase); - Assert.Equal("True", property.Attribute("ReadOnly")?.Value, StringComparer.OrdinalIgnoreCase); - } + Assert.NotNull(property); + Assert.Equal(3, property.Attributes().Count()); + Assert.Equal("IsImplicitlyDefined", property.Attribute("Name")?.Value, StringComparer.Ordinal); + Assert.Equal("False", property.Attribute("Visible")?.Value, StringComparer.OrdinalIgnoreCase); + Assert.Equal("True", property.Attribute("ReadOnly")?.Value, StringComparer.OrdinalIgnoreCase); + } - [Theory] - [MemberData(nameof(GetDependenciesRules))] - public void DependenciesRulesDescriptionHaveCorrectSuffix(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath); + [Theory] + [MemberData(nameof(GetDependenciesRules))] + public void DependenciesRulesDescriptionHaveCorrectSuffix(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); - Assert.EndsWith(" Reference Properties", rule.Attribute("Description")?.Value); - } + Assert.EndsWith(" Reference Properties", rule.Attribute("Description")?.Value); + } - [Theory] - [MemberData(nameof(GetDependenciesRules))] - public void DependenciesRulesDisplayNameHaveCorrectSuffix(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath); + [Theory] + [MemberData(nameof(GetDependenciesRules))] + public void DependenciesRulesDisplayNameHaveCorrectSuffix(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); - Assert.EndsWith(" Reference", rule.Attribute("DisplayName")?.Value); - } + Assert.EndsWith(" Reference", rule.Attribute("DisplayName")?.Value); + } - [Theory] - [MemberData(nameof(GetUnresolvedDependenciesRules))] - public void UnresolvedDependenciesRulesMustNotHaveOriginalItemSpec(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetUnresolvedDependenciesRules))] + public void UnresolvedDependenciesRulesMustNotHaveOriginalItemSpec(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - XElement? property = rule.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""OriginalItemSpec""]", namespaceManager); + XElement? property = rule.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""OriginalItemSpec""]", namespaceManager); - Assert.Null(property); - } + Assert.Null(property); + } - [Theory] - [MemberData(nameof(GetUnresolvedDependenciesRules))] - public void UnresolvedRulesDataSources(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetUnresolvedDependenciesRules))] + public void UnresolvedRulesDataSources(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - XElement? dataSource = rule.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); + XElement? dataSource = rule.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); - Assert.NotNull(dataSource); + Assert.NotNull(dataSource); - Assert.Null(dataSource.Attribute("MSBuildTarget")); // Must come from evaluation - Assert.Null(dataSource.Attribute("SourceType")); // Must come from evaluation + Assert.Null(dataSource.Attribute("MSBuildTarget")); // Must come from evaluation + Assert.Null(dataSource.Attribute("SourceType")); // Must come from evaluation - var hasConfigurationCondition = dataSource.Attribute("HasConfigurationCondition"); - Assert.NotNull(hasConfigurationCondition); - Assert.Equal("False", hasConfigurationCondition.Value, StringComparer.OrdinalIgnoreCase); + var hasConfigurationCondition = dataSource.Attribute("HasConfigurationCondition"); + Assert.NotNull(hasConfigurationCondition); + Assert.Equal("False", hasConfigurationCondition.Value, StringComparer.OrdinalIgnoreCase); - var itemType = dataSource.Attribute("ItemType"); - Assert.NotNull(itemType); - Assert.False(string.IsNullOrEmpty(itemType.Value)); + var itemType = dataSource.Attribute("ItemType"); + Assert.NotNull(itemType); + Assert.False(string.IsNullOrEmpty(itemType.Value)); - var persistence = dataSource.Attribute("Persistence"); - Assert.NotNull(persistence); - Assert.Equal("ProjectFile", persistence.Value, StringComparer.Ordinal); + var persistence = dataSource.Attribute("Persistence"); + Assert.NotNull(persistence); + Assert.Equal("ProjectFile", persistence.Value, StringComparer.Ordinal); - var sourceOfDefaultValue = dataSource.Attribute("SourceOfDefaultValue"); - Assert.NotNull(sourceOfDefaultValue); - Assert.Equal("AfterContext", sourceOfDefaultValue.Value, StringComparer.Ordinal); + var sourceOfDefaultValue = dataSource.Attribute("SourceOfDefaultValue"); + Assert.NotNull(sourceOfDefaultValue); + Assert.Equal("AfterContext", sourceOfDefaultValue.Value, StringComparer.Ordinal); - Assert.Equal(4, dataSource.Attributes().Count()); - } + Assert.Equal(4, dataSource.Attributes().Count()); + } - [Theory] - [MemberData(nameof(GetResolvedDependenciesRules))] - public void ResolvedRulesDataSources(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetResolvedDependenciesRules))] + public void ResolvedRulesDataSources(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - XElement? dataSource = rule.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); + XElement? dataSource = rule.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); - Assert.NotNull(dataSource); + Assert.NotNull(dataSource); - var hasConfigurationCondition = dataSource.Attribute("HasConfigurationCondition"); - Assert.NotNull(hasConfigurationCondition); - Assert.Equal("False", hasConfigurationCondition.Value, StringComparer.OrdinalIgnoreCase); + var hasConfigurationCondition = dataSource.Attribute("HasConfigurationCondition"); + Assert.NotNull(hasConfigurationCondition); + Assert.Equal("False", hasConfigurationCondition.Value, StringComparer.OrdinalIgnoreCase); - var itemType = dataSource.Attribute("ItemType"); - Assert.NotNull(itemType); - Assert.False(string.IsNullOrEmpty(itemType.Value)); + var itemType = dataSource.Attribute("ItemType"); + Assert.NotNull(itemType); + Assert.False(string.IsNullOrEmpty(itemType.Value)); - var msBuildTarget = dataSource.Attribute("MSBuildTarget"); - Assert.NotNull(msBuildTarget); - Assert.False(string.IsNullOrEmpty(msBuildTarget.Value)); + var msBuildTarget = dataSource.Attribute("MSBuildTarget"); + Assert.NotNull(msBuildTarget); + Assert.False(string.IsNullOrEmpty(msBuildTarget.Value)); - var persistence = dataSource.Attribute("Persistence"); - Assert.NotNull(persistence); - Assert.Equal("ResolvedReference", persistence.Value, StringComparer.Ordinal); + var persistence = dataSource.Attribute("Persistence"); + Assert.NotNull(persistence); + Assert.Equal("ResolvedReference", persistence.Value, StringComparer.Ordinal); - var sourceOfDefaultValue = dataSource.Attribute("SourceOfDefaultValue"); - Assert.NotNull(sourceOfDefaultValue); - Assert.Equal("AfterContext", sourceOfDefaultValue.Value, StringComparer.Ordinal); + var sourceOfDefaultValue = dataSource.Attribute("SourceOfDefaultValue"); + Assert.NotNull(sourceOfDefaultValue); + Assert.Equal("AfterContext", sourceOfDefaultValue.Value, StringComparer.Ordinal); - var sourceType = dataSource.Attribute("SourceType"); - Assert.NotNull(sourceType); - Assert.Equal("TargetResults", sourceType.Value, StringComparer.Ordinal); + var sourceType = dataSource.Attribute("SourceType"); + Assert.NotNull(sourceType); + Assert.Equal("TargetResults", sourceType.Value, StringComparer.Ordinal); - Assert.Equal(6, dataSource.Attributes().Count()); - } + Assert.Equal(6, dataSource.Attributes().Count()); + } - [Theory] - [MemberData(nameof(GetResolvedAndUnresolvedDependencyRulePairs))] - public void ResolvedAndUnresolvedDependencyRulesHaveSameDisplayName(string unresolvedName, string resolvedName, string unresolvedPath, string resolvedPath) - { - XElement unresolvedRule = LoadXamlRule(unresolvedPath); - XElement resolvedRule = LoadXamlRule(resolvedPath); + [Theory] + [MemberData(nameof(GetResolvedAndUnresolvedDependencyRulePairs))] + public void ResolvedAndUnresolvedDependencyRulesHaveSameDisplayName(string unresolvedName, string resolvedName, string unresolvedPath, string resolvedPath) + { + XElement unresolvedRule = LoadXamlRule(unresolvedPath); + XElement resolvedRule = LoadXamlRule(resolvedPath); - Assert.Equal(unresolvedRule.Attribute("DisplayName")?.Value, resolvedRule.Attribute("DisplayName")?.Value); - } + Assert.Equal(unresolvedRule.Attribute("DisplayName")?.Value, resolvedRule.Attribute("DisplayName")?.Value); + } - [Theory] - [MemberData(nameof(GetResolvedAndUnresolvedDependencyRulePairs))] - public void ResolvedAndUnresolvedDependencyRulesHaveSameDescription(string unresolvedName, string resolvedName, string unresolvedPath, string resolvedPath) - { - XElement unresolvedRule = LoadXamlRule(unresolvedPath); - XElement resolvedRule = LoadXamlRule(resolvedPath); + [Theory] + [MemberData(nameof(GetResolvedAndUnresolvedDependencyRulePairs))] + public void ResolvedAndUnresolvedDependencyRulesHaveSameDescription(string unresolvedName, string resolvedName, string unresolvedPath, string resolvedPath) + { + XElement unresolvedRule = LoadXamlRule(unresolvedPath); + XElement resolvedRule = LoadXamlRule(resolvedPath); - Assert.Equal(unresolvedRule.Attribute("Description")?.Value, resolvedRule.Attribute("Description")?.Value); - } + Assert.Equal(unresolvedRule.Attribute("Description")?.Value, resolvedRule.Attribute("Description")?.Value); + } - [Theory] - [MemberData(nameof(GetResolvedAndUnresolvedDependencyRulePairs))] - public void ResolvedAndUnresolvedDependencyRulesHaveSameVisibleProperties(string unresolvedName, string resolvedName, string unresolvedPath, string resolvedPath) - { - var unresolvedPropertyByName = GetVisibleProperties(LoadXamlRule(unresolvedPath)).ToDictionary(prop => prop.Attribute("Name").Value); - var resolvedPropertyByName = GetVisibleProperties(LoadXamlRule(resolvedPath)).ToDictionary(prop => prop.Attribute("Name").Value); + [Theory] + [MemberData(nameof(GetResolvedAndUnresolvedDependencyRulePairs))] + public void ResolvedAndUnresolvedDependencyRulesHaveSameVisibleProperties(string unresolvedName, string resolvedName, string unresolvedPath, string resolvedPath) + { + var unresolvedPropertyByName = GetVisibleProperties(LoadXamlRule(unresolvedPath)).ToDictionary(prop => prop.Attribute("Name").Value); + var resolvedPropertyByName = GetVisibleProperties(LoadXamlRule(resolvedPath)).ToDictionary(prop => prop.Attribute("Name").Value); - var missingInUnresolved = resolvedPropertyByName.Keys.Except(unresolvedPropertyByName.Keys).ToList(); - var missingInResolved = unresolvedPropertyByName.Keys.Except(resolvedPropertyByName.Keys).ToList(); + var missingInUnresolved = resolvedPropertyByName.Keys.Except(unresolvedPropertyByName.Keys).ToList(); + var missingInResolved = unresolvedPropertyByName.Keys.Except(resolvedPropertyByName.Keys).ToList(); - Assert.True(missingInUnresolved.Count == 0, "Resolved properties not found in unresolved: " + string.Join(", ", missingInUnresolved)); - Assert.True(missingInResolved.Count == 0, "Unresolved properties not found in resolved: " + string.Join(", ", missingInResolved)); - Assert.Equal(unresolvedPropertyByName.Count, resolvedPropertyByName.Count); // should be redundant given the above two checks + Assert.True(missingInUnresolved.Count == 0, "Resolved properties not found in unresolved: " + string.Join(", ", missingInUnresolved)); + Assert.True(missingInResolved.Count == 0, "Unresolved properties not found in resolved: " + string.Join(", ", missingInResolved)); + Assert.Equal(unresolvedPropertyByName.Count, resolvedPropertyByName.Count); // should be redundant given the above two checks - foreach ((string name, XElement resolved) in resolvedPropertyByName) - { - XElement unresolved = unresolvedPropertyByName[name]; + foreach ((string name, XElement resolved) in resolvedPropertyByName) + { + XElement unresolved = unresolvedPropertyByName[name]; - Assert.Equal(resolved.Attribute("Description")?.Value, unresolved.Attribute("Description")?.Value); - Assert.Equal(resolved.Attribute("DisplayName")?.Value, unresolved.Attribute("DisplayName")?.Value); - Assert.True(string.Equals(resolved.Attribute("ReadOnly")?.Value, unresolved.Attribute("ReadOnly")?.Value), - $"ReadOnly attribute for property {name} differs between unresolved/resolved rules"); - } + Assert.Equal(resolved.Attribute("Description")?.Value, unresolved.Attribute("Description")?.Value); + Assert.Equal(resolved.Attribute("DisplayName")?.Value, unresolved.Attribute("DisplayName")?.Value); + Assert.True(string.Equals(resolved.Attribute("ReadOnly")?.Value, unresolved.Attribute("ReadOnly")?.Value), + $"ReadOnly attribute for property {name} differs between unresolved/resolved rules"); } + } - public static IEnumerable GetUnresolvedDependenciesRules() - { - return Project(GetRules("Dependencies") - .Where(rule => rule.Path.IndexOf("Resolved", StringComparisons.Paths) == -1)); - } + public static IEnumerable GetUnresolvedDependenciesRules() + { + return Project(GetRules("Dependencies") + .Where(rule => rule.Path.IndexOf("Resolved", StringComparisons.Paths) == -1)); + } - public static IEnumerable GetResolvedDependenciesRules() - { - return Project(GetRules("Dependencies") - .Where(rule => rule.Path.IndexOf("Resolved", StringComparisons.Paths) != -1)); - } + public static IEnumerable GetResolvedDependenciesRules() + { + return Project(GetRules("Dependencies") + .Where(rule => rule.Path.IndexOf("Resolved", StringComparisons.Paths) != -1)); + } - public static IEnumerable GetDependenciesRules() - { - return Project(GetRules("Dependencies")); - } + public static IEnumerable GetDependenciesRules() + { + return Project(GetRules("Dependencies")); + } - public static IEnumerable GetResolvedAndUnresolvedDependencyRulePairs() - { - var rules = GetRules("Dependencies").ToList(); + public static IEnumerable GetResolvedAndUnresolvedDependencyRulePairs() + { + var rules = GetRules("Dependencies").ToList(); - var unresolvedPaths = rules.Select(rule => rule.Path).Where(path => path.IndexOf("Resolved", StringComparisons.Paths) == -1).ToHashSet(StringComparer.Ordinal); - var resolvedPaths = rules.Select(rule => rule.Path).Where(path => path.IndexOf("Resolved", StringComparisons.Paths) != -1).ToHashSet(StringComparer.Ordinal); + var unresolvedPaths = rules.Select(rule => rule.Path).Where(path => path.IndexOf("Resolved", StringComparisons.Paths) == -1).ToHashSet(StringComparer.Ordinal); + var resolvedPaths = rules.Select(rule => rule.Path).Where(path => path.IndexOf("Resolved", StringComparisons.Paths) != -1).ToHashSet(StringComparer.Ordinal); - Assert.Equal(resolvedPaths.Count, unresolvedPaths.Count); + Assert.Equal(resolvedPaths.Count, unresolvedPaths.Count); - foreach (string unresolvedPath in unresolvedPaths) - { - string unresolvedName = Path.GetFileNameWithoutExtension(unresolvedPath); - string resolvedName = "Resolved" + unresolvedName; - string resolvedPath = Path.Combine(Path.GetDirectoryName(unresolvedPath), resolvedName + ".xaml"); + foreach (string unresolvedPath in unresolvedPaths) + { + string unresolvedName = Path.GetFileNameWithoutExtension(unresolvedPath); + string resolvedName = "Resolved" + unresolvedName; + string resolvedPath = Path.Combine(Path.GetDirectoryName(unresolvedPath), resolvedName + ".xaml"); - Assert.Contains(resolvedPath, resolvedPaths); + Assert.Contains(resolvedPath, resolvedPaths); - yield return new object[] { unresolvedName, resolvedName, unresolvedPath, resolvedPath }; - } + yield return new object[] { unresolvedName, resolvedName, unresolvedPath, resolvedPath }; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ExportedRuleTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ExportedRuleTests.cs index f8f860160e..153ef5dd5e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ExportedRuleTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ExportedRuleTests.cs @@ -4,145 +4,144 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.ProjectSystem.Rules; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +public sealed class RuleExporterTests : XamlRuleTestBase { - public sealed class RuleExporterTests : XamlRuleTestBase + [Theory(Skip = "Waiting on all rules to be embedded")] + [MemberData(nameof(GetAllRules))] + public void AllRulesMustBeExported(string name, string fullPath, Type assemblyExporterType) { - [Theory(Skip = "Waiting on all rules to be embedded")] - [MemberData(nameof(GetAllRules))] - public void AllRulesMustBeExported(string name, string fullPath, Type assemblyExporterType) - { - name = name.Replace(".", ""); + name = name.Replace(".", ""); - MemberInfo member = typeof(VSRuleExporter).GetField(name) ?? typeof(RuleExporter).GetField(name); + MemberInfo member = typeof(VSRuleExporter).GetField(name) ?? typeof(RuleExporter).GetField(name); - Assert.True(member is not null, $"Rule '{fullPath}' has not been exported by {nameof(RuleExporter)} or {nameof(VSRuleExporter)}. Export this rule from a member called \"{name}\"."); - } + Assert.True(member is not null, $"Rule '{fullPath}' has not been exported by {nameof(RuleExporter)} or {nameof(VSRuleExporter)}. Export this rule from a member called \"{name}\"."); + } - [Theory] - [MemberData(nameof(GetAllExportedMembers))] - public void ExportedRulesMustBeStaticFields(MemberInfo member) - { - Assert.True(member is FieldInfo { IsStatic: true }, $"'{GetTypeQualifiedName(member)}' must be a static field."); - } + [Theory] + [MemberData(nameof(GetAllExportedMembers))] + public void ExportedRulesMustBeStaticFields(MemberInfo member) + { + Assert.True(member is FieldInfo { IsStatic: true }, $"'{GetTypeQualifiedName(member)}' must be a static field."); + } - [Theory] - [MemberData(nameof(GetAllExportedMembers))] - public void ExportedRulesMustBeMarkedWithAppliesTo(MemberInfo member) - { - var attribute = member.GetCustomAttribute(); + [Theory] + [MemberData(nameof(GetAllExportedMembers))] + public void ExportedRulesMustBeMarkedWithAppliesTo(MemberInfo member) + { + var attribute = member.GetCustomAttribute(); - Assert.True(attribute is not null, $"'{GetTypeQualifiedName(member)}' must be marked with [AppliesTo]"); - } + Assert.True(attribute is not null, $"'{GetTypeQualifiedName(member)}' must be marked with [AppliesTo]"); + } - [Theory] - [MemberData(nameof(GetAllExportedMembers))] - public void ExportedRulesMustBeMarkedWithOrder(MemberInfo member) - { - var attribute = member.GetCustomAttribute(); + [Theory] + [MemberData(nameof(GetAllExportedMembers))] + public void ExportedRulesMustBeMarkedWithOrder(MemberInfo member) + { + var attribute = member.GetCustomAttribute(); - Assert.True(attribute?.OrderPrecedence == Order.Default, $"'{GetTypeQualifiedName(member)}' must be marked with [Order(Order.Default)]"); - } + Assert.True(attribute?.OrderPrecedence == Order.Default, $"'{GetTypeQualifiedName(member)}' must be marked with [Order(Order.Default)]"); + } - [Theory] - [MemberData(nameof(GetAllExportedMembers))] - public void ExportedFieldsMustEndInRule(MemberInfo member) - { - Assert.True(member.Name.EndsWith("Rule"), $"'{GetTypeQualifiedName(member)}' must be end in 'Rule' so that the '[ExportRule(nameof(RuleName))]' expression refers to the rule itself"); - } + [Theory] + [MemberData(nameof(GetAllExportedMembers))] + public void ExportedFieldsMustEndInRule(MemberInfo member) + { + Assert.True(member.Name.EndsWith("Rule"), $"'{GetTypeQualifiedName(member)}' must be end in 'Rule' so that the '[ExportRule(nameof(RuleName))]' expression refers to the rule itself"); + } + + [Theory] + [MemberData(nameof(GetAllExportedMembersWithDeclaringType))] + public void MembersInSameTypeMustBeMarkedWithSameAppliesTo(Type declaringType, IEnumerable members) + { + string? appliesTo = null; - [Theory] - [MemberData(nameof(GetAllExportedMembersWithDeclaringType))] - public void MembersInSameTypeMustBeMarkedWithSameAppliesTo(Type declaringType, IEnumerable members) + foreach (MemberInfo member in members) { - string? appliesTo = null; + var attribute = member.GetCustomAttribute(); + if (attribute is null) + continue; // Another test will catch this - foreach (MemberInfo member in members) + if (appliesTo is null) { - var attribute = member.GetCustomAttribute(); - if (attribute is null) - continue; // Another test will catch this - - if (appliesTo is null) - { - appliesTo = attribute.AppliesTo; - } - else - { - Assert.True(appliesTo == attribute.AppliesTo, $"{declaringType}'s member must be all have the same value for [AppliesTo]"); - } + appliesTo = attribute.AppliesTo; } - } - - [Theory] - [MemberData(nameof(GetAllExportedMembersWithAttribute))] - public void BrowseObjectsMustBeInBrowseObjectContext(MemberInfo member, ExportPropertyXamlRuleDefinitionAttribute attribute) - { - if (!member.Name.Contains("BrowseObject")) - return; - - foreach (string context in attribute.Context.Split(';')) + else { - if (context == PropertyPageContexts.BrowseObject) - return; + Assert.True(appliesTo == attribute.AppliesTo, $"{declaringType}'s member must be all have the same value for [AppliesTo]"); } - - Assert.True(false, $"'{GetTypeQualifiedName(member)}' must live in the PropertyPageContexts.BrowseObject context."); } + } - [Theory] - [MemberData(nameof(GetAllExportedMembersWithAttribute))] - public void ExportedRulesMustExist(MemberInfo member, ExportPropertyXamlRuleDefinitionAttribute attribute) - { - Assert.NotNull(attribute); - - // HERE BE DRAGONS - // Note the following are *not* equivalent: - // Assembly.Load(assemblyNameString) - // Assembly.Load(new AssemblyName(assemblyNameString)) - // The first will accept certain malformed assembly names that the second does not, - // and will successfully load the assembly where the second throws an exception. - // CPS uses the second form when loading assemblies to extract embedded XAML, and - // so we must do the same in this test. - var assemblyName = new AssemblyName(attribute.XamlResourceAssemblyName); - var assembly = Assembly.Load(assemblyName); - - using Stream stream = assembly.GetManifestResourceStream(attribute.XamlResourceStreamName); - - Assert.True(stream is not null, $"The rule '{attribute.XamlResourceStreamName}' indicated by '{GetTypeQualifiedName(member)}' does not exist in assembly '{attribute.XamlResourceAssemblyName}'."); - } + [Theory] + [MemberData(nameof(GetAllExportedMembersWithAttribute))] + public void BrowseObjectsMustBeInBrowseObjectContext(MemberInfo member, ExportPropertyXamlRuleDefinitionAttribute attribute) + { + if (!member.Name.Contains("BrowseObject")) + return; - private static string GetTypeQualifiedName(MemberInfo member) + foreach (string context in attribute.Context.Split(';')) { - return $"{member.DeclaringType.Name}.{member.Name}"; + if (context == PropertyPageContexts.BrowseObject) + return; } - public static IEnumerable GetAllExportedMembers() - { - return RuleServices.GetAllExportedMembers().Select(member => new[] { member }); - } + Assert.True(false, $"'{GetTypeQualifiedName(member)}' must live in the PropertyPageContexts.BrowseObject context."); + } - public static IEnumerable GetAllExportedMembersWithAttribute() - { - foreach (MemberInfo member in RuleServices.GetAllExportedMembers()) - { - Attribute attribute = member.GetCustomAttribute(); + [Theory] + [MemberData(nameof(GetAllExportedMembersWithAttribute))] + public void ExportedRulesMustExist(MemberInfo member, ExportPropertyXamlRuleDefinitionAttribute attribute) + { + Assert.NotNull(attribute); + + // HERE BE DRAGONS + // Note the following are *not* equivalent: + // Assembly.Load(assemblyNameString) + // Assembly.Load(new AssemblyName(assemblyNameString)) + // The first will accept certain malformed assembly names that the second does not, + // and will successfully load the assembly where the second throws an exception. + // CPS uses the second form when loading assemblies to extract embedded XAML, and + // so we must do the same in this test. + var assemblyName = new AssemblyName(attribute.XamlResourceAssemblyName); + var assembly = Assembly.Load(assemblyName); + + using Stream stream = assembly.GetManifestResourceStream(attribute.XamlResourceStreamName); + + Assert.True(stream is not null, $"The rule '{attribute.XamlResourceStreamName}' indicated by '{GetTypeQualifiedName(member)}' does not exist in assembly '{attribute.XamlResourceAssemblyName}'."); + } - yield return new object[] { member, attribute }; - } - } + private static string GetTypeQualifiedName(MemberInfo member) + { + return $"{member.DeclaringType.Name}.{member.Name}"; + } - public static IEnumerable GetAllExportedMembersWithDeclaringType() + public static IEnumerable GetAllExportedMembers() + { + return RuleServices.GetAllExportedMembers().Select(member => new[] { member }); + } + + public static IEnumerable GetAllExportedMembersWithAttribute() + { + foreach (MemberInfo member in RuleServices.GetAllExportedMembers()) { - foreach (Type type in RuleServices.GetRuleExporterTypes()) - { - yield return new object[] { type, RuleServices.GetDeclaredMembers(type) }; - } + Attribute attribute = member.GetCustomAttribute(); + + yield return new object[] { member, attribute }; } + } - public static IEnumerable GetAllRules() + public static IEnumerable GetAllExportedMembersWithDeclaringType() + { + foreach (Type type in RuleServices.GetRuleExporterTypes()) { - return Project(GetRules(suffix: string.Empty, recursive: true)); + yield return new object[] { type, RuleServices.GetDeclaredMembers(type) }; } } + + public static IEnumerable GetAllRules() + { + return Project(GetRules(suffix: string.Empty, recursive: true)); + } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ItemRuleTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ItemRuleTests.cs index c996d1e5b8..84dd58f60a 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ItemRuleTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ItemRuleTests.cs @@ -3,250 +3,249 @@ using System.Xml.Linq; using System.Xml.XPath; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +public sealed class ItemRuleTests : XamlRuleTestBase { - public sealed class ItemRuleTests : XamlRuleTestBase + [Theory] + [MemberData(nameof(GetFileItemRules))] + public void FileRulesShouldMatchNone(string ruleName, string fullPath, Type assemblyExporterType) { - [Theory] - [MemberData(nameof(GetFileItemRules))] - public void FileRulesShouldMatchNone(string ruleName, string fullPath, Type assemblyExporterType) + // Special case for Folder rule which hasn't been split yet, but is in the Items folder. But also its completely different. + if (ruleName.Equals("Folder", StringComparison.Ordinal)) { - // Special case for Folder rule which hasn't been split yet, but is in the Items folder. But also its completely different. - if (ruleName.Equals("Folder", StringComparison.Ordinal)) - { - return; - } - - // Special case for PackageVersion, which is an item but not a file so none of the properties on "None" are actually - // relevant. - if (ruleName.Equals("PackageVersion", StringComparison.Ordinal)) - { - return; - } + return; + } - // No need to check None against None - if (ruleName.Equals("None", StringComparison.Ordinal)) - { - return; - } + // Special case for PackageVersion, which is an item but not a file so none of the properties on "None" are actually + // relevant. + if (ruleName.Equals("PackageVersion", StringComparison.Ordinal)) + { + return; + } - // F# has its own overrides which we can skip - if (ruleName.EndsWith(".FSharp", StringComparison.Ordinal)) - { - return; - } + // No need to check None against None + if (ruleName.Equals("None", StringComparison.Ordinal)) + { + return; + } - string noneFile = Path.Combine(fullPath, "..", "None.xaml"); + // F# has its own overrides which we can skip + if (ruleName.EndsWith(".FSharp", StringComparison.Ordinal)) + { + return; + } - XElement none = LoadXamlRule(noneFile, out var namespaceManager); - XElement rule = LoadXamlRule(fullPath); + string noneFile = Path.Combine(fullPath, "..", "None.xaml"); - // First fix up the Name as we know they'll differ. - rule.Attribute("Name").Value = "None"; + XElement none = LoadXamlRule(noneFile, out var namespaceManager); + XElement rule = LoadXamlRule(fullPath); - if (ruleName is "AdditionalFiles" or "Compile" or "EditorConfigFiles") - { - // Remove the "TargetPath" element for these types - var targetPathElement = none.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""TargetPath""]", namespaceManager); - Assert.NotNull(targetPathElement); - targetPathElement.Remove(); + // First fix up the Name as we know they'll differ. + rule.Attribute("Name").Value = "None"; - // Remove the "ExcludeFromCurrentConfiguration" element. - // This is for internal use and hidden, so we don't expect all items to have it. - var excludeFromCurrentConfigurationElement = rule.XPathSelectElement(@"/msb:Rule/msb:BoolProperty[@Name=""ExcludeFromCurrentConfiguration""]", namespaceManager); + if (ruleName is "AdditionalFiles" or "Compile" or "EditorConfigFiles") + { + // Remove the "TargetPath" element for these types + var targetPathElement = none.XPathSelectElement(@"/msb:Rule/msb:StringProperty[@Name=""TargetPath""]", namespaceManager); + Assert.NotNull(targetPathElement); + targetPathElement.Remove(); - excludeFromCurrentConfigurationElement?.Remove(); - } + // Remove the "ExcludeFromCurrentConfiguration" element. + // This is for internal use and hidden, so we don't expect all items to have it. + var excludeFromCurrentConfigurationElement = rule.XPathSelectElement(@"/msb:Rule/msb:BoolProperty[@Name=""ExcludeFromCurrentConfiguration""]", namespaceManager); - AssertXmlEqual(none, rule); + excludeFromCurrentConfigurationElement?.Remove(); } - [Theory] - [MemberData(nameof(GetBrowseObjectItemRules))] - public void BrowseObjectRulesShouldMatchNone(string ruleName, string fullPath, Type assemblyExporterType) + AssertXmlEqual(none, rule); + } + + [Theory] + [MemberData(nameof(GetBrowseObjectItemRules))] + public void BrowseObjectRulesShouldMatchNone(string ruleName, string fullPath, Type assemblyExporterType) + { + // Special case for Folder rule which hasn't been split yet, but is in the Items folder. But also its completely different. + if (ruleName.Equals("Folder", StringComparison.Ordinal)) { - // Special case for Folder rule which hasn't been split yet, but is in the Items folder. But also its completely different. - if (ruleName.Equals("Folder", StringComparison.Ordinal)) - { - return; - } - // No need to check None against None - if (ruleName.Equals("None", StringComparison.Ordinal)) - { - return; - } + return; + } + // No need to check None against None + if (ruleName.Equals("None", StringComparison.Ordinal)) + { + return; + } - string noneFile = Path.Combine(fullPath, "..", "None.BrowseObject.xaml"); + string noneFile = Path.Combine(fullPath, "..", "None.BrowseObject.xaml"); - XElement none = LoadXamlRule(noneFile); - XElement rule = LoadXamlRule(fullPath); + XElement none = LoadXamlRule(noneFile); + XElement rule = LoadXamlRule(fullPath); - // First fix up the Name and DisplayName as we know they'll differ. - rule.Attribute("Name").Value = "None"; - rule.Attribute("DisplayName").Value = "General"; + // First fix up the Name and DisplayName as we know they'll differ. + rule.Attribute("Name").Value = "None"; + rule.Attribute("DisplayName").Value = "General"; - AssertXmlEqual(none, rule); - } + AssertXmlEqual(none, rule); + } - [Theory] - [MemberData(nameof(GetFileItemRules))] - public void FileRulesShouldntBeLocalized(string ruleName, string fullPath, Type assemblyExporterType) + [Theory] + [MemberData(nameof(GetFileItemRules))] + public void FileRulesShouldntBeLocalized(string ruleName, string fullPath, Type assemblyExporterType) + { + // Special case for Folder rule which hasn't been split yet, but is in the Items folder + if (ruleName.Equals("Folder", StringComparison.Ordinal)) { - // Special case for Folder rule which hasn't been split yet, but is in the Items folder - if (ruleName.Equals("Folder", StringComparison.Ordinal)) - { - return; - } + return; + } - XElement rule = LoadXamlRule(fullPath); + XElement rule = LoadXamlRule(fullPath); - foreach (XElement element in rule.Elements()) - { - // No need to define categories if they're not going to be used - Assert.False(element.Name.LocalName.Equals("Rule.Categories", StringComparison.Ordinal)); - Assert.Null(element.Attribute("DisplayName")); - Assert.Null(element.Attribute("Description")); - Assert.Null(element.Attribute("Category")); - } + foreach (XElement element in rule.Elements()) + { + // No need to define categories if they're not going to be used + Assert.False(element.Name.LocalName.Equals("Rule.Categories", StringComparison.Ordinal)); + Assert.Null(element.Attribute("DisplayName")); + Assert.Null(element.Attribute("Description")); + Assert.Null(element.Attribute("Category")); } + } + + [Theory] + [MemberData(nameof(GetBrowseObjectItemRules))] + public void HidePropertyPagesForBrowseObjectRules(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); + XAttribute attribute = rule.Attribute("PropertyPagesHidden"); + + Assert.NotNull(attribute); + Assert.Equal("true", attribute.Value, ignoreCase: true); + } + + [Theory] + [MemberData(nameof(GetItemRules))] + public void RuleNameMatchesFileName(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); - [Theory] - [MemberData(nameof(GetBrowseObjectItemRules))] - public void HidePropertyPagesForBrowseObjectRules(string ruleName, string fullPath, Type assemblyExporterType) + // If a rule is split between File and BrowseObject we need to trim the BrowseObject part off + if (ruleName.IndexOf('.') > -1) { - XElement rule = LoadXamlRule(fullPath); - XAttribute attribute = rule.Attribute("PropertyPagesHidden"); + ruleName = ruleName.Substring(0, ruleName.IndexOf('.')); + } + + Assert.Equal(ruleName, rule.Attribute("Name").Value); + } - Assert.NotNull(attribute); - Assert.Equal("true", attribute.Value, ignoreCase: true); + [Theory] + [MemberData(nameof(GetItemRules))] + public void ItemTypesMustMatchFileNameRoot(string ruleName, string fullPath, Type assemblyExporterType) + { + // If a rule is split between File and BrowseObject we need to trim the BrowseObject part off + if (ruleName.IndexOf('.') > -1) + { + ruleName = ruleName.Substring(0, ruleName.IndexOf('.')); } - [Theory] - [MemberData(nameof(GetItemRules))] - public void RuleNameMatchesFileName(string ruleName, string fullPath, Type assemblyExporterType) + XElement rule = LoadXamlRule(fullPath); + + foreach (var element in rule.Descendants()) { - XElement rule = LoadXamlRule(fullPath); + var attribute = element.Attribute("ItemType"); - // If a rule is split between File and BrowseObject we need to trim the BrowseObject part off - if (ruleName.IndexOf('.') > -1) + if (attribute is not null) { - ruleName = ruleName.Substring(0, ruleName.IndexOf('.')); + Assert.Equal(ruleName, attribute.Value); } - - Assert.Equal(ruleName, rule.Attribute("Name").Value); } + } - [Theory] - [MemberData(nameof(GetItemRules))] - public void ItemTypesMustMatchFileNameRoot(string ruleName, string fullPath, Type assemblyExporterType) + [Theory] + [MemberData(nameof(GetFileItemRules))] + public void FileRulesShouldntHaveFileInfo(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); + + foreach (XElement element in rule.Elements()) { - // If a rule is split between File and BrowseObject we need to trim the BrowseObject part off - if (ruleName.IndexOf('.') > -1) + var nameAttribute = element.Attribute("Name"); + if (nameAttribute is null) { - ruleName = ruleName.Substring(0, ruleName.IndexOf('.')); + continue; } - XElement rule = LoadXamlRule(fullPath); + var isStringProperty = element.Name.LocalName.Equals("StringProperty", StringComparison.Ordinal); - foreach (var element in rule.Descendants()) + if (!isStringProperty) { - var attribute = element.Attribute("ItemType"); - - if (attribute is not null) - { - Assert.Equal(ruleName, attribute.Value); - } + continue; } - } - [Theory] - [MemberData(nameof(GetFileItemRules))] - public void FileRulesShouldntHaveFileInfo(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath); + var name = nameAttribute.Value; - foreach (XElement element in rule.Elements()) + // special case - Folder's Identity property is used by dependencies node + if (!ruleName.Equals("Folder", StringComparison.Ordinal)) { - var nameAttribute = element.Attribute("Name"); - if (nameAttribute is null) - { - continue; - } - - var isStringProperty = element.Name.LocalName.Equals("StringProperty", StringComparison.Ordinal); - - if (!isStringProperty) - { - continue; - } - - var name = nameAttribute.Value; - - // special case - Folder's Identity property is used by dependencies node - if (!ruleName.Equals("Folder", StringComparison.Ordinal)) - { - Assert.False(name.Equals("Identity", StringComparison.Ordinal)); - } - - Assert.False(name.Equals("FileNameAndExtension", StringComparison.Ordinal)); - Assert.False(name.Equals("URL", StringComparison.Ordinal)); - Assert.False(name.Equals("Extension", StringComparison.Ordinal)); + Assert.False(name.Equals("Identity", StringComparison.Ordinal)); } - } - public static IEnumerable GetBrowseObjectItemRules() - { - // Special case for Folder because it is both File and BrowseObject context (for now), but is named like a File. - return Project(GetRules("Items") - .Where(rule => rule.Path.EndsWith(".BrowseObject.xaml", StringComparisons.Paths) || - rule.Path.Equals("Folder.xaml", StringComparisons.Paths))); + Assert.False(name.Equals("FileNameAndExtension", StringComparison.Ordinal)); + Assert.False(name.Equals("URL", StringComparison.Ordinal)); + Assert.False(name.Equals("Extension", StringComparison.Ordinal)); } + } - public static IEnumerable GetFileItemRules() + public static IEnumerable GetBrowseObjectItemRules() + { + // Special case for Folder because it is both File and BrowseObject context (for now), but is named like a File. + return Project(GetRules("Items") + .Where(rule => rule.Path.EndsWith(".BrowseObject.xaml", StringComparisons.Paths) || + rule.Path.Equals("Folder.xaml", StringComparisons.Paths))); + } + + public static IEnumerable GetFileItemRules() + { + return Project(GetRules("Items") + .Where(rule => !rule.Path.EndsWith(".BrowseObject.xaml", StringComparisons.Paths))); + } + + public static IEnumerable GetItemRules() + { + return Project(GetRules("Items")); + } + + private static void AssertXmlEqual(XElement left, XElement right) + { + Assert.Equal(left.Name.LocalName, right.Name.LocalName, ignoreCase: true); + + var leftAttributes = left.Attributes().OrderBy(a => a.Name.LocalName).ToArray(); + var rightAttributes = right.Attributes().OrderBy(a => a.Name.LocalName).ToArray(); + Assert.Equal(leftAttributes.Length, rightAttributes.Length); + + for (int i = 0; i < leftAttributes.Length; i++) { - return Project(GetRules("Items") - .Where(rule => !rule.Path.EndsWith(".BrowseObject.xaml", StringComparisons.Paths))); + AssertAttributesEqual(leftAttributes[i], rightAttributes[i]); } - public static IEnumerable GetItemRules() + var leftChildNodes = left.Elements().OrderBy(a => a.Name.LocalName).ToArray(); + var rightChildNodes = right.Elements().OrderBy(a => a.Name.LocalName).ToArray(); + Assert.Equal(leftChildNodes.Length, rightChildNodes.Length); + + for (int i = 0; i < leftChildNodes.Length; i++) { - return Project(GetRules("Items")); + AssertXmlEqual(leftChildNodes[i], rightChildNodes[i]); } - private static void AssertXmlEqual(XElement left, XElement right) + static void AssertAttributesEqual(XAttribute left, XAttribute right) { Assert.Equal(left.Name.LocalName, right.Name.LocalName, ignoreCase: true); - var leftAttributes = left.Attributes().OrderBy(a => a.Name.LocalName).ToArray(); - var rightAttributes = right.Attributes().OrderBy(a => a.Name.LocalName).ToArray(); - Assert.Equal(leftAttributes.Length, rightAttributes.Length); - - for (int i = 0; i < leftAttributes.Length; i++) - { - AssertAttributesEqual(leftAttributes[i], rightAttributes[i]); - } - - var leftChildNodes = left.Elements().OrderBy(a => a.Name.LocalName).ToArray(); - var rightChildNodes = right.Elements().OrderBy(a => a.Name.LocalName).ToArray(); - Assert.Equal(leftChildNodes.Length, rightChildNodes.Length); - - for (int i = 0; i < leftChildNodes.Length; i++) + // ignore ItemType as we know they'll be different + if (left.Name.LocalName.Equals("ItemType", StringComparison.Ordinal)) { - AssertXmlEqual(leftChildNodes[i], rightChildNodes[i]); + return; } - static void AssertAttributesEqual(XAttribute left, XAttribute right) - { - Assert.Equal(left.Name.LocalName, right.Name.LocalName, ignoreCase: true); - - // ignore ItemType as we know they'll be different - if (left.Name.LocalName.Equals("ItemType", StringComparison.Ordinal)) - { - return; - } - - Assert.Equal(left.Value, right.Value, ignoreCase: true); - } + Assert.Equal(left.Value, right.Value, ignoreCase: true); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/MiscellaneousRuleTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/MiscellaneousRuleTests.cs index ba72e5f882..a433b38fa7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/MiscellaneousRuleTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/MiscellaneousRuleTests.cs @@ -6,292 +6,291 @@ using System.Xml.XPath; using Microsoft.VisualStudio.ProjectSystem.Rules; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +public sealed class MiscellaneousRuleTests : XamlRuleTestBase { - public sealed class MiscellaneousRuleTests : XamlRuleTestBase + private static readonly HashSet s_embeddedRuleNames; + + static MiscellaneousRuleTests() { - private static readonly HashSet s_embeddedRuleNames; + s_embeddedRuleNames = new(EnumerateEmbeddedTypeNames(), StringComparer.Ordinal); - static MiscellaneousRuleTests() + static IEnumerable EnumerateEmbeddedTypeNames() { - s_embeddedRuleNames = new(EnumerateEmbeddedTypeNames(), StringComparer.Ordinal); - - static IEnumerable EnumerateEmbeddedTypeNames() + foreach (var type in RuleServices.GetRuleExporterTypes()) { - foreach (var type in RuleServices.GetRuleExporterTypes()) + foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static)) { - foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static)) + foreach (var exportRule in field.GetCustomAttributes()) + { + yield return exportRule.RuleName; + } + + foreach (var exportRule in field.GetCustomAttributes()) { - foreach (var exportRule in field.GetCustomAttributes()) - { - yield return exportRule.RuleName; - } - - foreach (var exportRule in field.GetCustomAttributes()) - { - yield return exportRule.RuleName; - } + yield return exportRule.RuleName; } } } } + } - [Fact] - public void EmbeddedRuleNames() - { - Assert.NotEmpty(s_embeddedRuleNames); - } + [Fact] + public void EmbeddedRuleNames() + { + Assert.NotEmpty(s_embeddedRuleNames); + } - [Theory] - [MemberData(nameof(GetEmbeddedRules))] - public void EmbeddedRulesShouldNotHaveVisibleProperties(string ruleName, string fullPath, Type assemblyExporterType) - { - // We are not currently able to localize embedded rules. Such rules must not have visible properties, - // as all visible values must be localized. + [Theory] + [MemberData(nameof(GetEmbeddedRules))] + public void EmbeddedRulesShouldNotHaveVisibleProperties(string ruleName, string fullPath, Type assemblyExporterType) + { + // We are not currently able to localize embedded rules. Such rules must not have visible properties, + // as all visible values must be localized. - XElement rule = LoadXamlRule(fullPath); + XElement rule = LoadXamlRule(fullPath); - foreach (var property in GetProperties(rule)) - { - Assert.False( - IsVisible(property), - $"Property '{Name(property)}' in rule '{ruleName}' should not be visible."); - } - } - - [Theory] - [MemberData(nameof(GetAllDisplayedRules))] - public void NonVisiblePropertiesShouldNotBeLocalized(string ruleName, string fullPath, Type assemblyExporterType) + foreach (var property in GetProperties(rule)) { - XElement rule = LoadXamlRule(fullPath); + Assert.False( + IsVisible(property), + $"Property '{Name(property)}' in rule '{ruleName}' should not be visible."); + } + } - foreach (var property in GetProperties(rule)) - { - if (!IsVisible(property) && Name(property) is not ("SplashScreen" or "MinimumSplashScreenDisplayTime")) - { - AssertAttributeNotPresent(property, "DisplayName"); - AssertAttributeNotPresent(property, "Description"); - AssertAttributeNotPresent(property, "Category"); - } - } + [Theory] + [MemberData(nameof(GetAllDisplayedRules))] + public void NonVisiblePropertiesShouldNotBeLocalized(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); - static void AssertAttributeNotPresent(XElement element, string attributeName) + foreach (var property in GetProperties(rule)) + { + if (!IsVisible(property) && Name(property) is not ("SplashScreen" or "MinimumSplashScreenDisplayTime")) { - Assert.True( - element.Attribute(attributeName) is null, - userMessage: $"{attributeName} should not be present:\n{element}"); + AssertAttributeNotPresent(property, "DisplayName"); + AssertAttributeNotPresent(property, "Description"); + AssertAttributeNotPresent(property, "Category"); } } - [Theory] - [MemberData(nameof(GetAllDisplayedRules))] - public void VisiblePropertiesMustHaveDisplayName(string ruleName, string fullPath, Type assemblyExporterType) + static void AssertAttributeNotPresent(XElement element, string attributeName) { - // The "DisplayName" property is localised, while "Name" is not. - // Visible properties without a "DisplayName" will appear in English in all locales. + Assert.True( + element.Attribute(attributeName) is null, + userMessage: $"{attributeName} should not be present:\n{element}"); + } + } + + [Theory] + [MemberData(nameof(GetAllDisplayedRules))] + public void VisiblePropertiesMustHaveDisplayName(string ruleName, string fullPath, Type assemblyExporterType) + { + // The "DisplayName" property is localised, while "Name" is not. + // Visible properties without a "DisplayName" will appear in English in all locales. - XElement rule = LoadXamlRule(fullPath); + XElement rule = LoadXamlRule(fullPath); - foreach (var property in GetProperties(rule)) + foreach (var property in GetProperties(rule)) + { + if (IsVisible(property)) { - if (IsVisible(property)) - { - string? displayName = property.Attribute("DisplayName")?.Value; + string? displayName = property.Attribute("DisplayName")?.Value; - if (string.IsNullOrWhiteSpace(displayName)) - { - throw new Xunit.Sdk.XunitException($""" - Rule {ruleName} has visible property {Name(property)} with no DisplayName value. - """); - } + if (string.IsNullOrWhiteSpace(displayName)) + { + throw new Xunit.Sdk.XunitException($""" + Rule {ruleName} has visible property {Name(property)} with no DisplayName value. + """); } } } + } - [Theory] - [MemberData(nameof(GetAllRules))] - public void PropertyDescriptionMustEndWithFullStop(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath, out XmlNamespaceManager namespaceManager); + [Theory] + [MemberData(nameof(GetAllRules))] + public void PropertyDescriptionMustEndWithFullStop(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath, out XmlNamespaceManager namespaceManager); - foreach (var property in GetProperties(rule)) - { - // - // - // - // + foreach (var property in GetProperties(rule)) + { + // + // + // + // - var linkActionEditors = property.XPathSelectElements(@"./msb:StringProperty.ValueEditors/msb:ValueEditor[@EditorType=""LinkAction""]", namespaceManager); + var linkActionEditors = property.XPathSelectElements(@"./msb:StringProperty.ValueEditors/msb:ValueEditor[@EditorType=""LinkAction""]", namespaceManager); - if (linkActionEditors.Any()) - { - // LinkAction items use the description in hyperlink or button text. - // Neither of these needs to end with a full stop. - continue; - } + if (linkActionEditors.Any()) + { + // LinkAction items use the description in hyperlink or button text. + // Neither of these needs to end with a full stop. + continue; + } - string? description = property.Attribute("Description")?.Value; + string? description = property.Attribute("Description")?.Value; - if (description?.EndsWith(".") == false) - { - throw new Xunit.Sdk.XunitException($""" - Rule {ruleName} has visible property {property.Attribute("Name")} with description not ending in a period. - """); - } + if (description?.EndsWith(".") == false) + { + throw new Xunit.Sdk.XunitException($""" + Rule {ruleName} has visible property {property.Attribute("Name")} with description not ending in a period. + """); } } + } - [Theory] - [MemberData(nameof(GetAllRules))] - public void RuleMustHaveAName(string ruleName, string fullPath, Type assemblyExporterType) - { - XElement rule = LoadXamlRule(fullPath); + [Theory] + [MemberData(nameof(GetAllRules))] + public void RuleMustHaveAName(string ruleName, string fullPath, Type assemblyExporterType) + { + XElement rule = LoadXamlRule(fullPath); - string? name = rule.Attribute("Name")?.Value; + string? name = rule.Attribute("Name")?.Value; - Assert.NotNull(name); - Assert.NotEqual("", name); - } + Assert.NotNull(name); + Assert.NotEqual("", name); + } - [Theory] - [MemberData(nameof(GetAllRules))] - public void TargetResultsDataSourcesMustSpecifyTheTarget(string ruleName, string fullPath, Type assemblyExporterType) - { - var root = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetAllRules))] + public void TargetResultsDataSourcesMustSpecifyTheTarget(string ruleName, string fullPath, Type assemblyExporterType) + { + var root = LoadXamlRule(fullPath, out var namespaceManager); - var dataSource = root.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); + var dataSource = root.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); - var sourceType = dataSource?.Attribute("SourceType"); - var msBuildTarget = dataSource?.Attribute("MSBuildTarget"); + var sourceType = dataSource?.Attribute("SourceType"); + var msBuildTarget = dataSource?.Attribute("MSBuildTarget"); - if (sourceType is not null) + if (sourceType is not null) + { + if (sourceType.Value == "TargetResults") { - if (sourceType.Value == "TargetResults") - { - // A target must be specified - Assert.NotNull(msBuildTarget); - } - else - { - // Target must not be specified on other source types - Assert.Null(msBuildTarget); - } + // A target must be specified + Assert.NotNull(msBuildTarget); + } + else + { + // Target must not be specified on other source types + Assert.Null(msBuildTarget); } } + } - [Theory] - [MemberData(nameof(GetAllRules))] - public void ItemDataSourcesMustSpecifyTheItemType(string ruleName, string fullPath, Type assemblyExporterType) - { - var root = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetAllRules))] + public void ItemDataSourcesMustSpecifyTheItemType(string ruleName, string fullPath, Type assemblyExporterType) + { + var root = LoadXamlRule(fullPath, out var namespaceManager); - var dataSource = root.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); + var dataSource = root.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); - var sourceType = dataSource?.Attribute("SourceType")?.Value; - var itemType = dataSource?.Attribute("ItemType"); + var sourceType = dataSource?.Attribute("SourceType")?.Value; + var itemType = dataSource?.Attribute("ItemType"); - if (sourceType == "Item") - { - // An item type must be specified - Assert.NotNull(itemType); - } + if (sourceType == "Item") + { + // An item type must be specified + Assert.NotNull(itemType); } + } - [Theory] - [MemberData(nameof(GetAllRules))] - public void PropertiesDataSourcesMustMatchItemDataSources(string ruleName, string fullPath, Type assemblyExporterType) - { - var root = LoadXamlRule(fullPath, out var namespaceManager); + [Theory] + [MemberData(nameof(GetAllRules))] + public void PropertiesDataSourcesMustMatchItemDataSources(string ruleName, string fullPath, Type assemblyExporterType) + { + var root = LoadXamlRule(fullPath, out var namespaceManager); - // Get the top-level data source element - var dataSource = root.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); + // Get the top-level data source element + var dataSource = root.XPathSelectElement(@"/msb:Rule/msb:Rule.DataSource/msb:DataSource", namespaceManager); - // If the top level defines an ItemType, all properties must specify a matching ItemType. - var ruleItemType = dataSource?.Attribute("ItemType")?.Value; + // If the top level defines an ItemType, all properties must specify a matching ItemType. + var ruleItemType = dataSource?.Attribute("ItemType")?.Value; - if (ruleItemType is not null) + if (ruleItemType is not null) + { + foreach (var property in GetProperties(root)) { - foreach (var property in GetProperties(root)) - { - var element = GetDataSource(property); + var element = GetDataSource(property); - var propertyItemType = element?.Attribute("ItemType")?.Value; - if (propertyItemType is not null) - { - Assert.True( - StringComparer.Ordinal.Equals(ruleItemType, propertyItemType), - $"""Property data source has item type '{propertyItemType}' but the rule data source has item type '{ruleItemType} which does not match'."""); - } + var propertyItemType = element?.Attribute("ItemType")?.Value; + if (propertyItemType is not null) + { + Assert.True( + StringComparer.Ordinal.Equals(ruleItemType, propertyItemType), + $"""Property data source has item type '{propertyItemType}' but the rule data source has item type '{ruleItemType} which does not match'."""); } } } + } - [Theory] - [MemberData(nameof(GetAllRules))] - public void PropertyNamesMustNotContainSpaces(string ruleName, string fullPath, Type assemblyExporterType) - { - // While MSBuild properties may not contain spaces in their names this restriction doesn't necessarily - // apply when the property in the Rule connects to a non-MSBuild DataSource. Currently none of our - // properties have names with spaces, so for the moment we'll just keep this test simple. - - var root = LoadXamlRule(fullPath); - - foreach (var property in GetProperties(root)) - { - var name = property.Attribute("Name")?.Value; + [Theory] + [MemberData(nameof(GetAllRules))] + public void PropertyNamesMustNotContainSpaces(string ruleName, string fullPath, Type assemblyExporterType) + { + // While MSBuild properties may not contain spaces in their names this restriction doesn't necessarily + // apply when the property in the Rule connects to a non-MSBuild DataSource. Currently none of our + // properties have names with spaces, so for the moment we'll just keep this test simple. + + var root = LoadXamlRule(fullPath); - Assert.NotNull(name); - Assert.False(name.Contains(" "), $"Property name '{name}' in rule '{ruleName}' contains a space. This is likely to lead to a runtime exception when accessing the property"); - } - } - - public static IEnumerable GetMiscellaneousRules() + foreach (var property in GetProperties(root)) { - return Project(GetRules("")); - } + var name = property.Attribute("Name")?.Value; - public static IEnumerable GetAllDisplayedRules() - { - return GetMiscellaneousRules() - .Concat(ItemRuleTests.GetBrowseObjectItemRules()) - .Concat(DependencyRuleTests.GetDependenciesRules()) - .Concat(ProjectPropertiesLocalizationRuleTests.GetPropertyPagesRules()); + Assert.NotNull(name); + Assert.False(name.Contains(" "), $"Property name '{name}' in rule '{ruleName}' contains a space. This is likely to lead to a runtime exception when accessing the property"); } + } - public static IEnumerable GetAllRules() - { - return GetMiscellaneousRules() - .Concat(ItemRuleTests.GetItemRules()) - .Concat(DependencyRuleTests.GetDependenciesRules()) - .Concat(ProjectPropertiesLocalizationRuleTests.GetPropertyPagesRules()); - } + public static IEnumerable GetMiscellaneousRules() + { + return Project(GetRules("")); + } + + public static IEnumerable GetAllDisplayedRules() + { + return GetMiscellaneousRules() + .Concat(ItemRuleTests.GetBrowseObjectItemRules()) + .Concat(DependencyRuleTests.GetDependenciesRules()) + .Concat(ProjectPropertiesLocalizationRuleTests.GetPropertyPagesRules()); + } + + public static IEnumerable GetAllRules() + { + return GetMiscellaneousRules() + .Concat(ItemRuleTests.GetItemRules()) + .Concat(DependencyRuleTests.GetDependenciesRules()) + .Concat(ProjectPropertiesLocalizationRuleTests.GetPropertyPagesRules()); + } - public static IEnumerable GetEmbeddedRules() + public static IEnumerable GetEmbeddedRules() + { + foreach (var rule in GetAllRules()) { - foreach (var rule in GetAllRules()) - { - string ruleName = (string)rule[0]; + string ruleName = (string)rule[0]; - if (s_embeddedRuleNames.Contains(ruleName)) - { - yield return rule; - } + if (s_embeddedRuleNames.Contains(ruleName)) + { + yield return rule; } } + } - private static bool IsVisible(XElement property) - { - // Properties are visible by default - string visibleValue = property.Attribute("Visible")?.Value ?? bool.TrueString; + private static bool IsVisible(XElement property) + { + // Properties are visible by default + string visibleValue = property.Attribute("Visible")?.Value ?? bool.TrueString; - Assert.True(bool.TryParse(visibleValue, out bool isVisible)); + Assert.True(bool.TryParse(visibleValue, out bool isVisible)); - return isVisible; - } + return isVisible; + } - private static string Name(XElement rule) - { - return rule.Attribute("Name")?.Value ?? throw new Xunit.Sdk.XunitException($"Rule must have a name.\n{rule}"); - } + private static string Name(XElement rule) + { + return rule.Attribute("Name")?.Value ?? throw new Xunit.Sdk.XunitException($"Rule must have a name.\n{rule}"); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ProjectPropertiesRuleTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ProjectPropertiesRuleTests.cs index dd061dba38..21e8c4b7b9 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ProjectPropertiesRuleTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/ProjectPropertiesRuleTests.cs @@ -3,71 +3,70 @@ using System.Xml.Linq; using System.Xml.XPath; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +public sealed class ProjectPropertiesLocalizationRuleTests : XamlRuleTestBase { - public sealed class ProjectPropertiesLocalizationRuleTests : XamlRuleTestBase + [Theory] + [MemberData(nameof(GetPropertyPagesRules))] + public void CategoriesShouldBeDefinedOnFile(string ruleName, string fullPath, Type assemblyExporterType) { - [Theory] - [MemberData(nameof(GetPropertyPagesRules))] - public void CategoriesShouldBeDefinedOnFile(string ruleName, string fullPath, Type assemblyExporterType) - { - // These are launch profile files and don't use categories, so let's skip them. - if (ruleName.Contains("ExecutableDebugPropertyPage") || ruleName.Contains("ProjectDebugPropertyPage")) - return; + // These are launch profile files and don't use categories, so let's skip them. + if (ruleName.Contains("ExecutableDebugPropertyPage") || ruleName.Contains("ProjectDebugPropertyPage")) + return; - XElement rule = LoadXamlRule(fullPath, out var namespaceManager); - var categories = new HashSet(); - AddCategories(); + XElement rule = LoadXamlRule(fullPath, out var namespaceManager); + var categories = new HashSet(); + AddCategories(); - // If the page is an extension, we have to check the base page for categories. - if (ruleName.Contains(".CSharp")) - AddCategories(".CSharp"); + // If the page is an extension, we have to check the base page for categories. + if (ruleName.Contains(".CSharp")) + AddCategories(".CSharp"); - else if (ruleName.Contains(".VisualBasic")) - AddCategories(".VisualBasic"); + else if (ruleName.Contains(".VisualBasic")) + AddCategories(".VisualBasic"); - else if (ruleName.Contains(".FSharp")) - AddCategories(".FSharp"); + else if (ruleName.Contains(".FSharp")) + AddCategories(".FSharp"); - var propertyElements = rule.XPathSelectElements(@"/msb:Rule", namespaceManager).Elements(); - foreach (XElement element in propertyElements) - { - // Skip these xml elements. - if (element.Name.LocalName is "Rule.Categories" or "Rule.DataSource" or "Rule.Metadata") - continue; + var propertyElements = rule.XPathSelectElements(@"/msb:Rule", namespaceManager).Elements(); + foreach (XElement element in propertyElements) + { + // Skip these xml elements. + if (element.Name.LocalName is "Rule.Categories" or "Rule.DataSource" or "Rule.Metadata") + continue; - // Skip if the property is not visible. - if (bool.TryParse(element.Attribute("Visible")?.Value, out bool isVisible) && !isVisible) - continue; + // Skip if the property is not visible. + if (bool.TryParse(element.Attribute("Visible")?.Value, out bool isVisible) && !isVisible) + continue; - var categoryAttribute = element.Attribute("Category"); + var categoryAttribute = element.Attribute("Category"); - // Ensure properties have a category attribute with a value. - Assert.True(categoryAttribute is not null, $"Rule '{ruleName}' has property '{element.Attribute("Name").Value}' with no category. Please add a category to the property."); - Assert.True(!string.IsNullOrEmpty(categoryAttribute.Value), $"Rule '{ruleName}' has a property '{element.Attribute("Name").Value}' with an empty category. Please add a value to the category to the property."); + // Ensure properties have a category attribute with a value. + Assert.True(categoryAttribute is not null, $"Rule '{ruleName}' has property '{element.Attribute("Name").Value}' with no category. Please add a category to the property."); + Assert.True(!string.IsNullOrEmpty(categoryAttribute.Value), $"Rule '{ruleName}' has a property '{element.Attribute("Name").Value}' with an empty category. Please add a value to the category to the property."); - // Ensure the category value is defined in the set of categories. - Assert.True(categories.Contains(categoryAttribute.Value), $"Rule '{ruleName}' has a property '{element.Attribute("Name").Value}' with a category '{categoryAttribute.Value}' that is not defined in the set of categories. Please add the category to the set of categories in the Rule.Categories."); - } + // Ensure the category value is defined in the set of categories. + Assert.True(categories.Contains(categoryAttribute.Value), $"Rule '{ruleName}' has a property '{element.Attribute("Name").Value}' with a category '{categoryAttribute.Value}' that is not defined in the set of categories. Please add the category to the set of categories in the Rule.Categories."); + } - void AddCategories(string ruleExtension = "") + void AddCategories(string ruleExtension = "") + { + try + { + XElement baseRule = LoadXamlRule(fullPath.Remove(fullPath.IndexOf(ruleExtension), ruleExtension.Length), out var baseNamespaceManager); + var baseCategories = baseRule.XPathSelectElements(@"/msb:Rule/msb:Rule.Categories/msb:Category", baseNamespaceManager).Select(e => e.Attribute("Name").Value).ToHashSet(); + categories.AddRange(baseCategories); + } + catch (FileNotFoundException) { - try - { - XElement baseRule = LoadXamlRule(fullPath.Remove(fullPath.IndexOf(ruleExtension), ruleExtension.Length), out var baseNamespaceManager); - var baseCategories = baseRule.XPathSelectElements(@"/msb:Rule/msb:Rule.Categories/msb:Category", baseNamespaceManager).Select(e => e.Attribute("Name").Value).ToHashSet(); - categories.AddRange(baseCategories); - } - catch (FileNotFoundException) - { - // It's okay if there's no base file, don't add any categories. - } + // It's okay if there's no base file, don't add any categories. } } + } - public static IEnumerable GetPropertyPagesRules() - { - return Project(GetRules("PropertyPages")); - } + public static IEnumerable GetPropertyPagesRules() + { + return Project(GetRules("PropertyPages")); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/RuleServices.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/RuleServices.cs index 9a6692e522..7e19b4f790 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/RuleServices.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/RuleServices.cs @@ -3,75 +3,74 @@ using System.Reflection; using Microsoft.VisualStudio.ProjectSystem.Rules; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +internal static class RuleServices { - internal static class RuleServices + /// + /// Returns the list of embedded rules + /// + /// + public static IEnumerable GetAllEmbeddedRules() { - /// - /// Returns the list of embedded rules - /// - /// - public static IEnumerable GetAllEmbeddedRules() + foreach (MemberInfo member in GetAllExportedMembers()) { - foreach (MemberInfo member in GetAllExportedMembers()) + foreach (var attribute in member.GetCustomAttributes()) { - foreach (var attribute in member.GetCustomAttributes()) + if (attribute is not null) { - if (attribute is not null) - { - yield return attribute.RuleName; - } + yield return attribute.RuleName; } + } - foreach (var attribute in member.GetCustomAttributes()) + foreach (var attribute in member.GetCustomAttributes()) + { + if (attribute is not null) { - if (attribute is not null) - { - yield return attribute.RuleName; - } + yield return attribute.RuleName; } } } + } - public static IEnumerable GetRuleExporterTypes() - { - Type parentType1 = typeof(RuleExporter); + public static IEnumerable GetRuleExporterTypes() + { + Type parentType1 = typeof(RuleExporter); - foreach (Type type in parentType1.GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Public)) - { - yield return type; - } + foreach (Type type in parentType1.GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Public)) + { + yield return type; + } - yield return parentType1; + yield return parentType1; - Type parentType2 = typeof(VSRuleExporter); + Type parentType2 = typeof(VSRuleExporter); - foreach (Type type in parentType2.GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Public)) - { - yield return type; - } - - yield return parentType2; + foreach (Type type in parentType2.GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Public)) + { + yield return type; } - public static IEnumerable GetAllExportedMembers() + yield return parentType2; + } + + public static IEnumerable GetAllExportedMembers() + { + foreach (Type type in GetRuleExporterTypes()) { - foreach (Type type in GetRuleExporterTypes()) + foreach (MemberInfo member in GetDeclaredMembers(type)) { - foreach (MemberInfo member in GetDeclaredMembers(type)) - { - yield return member; - } + yield return member; } } + } - public static IEnumerable GetDeclaredMembers(Type type) + public static IEnumerable GetDeclaredMembers(Type type) + { + foreach (MemberInfo member in type.GetMembers()) { - foreach (MemberInfo member in type.GetMembers()) - { - if (member.DeclaringType == type) - yield return member; - } + if (member.DeclaringType == type) + yield return member; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/XamlRuleTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/XamlRuleTestBase.cs index 1bb434b021..3149cd223f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/XamlRuleTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Rules/XamlRuleTestBase.cs @@ -5,106 +5,105 @@ using Microsoft.VisualStudio.ProjectSystem.Rules; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules +namespace Microsoft.VisualStudio.ProjectSystem.VS.Rules; + +public abstract class XamlRuleTestBase { - public abstract class XamlRuleTestBase - { - protected const string MSBuildNamespace = "http://schemas.microsoft.com/build/2009/properties"; + protected const string MSBuildNamespace = "http://schemas.microsoft.com/build/2009/properties"; - protected static IEnumerable<(string Path, Type AssemblyExporterType)> GetRules(string? suffix = null, bool recursive = false) + protected static IEnumerable<(string Path, Type AssemblyExporterType)> GetRules(string? suffix = null, bool recursive = false) + { + // Not all rules are embedded as manifests so we have to read the xaml files from the file system. + (string, Type)[] projects = { - // Not all rules are embedded as manifests so we have to read the xaml files from the file system. - (string, Type)[] projects = - { - (Path.Combine(RepoUtil.FindRepoRootPath(), "src", "Microsoft.VisualStudio.ProjectSystem.Managed", "ProjectSystem", "Rules"), typeof(RuleExporter)), - (Path.Combine(RepoUtil.FindRepoRootPath(), "src", "Microsoft.VisualStudio.ProjectSystem.VS.Managed", "ProjectSystem", "Rules"), typeof(VSRuleExporter)) - }; + (Path.Combine(RepoUtil.FindRepoRootPath(), "src", "Microsoft.VisualStudio.ProjectSystem.Managed", "ProjectSystem", "Rules"), typeof(RuleExporter)), + (Path.Combine(RepoUtil.FindRepoRootPath(), "src", "Microsoft.VisualStudio.ProjectSystem.VS.Managed", "ProjectSystem", "Rules"), typeof(VSRuleExporter)) + }; + + bool foundDirectory = false; - bool foundDirectory = false; + foreach ((string basePath, Type assemblyExporterType) in projects) + { + string path = string.IsNullOrEmpty(suffix) ? basePath : Path.Combine(basePath, suffix); - foreach ((string basePath, Type assemblyExporterType) in projects) + if (Directory.Exists(path)) { - string path = string.IsNullOrEmpty(suffix) ? basePath : Path.Combine(basePath, suffix); + foundDirectory = true; - if (Directory.Exists(path)) - { - foundDirectory = true; + var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + foreach (var filePath in Directory.EnumerateFiles(path, "*.xaml", searchOption)) + { + XElement rule = LoadXamlRule(filePath); - foreach (var filePath in Directory.EnumerateFiles(path, "*.xaml", searchOption)) + // Ignore XAML documents for non-Rule types (such as ProjectSchemaDefinitions) + if (rule.Name.LocalName == "Rule") { - XElement rule = LoadXamlRule(filePath); - - // Ignore XAML documents for non-Rule types (such as ProjectSchemaDefinitions) - if (rule.Name.LocalName == "Rule") - { - yield return (filePath, assemblyExporterType); - } + yield return (filePath, assemblyExporterType); } } } - - Assert.True(foundDirectory, $"Couldn't find XAML rules folder with suffix '{suffix}'."); } - /// Projects a XAML rule file's path into the form used by unit tests theories. - protected static IEnumerable Project(IEnumerable<(string Path, Type AssemblyExporterType)> files) - { - // we return the rule name separately mainly to get a readable display in Test Explorer so failures can be diagnosed more easily - return from file in files - select new object[] { Path.GetFileNameWithoutExtension(file.Path), file.Path, file.AssemblyExporterType }; - } + Assert.True(foundDirectory, $"Couldn't find XAML rules folder with suffix '{suffix}'."); + } - protected static XElement LoadXamlRule(string filePath) - { - return LoadXamlRule(filePath, out _); - } + /// Projects a XAML rule file's path into the form used by unit tests theories. + protected static IEnumerable Project(IEnumerable<(string Path, Type AssemblyExporterType)> files) + { + // we return the rule name separately mainly to get a readable display in Test Explorer so failures can be diagnosed more easily + return from file in files + select new object[] { Path.GetFileNameWithoutExtension(file.Path), file.Path, file.AssemblyExporterType }; + } - protected static XElement LoadXamlRule(string filePath, out XmlNamespaceManager namespaceManager) - { - var settings = new XmlReaderSettings { XmlResolver = null }; - using var fileStream = File.OpenRead(filePath); - using var reader = XmlReader.Create(fileStream, settings); - var root = XDocument.Load(reader).Root; + protected static XElement LoadXamlRule(string filePath) + { + return LoadXamlRule(filePath, out _); + } - namespaceManager = new XmlNamespaceManager(reader.NameTable); - namespaceManager.AddNamespace("msb", MSBuildNamespace); + protected static XElement LoadXamlRule(string filePath, out XmlNamespaceManager namespaceManager) + { + var settings = new XmlReaderSettings { XmlResolver = null }; + using var fileStream = File.OpenRead(filePath); + using var reader = XmlReader.Create(fileStream, settings); + var root = XDocument.Load(reader).Root; - return root; - } + namespaceManager = new XmlNamespaceManager(reader.NameTable); + namespaceManager.AddNamespace("msb", MSBuildNamespace); + + return root; + } - protected static IEnumerable GetProperties(XElement rule) + protected static IEnumerable GetProperties(XElement rule) + { + foreach (var child in rule.Elements()) { - foreach (var child in rule.Elements()) + if (child.Name.LocalName.EndsWith("Property", StringComparison.Ordinal) && + child.Name.LocalName.IndexOf('.') == -1) { - if (child.Name.LocalName.EndsWith("Property", StringComparison.Ordinal) && - child.Name.LocalName.IndexOf('.') == -1) - { - yield return child; - } + yield return child; } } + } - protected static XElement? GetDataSource(XElement property) + protected static XElement? GetDataSource(XElement property) + { + foreach (var child in property.Elements()) { - foreach (var child in property.Elements()) - { - if (child.Name.LocalName.EndsWith("DataSource", StringComparison.Ordinal)) - return child.Elements().First(); - } - - return null; + if (child.Name.LocalName.EndsWith("DataSource", StringComparison.Ordinal)) + return child.Elements().First(); } - protected static IEnumerable GetVisibleProperties(XElement rule) + return null; + } + + protected static IEnumerable GetVisibleProperties(XElement rule) + { + foreach (var property in GetProperties(rule)) { - foreach (var property in GetProperties(rule)) + if (!string.Equals(property.Attribute("Visible")?.Value, "False", StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(property.Attribute("Visible")?.Value, "False", StringComparison.OrdinalIgnoreCase)) - { - yield return property; - } + yield return property; } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/SpecialFileProviders/VsProjectSpecialFilesManagerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/SpecialFileProviders/VsProjectSpecialFilesManagerTests.cs index 08124d1c2e..943ca4cc57 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/SpecialFileProviders/VsProjectSpecialFilesManagerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/SpecialFileProviders/VsProjectSpecialFilesManagerTests.cs @@ -2,67 +2,66 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.SpecialFilesProviders +namespace Microsoft.VisualStudio.ProjectSystem.VS.SpecialFilesProviders; + +public class VsProjectSpecialFilesManagerTests { - public class VsProjectSpecialFilesManagerTests + [Fact] + public async Task GetFiles_WhenUnderlyingGetFilesReturnsOK_ReturnsFileName() { - [Fact] - public async Task GetFiles_WhenUnderlyingGetFilesReturnsOK_ReturnsFileName() + var specialFiles = IVsProjectSpecialFilesFactory.ImplementGetFile((int fileId, uint flags, out uint itemId, out string fileName) => { - var specialFiles = IVsProjectSpecialFilesFactory.ImplementGetFile((int fileId, uint flags, out uint itemId, out string fileName) => - { - itemId = 0; - fileName = "FileName"; - return VSConstants.S_OK; - }); + itemId = 0; + fileName = "FileName"; + return VSConstants.S_OK; + }); - var manager = CreateInstance(specialFiles); + var manager = CreateInstance(specialFiles); - var result = await manager.GetFileAsync(SpecialFiles.AppConfig, SpecialFileFlags.CheckoutIfExists); + var result = await manager.GetFileAsync(SpecialFiles.AppConfig, SpecialFileFlags.CheckoutIfExists); - Assert.Equal("FileName", result); - } + Assert.Equal("FileName", result); + } - [Fact] - public async Task GetFiles_WhenUnderlyingGetFilesReturnsNotImpl_ReturnsNull() + [Fact] + public async Task GetFiles_WhenUnderlyingGetFilesReturnsNotImpl_ReturnsNull() + { + var specialFiles = IVsProjectSpecialFilesFactory.ImplementGetFile((int fileId, uint flags, out uint itemId, out string fileName) => { - var specialFiles = IVsProjectSpecialFilesFactory.ImplementGetFile((int fileId, uint flags, out uint itemId, out string fileName) => - { - itemId = 0; - fileName = "FileName"; - return VSConstants.E_NOTIMPL; - }); + itemId = 0; + fileName = "FileName"; + return VSConstants.E_NOTIMPL; + }); - var manager = CreateInstance(specialFiles); + var manager = CreateInstance(specialFiles); - var result = await manager.GetFileAsync(SpecialFiles.AppConfig, SpecialFileFlags.CheckoutIfExists); + var result = await manager.GetFileAsync(SpecialFiles.AppConfig, SpecialFileFlags.CheckoutIfExists); - Assert.Null(result); - } + Assert.Null(result); + } - [Fact] - public async Task GetFiles_WhenUnderlyingGetFilesReturnsHResult_Throws() + [Fact] + public async Task GetFiles_WhenUnderlyingGetFilesReturnsHResult_Throws() + { + var specialFiles = IVsProjectSpecialFilesFactory.ImplementGetFile((int fileId, uint flags, out uint itemId, out string fileName) => { - var specialFiles = IVsProjectSpecialFilesFactory.ImplementGetFile((int fileId, uint flags, out uint itemId, out string fileName) => - { - itemId = 0; - fileName = "FileName"; - return VSConstants.E_OUTOFMEMORY; - }); + itemId = 0; + fileName = "FileName"; + return VSConstants.E_OUTOFMEMORY; + }); - var manager = CreateInstance(specialFiles); + var manager = CreateInstance(specialFiles); - await Assert.ThrowsAsync(() => - { - return manager.GetFileAsync(SpecialFiles.AppConfig, SpecialFileFlags.CheckoutIfExists); - }); - } - - private VsProjectSpecialFilesManager CreateInstance(IVsProjectSpecialFiles specialFiles) + await Assert.ThrowsAsync(() => { - IUnconfiguredProjectVsServices projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(hierarchyCreator: () => (IVsHierarchy)specialFiles); + return manager.GetFileAsync(SpecialFiles.AppConfig, SpecialFileFlags.CheckoutIfExists); + }); + } + + private VsProjectSpecialFilesManager CreateInstance(IVsProjectSpecialFiles specialFiles) + { + IUnconfiguredProjectVsServices projectVsServices = IUnconfiguredProjectVsServicesFactory.Implement(hierarchyCreator: () => (IVsHierarchy)specialFiles); - return new VsProjectSpecialFilesManager(projectVsServices); - } + return new VsProjectSpecialFilesManager(projectVsServices); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridgeTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridgeTests.cs index 43ec9bd92e..723736d09f 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridgeTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsBuildManagerBridgeTests.cs @@ -2,139 +2,138 @@ using Microsoft.VisualStudio.ProjectSystem.VS.Automation; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +public class DesignTimeInputsBuildManagerBridgeTests : IDisposable { - public class DesignTimeInputsBuildManagerBridgeTests : IDisposable + private readonly TestBuildManager _buildManager; + private readonly TestDesignTimeInputsBuildManagerBridge _bridge; + private string? _lastCompiledFile; + private string? _lastOutputPath; + private ImmutableHashSet? _lastSharedInputs; + + [Fact] + public async Task ChangedFile_FiresTempPEDirty() { - private readonly TestBuildManager _buildManager; - private readonly TestDesignTimeInputsBuildManagerBridge _bridge; - private string? _lastCompiledFile; - private string? _lastOutputPath; - private ImmutableHashSet? _lastSharedInputs; - - [Fact] - public async Task ChangedFile_FiresTempPEDirty() - { - await _bridge.ApplyAsync(new DesignTimeInputSnapshot( - ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), - ImmutableHashSet.Empty, - ImmutableHashSet.Empty, - "C:\\TempPE")); - - await _bridge.ApplyAsync(new DesignTimeInputSnapshot( - ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), - ImmutableHashSet.Empty, - new[] { new DesignTimeInputFileChange("Resources1.Designer.cs", false) }, - "C:\\TempPE")); - - // One file should have been added - Assert.Single(_buildManager.DirtyItems); - Assert.Equal("Resources1.Designer.cs", _buildManager.DirtyItems[0]); - Assert.Empty(_buildManager.DeletedItems); - } + await _bridge.ApplyAsync(new DesignTimeInputSnapshot( + ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), + ImmutableHashSet.Empty, + ImmutableHashSet.Empty, + "C:\\TempPE")); + + await _bridge.ApplyAsync(new DesignTimeInputSnapshot( + ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), + ImmutableHashSet.Empty, + new[] { new DesignTimeInputFileChange("Resources1.Designer.cs", false) }, + "C:\\TempPE")); + + // One file should have been added + Assert.Single(_buildManager.DirtyItems); + Assert.Equal("Resources1.Designer.cs", _buildManager.DirtyItems[0]); + Assert.Empty(_buildManager.DeletedItems); + } - [Fact] - public async Task RemovedFile_FiresTempPEDeleted() - { - await _bridge.ApplyAsync(new DesignTimeInputSnapshot( - ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), - ImmutableHashSet.Empty, - Array.Empty(), - "")); - - await _bridge.ApplyAsync(new DesignTimeInputSnapshot( - ImmutableHashSet.Empty, - ImmutableHashSet.Empty, - Array.Empty(), - "")); - - // One file should have been added - Assert.Empty(_buildManager.DirtyItems); - Assert.Single(_buildManager.DeletedItems); - Assert.Equal("Resources1.Designer.cs", _buildManager.DeletedItems[0]); - } + [Fact] + public async Task RemovedFile_FiresTempPEDeleted() + { + await _bridge.ApplyAsync(new DesignTimeInputSnapshot( + ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), + ImmutableHashSet.Empty, + Array.Empty(), + "")); + + await _bridge.ApplyAsync(new DesignTimeInputSnapshot( + ImmutableHashSet.Empty, + ImmutableHashSet.Empty, + Array.Empty(), + "")); + + // One file should have been added + Assert.Empty(_buildManager.DirtyItems); + Assert.Single(_buildManager.DeletedItems); + Assert.Equal("Resources1.Designer.cs", _buildManager.DeletedItems[0]); + } - [Fact] - public async Task GetDesignTimeInputXmlAsync_HasCorrectArguments() - { - await _bridge.ApplyAsync(new DesignTimeInputSnapshot( - ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), - ImmutableHashSet.Empty, - new[] { new DesignTimeInputFileChange("Resources1.Designer.cs", false) }, - "C:\\TempPE")); + [Fact] + public async Task GetDesignTimeInputXmlAsync_HasCorrectArguments() + { + await _bridge.ApplyAsync(new DesignTimeInputSnapshot( + ImmutableHashSet.CreateRange(new string[] { "Resources1.Designer.cs" }), + ImmutableHashSet.Empty, + new[] { new DesignTimeInputFileChange("Resources1.Designer.cs", false) }, + "C:\\TempPE")); - await _bridge.BuildDesignTimeOutputAsync("Resources1.Designer.cs"); + await _bridge.BuildDesignTimeOutputAsync("Resources1.Designer.cs"); - Assert.Equal("Resources1.Designer.cs", _lastCompiledFile); - Assert.Equal("C:\\TempPE", _lastOutputPath); - Assert.Equal(ImmutableHashSet.Empty, _lastSharedInputs); - } + Assert.Equal("Resources1.Designer.cs", _lastCompiledFile); + Assert.Equal("C:\\TempPE", _lastOutputPath); + Assert.Equal(ImmutableHashSet.Empty, _lastSharedInputs); + } - public DesignTimeInputsBuildManagerBridgeTests() - { - var threadingService = IProjectThreadingServiceFactory.Create(); + public DesignTimeInputsBuildManagerBridgeTests() + { + var threadingService = IProjectThreadingServiceFactory.Create(); - var changeTracker = Mock.Of(); + var changeTracker = Mock.Of(); - var compilerMock = new Mock(); - compilerMock.Setup(c => c.BuildDesignTimeOutputAsync(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((file, outputPath, sharedInputs) => - { - _lastCompiledFile = file; - _lastOutputPath = outputPath; - _lastSharedInputs = sharedInputs; - }); + var compilerMock = new Mock(); + compilerMock.Setup(c => c.BuildDesignTimeOutputAsync(It.IsAny(), It.IsAny(), It.IsAny>())) + .Callback>((file, outputPath, sharedInputs) => + { + _lastCompiledFile = file; + _lastOutputPath = outputPath; + _lastSharedInputs = sharedInputs; + }); - var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject\MyProject.csproj"); + var project = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject\MyProject.csproj"); - _buildManager = new TestBuildManager(); + _buildManager = new TestBuildManager(); - _bridge = new TestDesignTimeInputsBuildManagerBridge(project, threadingService, changeTracker, compilerMock.Object, _buildManager) - { - SkipInitialization = true - }; - } + _bridge = new TestDesignTimeInputsBuildManagerBridge(project, threadingService, changeTracker, compilerMock.Object, _buildManager) + { + SkipInitialization = true + }; + } - public void Dispose() + public void Dispose() + { + ((IDisposable)_bridge).Dispose(); + } + + internal class TestDesignTimeInputsBuildManagerBridge : DesignTimeInputsBuildManagerBridge + { + public TestDesignTimeInputsBuildManagerBridge(UnconfiguredProject project, IProjectThreadingService threadingService, IDesignTimeInputsChangeTracker designTimeInputsChangeTracker, IDesignTimeInputsCompiler designTimeInputsCompiler, VSBuildManager buildManager) + : base(project, threadingService, designTimeInputsChangeTracker, designTimeInputsCompiler, buildManager) { - ((IDisposable)_bridge).Dispose(); } - internal class TestDesignTimeInputsBuildManagerBridge : DesignTimeInputsBuildManagerBridge + public Task ApplyAsync(DesignTimeInputSnapshot value) { - public TestDesignTimeInputsBuildManagerBridge(UnconfiguredProject project, IProjectThreadingService threadingService, IDesignTimeInputsChangeTracker designTimeInputsChangeTracker, IDesignTimeInputsCompiler designTimeInputsCompiler, VSBuildManager buildManager) - : base(project, threadingService, designTimeInputsChangeTracker, designTimeInputsCompiler, buildManager) - { - } + var input = IProjectVersionedValueFactory.Create(value); - public Task ApplyAsync(DesignTimeInputSnapshot value) - { - var input = IProjectVersionedValueFactory.Create(value); - - return base.ApplyAsync(input); - } + return base.ApplyAsync(input); } + } - internal class TestBuildManager : VSBuildManager - { - public List DeletedItems { get; } = new List(); - public List DirtyItems { get; } = new List(); + internal class TestBuildManager : VSBuildManager + { + public List DeletedItems { get; } = new List(); + public List DirtyItems { get; } = new List(); - internal TestBuildManager() - : base(IProjectThreadingServiceFactory.Create(), - IUnconfiguredProjectCommonServicesFactory.Create(UnconfiguredProjectFactory.Create())) - { - } + internal TestBuildManager() + : base(IProjectThreadingServiceFactory.Create(), + IUnconfiguredProjectCommonServicesFactory.Create(UnconfiguredProjectFactory.Create())) + { + } - internal override void OnDesignTimeOutputDeleted(string outputMoniker) - { - DeletedItems.Add(outputMoniker); - } + internal override void OnDesignTimeOutputDeleted(string outputMoniker) + { + DeletedItems.Add(outputMoniker); + } - internal override void OnDesignTimeOutputDirty(string outputMoniker) - { - DirtyItems.Add(outputMoniker); - } + internal override void OnDesignTimeOutputDirty(string outputMoniker) + { + DirtyItems.Add(outputMoniker); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTrackerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTrackerTests.cs index a77e83a0fd..32dc32481e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTrackerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsChangeTrackerTests.cs @@ -2,340 +2,339 @@ using Xunit.Sdk; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE -{ - public class DesignTimeInputsChangeTrackerTests : IDisposable - { - private const int TestTimeoutMillisecondsDelay = 1000; +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; - private string? _lastIntermediateOutputPath; - private readonly string _projectFolder = @"C:\MyProject"; - private string _intermediateOutputPath = "MyOutput"; - private readonly DesignTimeInputsChangeTracker _changeTracker; +public class DesignTimeInputsChangeTrackerTests : IDisposable +{ + private const int TestTimeoutMillisecondsDelay = 1000; - private readonly List _outputProduced = new(); - private readonly TaskCompletionSource _outputProducedSource = new(); - private int _expectedOutput; + private string? _lastIntermediateOutputPath; + private readonly string _projectFolder = @"C:\MyProject"; + private string _intermediateOutputPath = "MyOutput"; + private readonly DesignTimeInputsChangeTracker _changeTracker; - [Fact] - public async Task SingleDesignTimeInput_ProducesOutput() - { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + private readonly List _outputProduced = new(); + private readonly TaskCompletionSource _outputProducedSource = new(); + private int _expectedOutput; - await VerifyOutput(1, () => - { - SendDesignTimeInputs(inputs); - }); - - Assert.Single(_outputProduced[0].Inputs); - Assert.Empty(_outputProduced[0].SharedInputs); - Assert.Contains("File1.cs", _outputProduced[0].Inputs); - Assert.Single(_outputProduced[0].ChangedInputs); - Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); - } + [Fact] + public async Task SingleDesignTimeInput_ProducesOutput() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - [Fact] - public async Task FileChange_WithoutInputs_NoOutput() + await VerifyOutput(1, () => { - await VerifyOutput(0, () => - { - SendFileChange("File1.cs"); - }); - } + SendDesignTimeInputs(inputs); + }); + + Assert.Single(_outputProduced[0].Inputs); + Assert.Empty(_outputProduced[0].SharedInputs); + Assert.Contains("File1.cs", _outputProduced[0].Inputs); + Assert.Single(_outputProduced[0].ChangedInputs); + Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + } - [Fact] - public async Task SingleDesignTimeInput_Changes_ChangedTwice() + [Fact] + public async Task FileChange_WithoutInputs_NoOutput() + { + await VerifyOutput(0, () => { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + SendFileChange("File1.cs"); + }); + } - await VerifyOutput(2, () => - { - SendDesignTimeInputs(inputs); - - SendFileChange("File1.cs"); - }); - - Assert.Single(_outputProduced[0].ChangedInputs); - Assert.Single(_outputProduced[0].Inputs); - Assert.Empty(_outputProduced[0].SharedInputs); - Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); - - Assert.Single(_outputProduced[1].ChangedInputs); - Assert.Single(_outputProduced[1].Inputs); - Assert.Empty(_outputProduced[1].SharedInputs); - Assert.Equal("File1.cs", _outputProduced[1].ChangedInputs[0].File); - Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); - } + [Fact] + public async Task SingleDesignTimeInput_Changes_ChangedTwice() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - [Fact] - public async Task TwoDesignTimeInputs_SharedInputChanges_BothChanged() + await VerifyOutput(2, () => { - var inputs = new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { "SharedFile.cs" }); + SendDesignTimeInputs(inputs); + + SendFileChange("File1.cs"); + }); + + Assert.Single(_outputProduced[0].ChangedInputs); + Assert.Single(_outputProduced[0].Inputs); + Assert.Empty(_outputProduced[0].SharedInputs); + Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + + Assert.Single(_outputProduced[1].ChangedInputs); + Assert.Single(_outputProduced[1].Inputs); + Assert.Empty(_outputProduced[1].SharedInputs); + Assert.Equal("File1.cs", _outputProduced[1].ChangedInputs[0].File); + Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); + } - await VerifyOutput(2, () => - { - SendDesignTimeInputs(inputs); - - SendFileChange("SharedFile.cs"); - }); - - Assert.Equal(2, _outputProduced[0].ChangedInputs.Length); - Assert.Equal(2, _outputProduced[0].Inputs.Count); - Assert.Single(_outputProduced[0].SharedInputs); - Assert.Contains("File1.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); - Assert.Contains("File2.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); - - Assert.Equal(2, _outputProduced[1].ChangedInputs.Length); - Assert.Equal(2, _outputProduced[1].Inputs.Count); - Assert.Single(_outputProduced[1].SharedInputs); - Assert.Contains("File1.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); - Assert.Contains("File2.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); - Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); - } + [Fact] + public async Task TwoDesignTimeInputs_SharedInputChanges_BothChanged() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { "SharedFile.cs" }); - [Fact] - public async Task SingleDesignTimeInput_Removed_ShouldntBeChanged() + await VerifyOutput(2, () => { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + SendDesignTimeInputs(inputs); + + SendFileChange("SharedFile.cs"); + }); + + Assert.Equal(2, _outputProduced[0].ChangedInputs.Length); + Assert.Equal(2, _outputProduced[0].Inputs.Count); + Assert.Single(_outputProduced[0].SharedInputs); + Assert.Contains("File1.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); + Assert.Contains("File2.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + + Assert.Equal(2, _outputProduced[1].ChangedInputs.Length); + Assert.Equal(2, _outputProduced[1].Inputs.Count); + Assert.Single(_outputProduced[1].SharedInputs); + Assert.Contains("File1.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); + Assert.Contains("File2.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); + Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); + } - await VerifyOutput(2, () => - { - SendDesignTimeInputs(inputs); - - SendDesignTimeInputs(new DesignTimeInputs(new string[] { }, new string[] { })); - }); - - // First update should include the file - Assert.Single(_outputProduced[0].ChangedInputs); - Assert.Single(_outputProduced[0].Inputs); - Assert.Empty(_outputProduced[0].SharedInputs); - Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); - - // Second shouldn't - Assert.Empty(_outputProduced[1].ChangedInputs); - Assert.Empty(_outputProduced[1].Inputs); - Assert.Empty(_outputProduced[1].SharedInputs); - } + [Fact] + public async Task SingleDesignTimeInput_Removed_ShouldntBeChanged() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - [Fact] - public async Task SingleDesignTimeInput_AnotherAdded_OneInEachChange() + await VerifyOutput(2, () => { - await VerifyOutput(2, () => - { - SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { })); - - SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { })); - }); - - Assert.Single(_outputProduced[0].ChangedInputs); - Assert.Single(_outputProduced[0].Inputs); - Assert.Empty(_outputProduced[0].SharedInputs); - Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); - - Assert.Single(_outputProduced[1].ChangedInputs); - Assert.Equal(2, _outputProduced[1].Inputs.Count); - Assert.Empty(_outputProduced[1].SharedInputs); - Assert.Equal("File2.cs", _outputProduced[1].ChangedInputs[0].File); - Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); - } + SendDesignTimeInputs(inputs); + + SendDesignTimeInputs(new DesignTimeInputs(new string[] { }, new string[] { })); + }); + + // First update should include the file + Assert.Single(_outputProduced[0].ChangedInputs); + Assert.Single(_outputProduced[0].Inputs); + Assert.Empty(_outputProduced[0].SharedInputs); + Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + + // Second shouldn't + Assert.Empty(_outputProduced[1].ChangedInputs); + Assert.Empty(_outputProduced[1].Inputs); + Assert.Empty(_outputProduced[1].SharedInputs); + } - [Fact] - public async Task TwoDesignTimeInputs_OutputPathChanged_BothChanged() + [Fact] + public async Task SingleDesignTimeInput_AnotherAdded_OneInEachChange() + { + await VerifyOutput(2, () => { - var inputs = new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { }); + SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { })); + + SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { })); + }); + + Assert.Single(_outputProduced[0].ChangedInputs); + Assert.Single(_outputProduced[0].Inputs); + Assert.Empty(_outputProduced[0].SharedInputs); + Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + + Assert.Single(_outputProduced[1].ChangedInputs); + Assert.Equal(2, _outputProduced[1].Inputs.Count); + Assert.Empty(_outputProduced[1].SharedInputs); + Assert.Equal("File2.cs", _outputProduced[1].ChangedInputs[0].File); + Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); + } - await VerifyOutput(2, () => - { - SendDesignTimeInputs(inputs); - - SendDesignTimeInputs(inputs, "NewOutputPath"); - }); - - Assert.Equal(2, _outputProduced[0].ChangedInputs.Length); - Assert.Equal(2, _outputProduced[0].Inputs.Count); - Assert.Empty(_outputProduced[0].SharedInputs); - Assert.Contains("File1.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); - Assert.Contains("File2.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); - - Assert.Equal(2, _outputProduced[1].ChangedInputs.Length); - Assert.Equal(2, _outputProduced[1].Inputs.Count); - Assert.Empty(_outputProduced[1].SharedInputs); - Assert.Contains("File1.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); - Assert.Contains("File2.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); - Assert.True(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); - } + [Fact] + public async Task TwoDesignTimeInputs_OutputPathChanged_BothChanged() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { }); - [Fact] - public async Task NewSharedDesignTimeInput_ModifiedInThePast_ShouldIgnoreFileWriteTime() + await VerifyOutput(2, () => { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + SendDesignTimeInputs(inputs); + + SendDesignTimeInputs(inputs, "NewOutputPath"); + }); + + Assert.Equal(2, _outputProduced[0].ChangedInputs.Length); + Assert.Equal(2, _outputProduced[0].Inputs.Count); + Assert.Empty(_outputProduced[0].SharedInputs); + Assert.Contains("File1.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); + Assert.Contains("File2.cs", _outputProduced[0].ChangedInputs.Select(f => f.File)); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + + Assert.Equal(2, _outputProduced[1].ChangedInputs.Length); + Assert.Equal(2, _outputProduced[1].Inputs.Count); + Assert.Empty(_outputProduced[1].SharedInputs); + Assert.Contains("File1.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); + Assert.Contains("File2.cs", _outputProduced[1].ChangedInputs.Select(f => f.File)); + Assert.True(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); + } - await VerifyOutput(2, () => - { - SendDesignTimeInputs(inputs); + [Fact] + public async Task NewSharedDesignTimeInput_ModifiedInThePast_ShouldIgnoreFileWriteTime() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { "OldFile.cs" }); + await VerifyOutput(2, () => + { + SendDesignTimeInputs(inputs); - SendDesignTimeInputs(inputs); - }); + inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { "OldFile.cs" }); - Assert.Single(_outputProduced[0].ChangedInputs); - Assert.Single(_outputProduced[0].Inputs); - Assert.Empty(_outputProduced[0].SharedInputs); - Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); - Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + SendDesignTimeInputs(inputs); + }); - Assert.Single(_outputProduced[1].ChangedInputs); - Assert.Single(_outputProduced[1].Inputs); - Assert.Single(_outputProduced[1].SharedInputs); - Assert.Equal("File1.cs", _outputProduced[1].ChangedInputs[0].File); - Assert.True(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); - } + Assert.Single(_outputProduced[0].ChangedInputs); + Assert.Single(_outputProduced[0].Inputs); + Assert.Empty(_outputProduced[0].SharedInputs); + Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); + Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); + + Assert.Single(_outputProduced[1].ChangedInputs); + Assert.Single(_outputProduced[1].Inputs); + Assert.Single(_outputProduced[1].SharedInputs); + Assert.Equal("File1.cs", _outputProduced[1].ChangedInputs[0].File); + Assert.True(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); + } - public DesignTimeInputsChangeTrackerTests() + public DesignTimeInputsChangeTrackerTests() + { + var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); + using var designTimeInputsSource = ProjectValueDataSourceFactory.Create(services); + + var dataSourceMock = new Mock(); + dataSourceMock.SetupGet(s => s.SourceBlock) + .Returns(designTimeInputsSource.SourceBlock); + + using var fileWatcherSource = ProjectValueDataSourceFactory.Create(services); + + var watcherMock = new Mock(); + watcherMock.SetupGet(s => s.SourceBlock) + .Returns(fileWatcherSource.SourceBlock); + + var threadingService = IProjectThreadingServiceFactory.Create(); + var projectSubscriptionService = IActiveConfiguredProjectSubscriptionServiceFactory.Create(); + var unconfiguredProject = UnconfiguredProjectFactory.Create( + fullPath: Path.Combine(_projectFolder, "MyTestProj.csproj"), + projectAsynchronousTasksService: IProjectAsynchronousTasksServiceFactory.Create()); + + var unconfiguredProjectServices = IUnconfiguredProjectServicesFactory.Create( + projectService: IProjectServiceFactory.Create( + services: ProjectServicesFactory.Create( + threadingService: threadingService))); + + _changeTracker = new DesignTimeInputsChangeTracker(unconfiguredProject, + unconfiguredProjectServices, + threadingService, + projectSubscriptionService, + dataSourceMock.Object, + watcherMock.Object) { - var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); - using var designTimeInputsSource = ProjectValueDataSourceFactory.Create(services); - - var dataSourceMock = new Mock(); - dataSourceMock.SetupGet(s => s.SourceBlock) - .Returns(designTimeInputsSource.SourceBlock); - - using var fileWatcherSource = ProjectValueDataSourceFactory.Create(services); - - var watcherMock = new Mock(); - watcherMock.SetupGet(s => s.SourceBlock) - .Returns(fileWatcherSource.SourceBlock); - - var threadingService = IProjectThreadingServiceFactory.Create(); - var projectSubscriptionService = IActiveConfiguredProjectSubscriptionServiceFactory.Create(); - var unconfiguredProject = UnconfiguredProjectFactory.Create( - fullPath: Path.Combine(_projectFolder, "MyTestProj.csproj"), - projectAsynchronousTasksService: IProjectAsynchronousTasksServiceFactory.Create()); - - var unconfiguredProjectServices = IUnconfiguredProjectServicesFactory.Create( - projectService: IProjectServiceFactory.Create( - services: ProjectServicesFactory.Create( - threadingService: threadingService))); - - _changeTracker = new DesignTimeInputsChangeTracker(unconfiguredProject, - unconfiguredProjectServices, - threadingService, - projectSubscriptionService, - dataSourceMock.Object, - watcherMock.Object) - { - AllowSourceBlockCompletion = true - }; + AllowSourceBlockCompletion = true + }; - // Create a block to receive the output - var receiver = DataflowBlockSlim.CreateActionBlock>(OutputProduced); - _changeTracker.SourceBlock.LinkTo(receiver, DataflowOption.PropagateCompletion); - } + // Create a block to receive the output + var receiver = DataflowBlockSlim.CreateActionBlock>(OutputProduced); + _changeTracker.SourceBlock.LinkTo(receiver, DataflowOption.PropagateCompletion); + } - private void OutputProduced(IProjectVersionedValue val) - { - _outputProduced.Add(val.Value); + private void OutputProduced(IProjectVersionedValue val) + { + _outputProduced.Add(val.Value); - if (_outputProduced.Count == _expectedOutput) - { - _outputProducedSource?.SetResult(); - } + if (_outputProduced.Count == _expectedOutput) + { + _outputProducedSource?.SetResult(); } + } - private async Task VerifyOutput(int numberOfOutputExpected, Action actionThatCausesCompilation) - { - _expectedOutput = numberOfOutputExpected; + private async Task VerifyOutput(int numberOfOutputExpected, Action actionThatCausesCompilation) + { + _expectedOutput = numberOfOutputExpected; - actionThatCausesCompilation(); + actionThatCausesCompilation(); - // complete out block so that it produces output - _changeTracker.SourceBlock.Complete(); - await _changeTracker.SourceBlock.Completion; + // complete out block so that it produces output + _changeTracker.SourceBlock.Complete(); + await _changeTracker.SourceBlock.Completion; - // The timeout here is annoying, but even though our test is "smart" and waits for data, unfortunately if the code breaks the test is more likely to hang than fail - var delay = Task.Delay(TestTimeoutMillisecondsDelay); + // The timeout here is annoying, but even though our test is "smart" and waits for data, unfortunately if the code breaks the test is more likely to hang than fail + var delay = Task.Delay(TestTimeoutMillisecondsDelay); - if (await Task.WhenAny(_outputProducedSource.Task, delay) == delay) + if (await Task.WhenAny(_outputProducedSource.Task, delay) == delay) + { + if (_outputProduced.Count != numberOfOutputExpected) { - if (_outputProduced.Count != numberOfOutputExpected) - { - throw new AssertActualExpectedException(numberOfOutputExpected, _outputProduced.Count, $"Timed out after {TestTimeoutMillisecondsDelay}ms"); - } + throw new AssertActualExpectedException(numberOfOutputExpected, _outputProduced.Count, $"Timed out after {TestTimeoutMillisecondsDelay}ms"); } } + } - private void SendDesignTimeInputs(DesignTimeInputs inputs, string? intermediateOutputPath = null) - { - _intermediateOutputPath = intermediateOutputPath ?? _intermediateOutputPath; - - var ruleUpdate = - """ - { - "ProjectChanges": { - "ConfigurationGeneral": { - "Difference": { - "ChangedProperties": [ - - """; + private void SendDesignTimeInputs(DesignTimeInputs inputs, string? intermediateOutputPath = null) + { + _intermediateOutputPath = intermediateOutputPath ?? _intermediateOutputPath; - if (_lastIntermediateOutputPath != _intermediateOutputPath) + var ruleUpdate = + """ { - ruleUpdate += - """ - "IntermediateOutputPath", + "ProjectChanges": { + "ConfigurationGeneral": { + "Difference": { + "ChangedProperties": [ - """; - } + """; - // root namespace and project folder have changed if its the first time we've sent inputs - if (_lastIntermediateOutputPath is null) - { - ruleUpdate += - """ - "ProjectDir", - "RootNamespace" + if (_lastIntermediateOutputPath != _intermediateOutputPath) + { + ruleUpdate += + """ + "IntermediateOutputPath", - """; - } + """; + } - ruleUpdate = ruleUpdate.TrimEnd(','); + // root namespace and project folder have changed if its the first time we've sent inputs + if (_lastIntermediateOutputPath is null) + { ruleUpdate += - $$""" - ] - }, - "After": { - "Properties": { - "ProjectDir": "{{_projectFolder.Replace("\\", "\\\\")}}", - "IntermediateOutputPath": "{{_intermediateOutputPath.Replace("\\", "\\\\")}}", - "RootNamespace": "MyNamespace" - } + """ + "ProjectDir", + "RootNamespace" + + """; + } + + ruleUpdate = ruleUpdate.TrimEnd(','); + ruleUpdate += + $$""" + ] + }, + "After": { + "Properties": { + "ProjectDir": "{{_projectFolder.Replace("\\", "\\\\")}}", + "IntermediateOutputPath": "{{_intermediateOutputPath.Replace("\\", "\\\\")}}", + "RootNamespace": "MyNamespace" } } } } - """; - IProjectSubscriptionUpdate subscriptionUpdate = IProjectSubscriptionUpdateFactory.FromJson(ruleUpdate); + } + """; + IProjectSubscriptionUpdate subscriptionUpdate = IProjectSubscriptionUpdateFactory.FromJson(ruleUpdate); - _lastIntermediateOutputPath = _intermediateOutputPath; + _lastIntermediateOutputPath = _intermediateOutputPath; - _changeTracker.ProcessDataflowChanges(new ProjectVersionedValue>(new ValueTuple(inputs, subscriptionUpdate), Empty.ProjectValueVersions)); - } + _changeTracker.ProcessDataflowChanges(new ProjectVersionedValue>(new ValueTuple(inputs, subscriptionUpdate), Empty.ProjectValueVersions)); + } - private void SendFileChange(params string[] files) - { - _changeTracker.ProcessFileChangeNotification(new ProjectVersionedValue(files, Empty.ProjectValueVersions)); - } + private void SendFileChange(params string[] files) + { + _changeTracker.ProcessFileChangeNotification(new ProjectVersionedValue(files, Empty.ProjectValueVersions)); + } - public void Dispose() - { - _changeTracker.Dispose(); - } + public void Dispose() + { + _changeTracker.Dispose(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueueTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueueTests.cs index 4bfe9e0a42..e015102a0c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueueTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompiler.CompilationQueueTests.cs @@ -2,129 +2,128 @@ using static Microsoft.VisualStudio.ProjectSystem.VS.TempPE.DesignTimeInputsCompiler; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +public class CompilationQueueTests { - public class CompilationQueueTests + [Fact] + public void Push_Adds() { - [Fact] - public void Push_Adds() - { - var queue = new CompilationQueue(); + var queue = new CompilationQueue(); + + queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); - queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); + Assert.Equal(1, queue.Count); + } - Assert.Equal(1, queue.Count); - } + [Fact] + public void Pop_Removes() + { + var queue = new CompilationQueue(); - [Fact] - public void Pop_Removes() - { - var queue = new CompilationQueue(); + queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); - queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); + queue.Pop(); - queue.Pop(); + Assert.Equal(0, queue.Count); + } - Assert.Equal(0, queue.Count); - } + [Fact] + public void Pop_WhenEmpty_ReturnsNull() + { + var queue = new CompilationQueue(); - [Fact] - public void Pop_WhenEmpty_ReturnsNull() - { - var queue = new CompilationQueue(); + Assert.Null(queue.Pop()); + } - Assert.Null(queue.Pop()); - } + [Fact] + public void Push_UpdatesFileWriteTime() + { + var queue = new CompilationQueue(); - [Fact] - public void Push_UpdatesFileWriteTime() - { - var queue = new CompilationQueue(); + queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); - queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); + queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", true)); - queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", true)); + Assert.Equal(1, queue.Count); + Assert.True(queue.Pop()?.IgnoreFileWriteTime); + } - Assert.Equal(1, queue.Count); - Assert.True(queue.Pop()?.IgnoreFileWriteTime); - } + [Fact] + public void RemoveSpecific_Removes() + { + var queue = new CompilationQueue(); - [Fact] - public void RemoveSpecific_Removes() - { - var queue = new CompilationQueue(); + queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); - queue.Push(new QueueItem("FileName", ImmutableHashSet.Empty, "", false)); + queue.RemoveSpecific("FileName"); - queue.RemoveSpecific("FileName"); + Assert.Equal(0, queue.Count); + } - Assert.Equal(0, queue.Count); - } + [Fact] + public void Update_Adds() + { + var queue = new CompilationQueue(); - [Fact] - public void Update_Adds() + var range = ImmutableArray.CreateRange(new[] { - var queue = new CompilationQueue(); + new DesignTimeInputFileChange("FileName1", false), + new DesignTimeInputFileChange("FileName2", false) + }); - var range = ImmutableArray.CreateRange(new[] - { - new DesignTimeInputFileChange("FileName1", false), - new DesignTimeInputFileChange("FileName2", false) - }); + queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1", "FileName2" }), ImmutableHashSet.Empty, ""); - queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1", "FileName2" }), ImmutableHashSet.Empty, ""); + Assert.Equal(2, queue.Count); + } - Assert.Equal(2, queue.Count); - } + [Fact] + public void Update_WIthDuplicates_Ignored() + { + var queue = new CompilationQueue(); - [Fact] - public void Update_WIthDuplicates_Ignored() + var range = ImmutableArray.CreateRange(new[] { - var queue = new CompilationQueue(); + new DesignTimeInputFileChange("FileName1", false), + new DesignTimeInputFileChange("FileName1", false) + }); - var range = ImmutableArray.CreateRange(new[] - { - new DesignTimeInputFileChange("FileName1", false), - new DesignTimeInputFileChange("FileName1", false) - }); + queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1" }), ImmutableHashSet.Empty, ""); - queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1" }), ImmutableHashSet.Empty, ""); + Assert.Equal(1, queue.Count); + } - Assert.Equal(1, queue.Count); - } + [Fact] + public void Update_UpdatesFileWriteTime() + { + var queue = new CompilationQueue(); - [Fact] - public void Update_UpdatesFileWriteTime() + var range = ImmutableArray.CreateRange(new[] { - var queue = new CompilationQueue(); + new DesignTimeInputFileChange("FileName1", false), + new DesignTimeInputFileChange("FileName1", true) + }); - var range = ImmutableArray.CreateRange(new[] - { - new DesignTimeInputFileChange("FileName1", false), - new DesignTimeInputFileChange("FileName1", true) - }); + queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1" }), ImmutableHashSet.Empty, ""); - queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1" }), ImmutableHashSet.Empty, ""); + Assert.Equal(1, queue.Count); + Assert.True(queue.Pop()?.IgnoreFileWriteTime); + } - Assert.Equal(1, queue.Count); - Assert.True(queue.Pop()?.IgnoreFileWriteTime); - } + [Fact] + public void Update_RemovesItems() + { + var queue = new CompilationQueue(); - [Fact] - public void Update_RemovesItems() + var range = ImmutableArray.CreateRange(new[] { - var queue = new CompilationQueue(); - - var range = ImmutableArray.CreateRange(new[] - { - new DesignTimeInputFileChange("FileName1", false), - new DesignTimeInputFileChange("FileName2", false) - }); + new DesignTimeInputFileChange("FileName1", false), + new DesignTimeInputFileChange("FileName2", false) + }); - queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1" }), ImmutableHashSet.Empty, ""); + queue.Update(range, ImmutableHashSet.CreateRange(new string[] { "FileName1" }), ImmutableHashSet.Empty, ""); - Assert.Equal(1, queue.Count); - Assert.Equal("FileName1", queue.Pop()?.FileName); - } + Assert.Equal(1, queue.Count); + Assert.Equal("FileName1", queue.Pop()?.FileName); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompilerTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompilerTests.cs index f5a68d9d10..f0af8438a6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompilerTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsCompilerTests.cs @@ -6,53 +6,53 @@ using Microsoft.VisualStudio.Telemetry; using Xunit.Sdk; -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +public class DesignTimeInputsCompilerTests : IDisposable { - public class DesignTimeInputsCompilerTests : IDisposable + private const int TestTimeoutMillisecondsDelay = 1000; + + private DesignTimeInputs? _designTimeInputs; + private readonly string _projectFolder = @"C:\MyProject"; + private readonly string _outputPath = @"C:\MyProject\MyOutput\TempPE"; + private readonly IFileSystemMock _fileSystem; + private readonly DesignTimeInputsCompiler _manager; + + // For tracking compilation events that occur, to verify + private readonly List<(string OutputFileName, string[] SourceFiles)> _compilationResults = new(); + private TaskCompletionSource? _compilationOccurredCompletionSource; + private int _expectedCompilations; + private Func, bool> _compilationCallback; + + [Fact] + public async Task SingleDesignTimeInput_ShouldCompile() { - private const int TestTimeoutMillisecondsDelay = 1000; - - private DesignTimeInputs? _designTimeInputs; - private readonly string _projectFolder = @"C:\MyProject"; - private readonly string _outputPath = @"C:\MyProject\MyOutput\TempPE"; - private readonly IFileSystemMock _fileSystem; - private readonly DesignTimeInputsCompiler _manager; - - // For tracking compilation events that occur, to verify - private readonly List<(string OutputFileName, string[] SourceFiles)> _compilationResults = new(); - private TaskCompletionSource? _compilationOccurredCompletionSource; - private int _expectedCompilations; - private Func, bool> _compilationCallback; - - [Fact] - public async Task SingleDesignTimeInput_ShouldCompile() - { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(1, inputs); + await VerifyCompilation(1, inputs); - Assert.Single(_compilationResults); - Assert.Single(_compilationResults[0].SourceFiles); - Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); - } + Assert.Single(_compilationResults); + Assert.Single(_compilationResults[0].SourceFiles); + Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); + } - [Fact] - public async Task SingleDesignTimeInput_OutputDoesntExist_ShouldCompile() - { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_OutputDoesntExist_ShouldCompile() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(1, inputs); + await VerifyCompilation(1, inputs); - string tempPEDescriptionXml = await _manager.BuildDesignTimeOutputAsync("File1.cs", _outputPath, ImmutableHashSet.Empty); + string tempPEDescriptionXml = await _manager.BuildDesignTimeOutputAsync("File1.cs", _outputPath, ImmutableHashSet.Empty); - // This also validates that getting the description didn't force a compile, because the output is up to date - Assert.Single(_compilationResults); - Assert.Single(_compilationResults[0].SourceFiles); - Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); + // This also validates that getting the description didn't force a compile, because the output is up to date + Assert.Single(_compilationResults); + Assert.Single(_compilationResults[0].SourceFiles); + Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); - Assert.Equal(@" + Assert.Equal(@" ", tempPEDescriptionXml); - } + } - [Fact] - public async Task SingleDesignTimeInput_OutputDoesntExist_ShouldCompileWhenGettingXML() - { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_OutputDoesntExist_ShouldCompileWhenGettingXML() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(1, inputs); + await VerifyCompilation(1, inputs); - // If the first compile didn't happen, our test results won't be valid - Assert.Single(_compilationResults); - Assert.Single(_compilationResults[0].SourceFiles); - Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); + // If the first compile didn't happen, our test results won't be valid + Assert.Single(_compilationResults); + Assert.Single(_compilationResults[0].SourceFiles); + Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); - // Remove the output file, should mean that getting the XML forces a compile - _fileSystem.RemoveFile(Path.Combine(_outputPath, "File1.cs.dll")); + // Remove the output file, should mean that getting the XML forces a compile + _fileSystem.RemoveFile(Path.Combine(_outputPath, "File1.cs.dll")); - string tempPEDescriptionXml = await _manager.BuildDesignTimeOutputAsync("File1.cs", _outputPath, ImmutableHashSet.Empty); + string tempPEDescriptionXml = await _manager.BuildDesignTimeOutputAsync("File1.cs", _outputPath, ImmutableHashSet.Empty); - // Verify a second compile happened - Assert.Equal(2, _compilationResults.Count); - Assert.Single(_compilationResults[1].SourceFiles); - Assert.Contains("File1.cs", _compilationResults[1].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[1].OutputFileName); + // Verify a second compile happened + Assert.Equal(2, _compilationResults.Count); + Assert.Single(_compilationResults[1].SourceFiles); + Assert.Contains("File1.cs", _compilationResults[1].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[1].OutputFileName); - Assert.Equal(@" + Assert.Equal(@" ", tempPEDescriptionXml); - } - - [Fact] - public async Task SingleDesignTimeInput_Changes_ShouldCompileTwice() - { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + } - await VerifyCompilation(1, inputs); + [Fact] + public async Task SingleDesignTimeInput_Changes_ShouldCompileTwice() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(1, "File1.cs"); + await VerifyCompilation(1, inputs); - Assert.Equal(2, _compilationResults.Count); - Assert.Single(_compilationResults[0].SourceFiles); - Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); + await VerifyCompilation(1, "File1.cs"); - Assert.Single(_compilationResults[1].SourceFiles); - Assert.Contains("File1.cs", _compilationResults[1].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[1].OutputFileName); - } + Assert.Equal(2, _compilationResults.Count); + Assert.Single(_compilationResults[0].SourceFiles); + Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); - [Fact] - public async Task NoDesignTimeInputs_NeverCompiles() - { - var inputs = new DesignTimeInputs(new string[] { }, new string[] { }); + Assert.Single(_compilationResults[1].SourceFiles); + Assert.Contains("File1.cs", _compilationResults[1].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[1].OutputFileName); + } - await VerifyCompilation(0, inputs); + [Fact] + public async Task NoDesignTimeInputs_NeverCompiles() + { + var inputs = new DesignTimeInputs(new string[] { }, new string[] { }); - Assert.Empty(_compilationResults); - } + await VerifyCompilation(0, inputs); - [Fact] - public async Task SingleDesignTimeInput_OutputUpToDate_ShouldntCompile() - { - _fileSystem.AddFile(Path.Combine(_outputPath, "File1.cs.dll"), DateTime.UtcNow.AddMinutes(10)); + Assert.Empty(_compilationResults); + } - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_OutputUpToDate_ShouldntCompile() + { + _fileSystem.AddFile(Path.Combine(_outputPath, "File1.cs.dll"), DateTime.UtcNow.AddMinutes(10)); - await VerifyCompilation(0, inputs); + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - Assert.Empty(_compilationResults); - } + await VerifyCompilation(0, inputs); - [Fact] - public async Task SingleDesignTimeInput_OutputOutOfDate_ShouldCompile() - { - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + Assert.Empty(_compilationResults); + } - _fileSystem.AddFile(Path.Combine(_outputPath, "File1.cs.dll"), DateTime.UtcNow.AddMinutes(-10)); + [Fact] + public async Task SingleDesignTimeInput_OutputOutOfDate_ShouldCompile() + { + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(1, inputs); + _fileSystem.AddFile(Path.Combine(_outputPath, "File1.cs.dll"), DateTime.UtcNow.AddMinutes(-10)); - Assert.Single(_compilationResults); - Assert.Single(_compilationResults[0].SourceFiles); - } + await VerifyCompilation(1, inputs); - [Fact] - public async Task SingleDesignTimeInput_Removed_ShouldntCompile() - { - _manager.CompileSynchronously = false; + Assert.Single(_compilationResults); + Assert.Single(_compilationResults[0].SourceFiles); + } - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_Removed_ShouldntCompile() + { + _manager.CompileSynchronously = false; - await VerifyDLLsCompiled(0, () => - { - SendDesignTimeInputs(inputs); + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - SendDesignTimeInputs(new DesignTimeInputs(new string[] { }, new string[] { })); - }); + await VerifyDLLsCompiled(0, () => + { + SendDesignTimeInputs(inputs); - Assert.Empty(_compilationResults); - } + SendDesignTimeInputs(new DesignTimeInputs(new string[] { }, new string[] { })); + }); - [Fact] - public async Task SingleDesignTimeInput_AnotherAdded_ShouldCompileBoth() - { - await VerifyDLLsCompiled(2, () => - { - SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { })); - - SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { })); - }); - - Assert.Equal(2, _compilationResults.Count); - Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); - Assert.DoesNotContain("File2.cs", _compilationResults[0].SourceFiles); - Assert.Contains("File2.cs", _compilationResults[1].SourceFiles); - Assert.DoesNotContain("File1.cs", _compilationResults[1].SourceFiles); - Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); - Assert.Equal(Path.Combine(_outputPath, "File2.cs.dll"), _compilationResults[1].OutputFileName); - } + Assert.Empty(_compilationResults); + } - [Fact] - public async Task SingleDesignTimeInput_CompileFailed_ShouldDeleteOutputFile() + [Fact] + public async Task SingleDesignTimeInput_AnotherAdded_ShouldCompileBoth() + { + await VerifyDLLsCompiled(2, () => { - var outputPath = Path.Combine(_outputPath, "File1.cs.dll"); - _fileSystem.AddFile(outputPath, DateTime.UtcNow.AddMinutes(-10)); + SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { })); + + SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { })); + }); + + Assert.Equal(2, _compilationResults.Count); + Assert.Contains("File1.cs", _compilationResults[0].SourceFiles); + Assert.DoesNotContain("File2.cs", _compilationResults[0].SourceFiles); + Assert.Contains("File2.cs", _compilationResults[1].SourceFiles); + Assert.DoesNotContain("File1.cs", _compilationResults[1].SourceFiles); + Assert.Equal(Path.Combine(_outputPath, "File1.cs.dll"), _compilationResults[0].OutputFileName); + Assert.Equal(Path.Combine(_outputPath, "File2.cs.dll"), _compilationResults[1].OutputFileName); + } - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_CompileFailed_ShouldDeleteOutputFile() + { + var outputPath = Path.Combine(_outputPath, "File1.cs.dll"); + _fileSystem.AddFile(outputPath, DateTime.UtcNow.AddMinutes(-10)); - // We want our compilation to fail - _compilationCallback = (x, z) => false; + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(0, inputs); + // We want our compilation to fail + _compilationCallback = (x, z) => false; - Assert.Empty(_compilationResults); - Assert.False(_fileSystem.FileExists(outputPath)); - } + await VerifyCompilation(0, inputs); - [Fact] - public async Task SingleDesignTimeInput_CompileCancelled_ShouldDeleteOutputFile() - { - var outputPath = Path.Combine(_outputPath, "File1.cs.dll"); - _fileSystem.AddFile(outputPath, DateTime.UtcNow.AddMinutes(-10)); + Assert.Empty(_compilationResults); + Assert.False(_fileSystem.FileExists(outputPath)); + } - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_CompileCancelled_ShouldDeleteOutputFile() + { + var outputPath = Path.Combine(_outputPath, "File1.cs.dll"); + _fileSystem.AddFile(outputPath, DateTime.UtcNow.AddMinutes(-10)); - // We want our compilation to throw - _compilationCallback = (x, z) => throw new OperationCanceledException("Boom!"); + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(0, inputs); + // We want our compilation to throw + _compilationCallback = (x, z) => throw new OperationCanceledException("Boom!"); - Assert.Empty(_compilationResults); - Assert.False(_fileSystem.FileExists(outputPath)); - } + await VerifyCompilation(0, inputs); - [Fact] - public async Task SingleDesignTimeInput_CompileThrows_ShouldDeleteOutputFile() - { - var outputPath = Path.Combine(_outputPath, "File1.cs.dll"); - _fileSystem.AddFile(outputPath, DateTime.UtcNow.AddMinutes(-10)); + Assert.Empty(_compilationResults); + Assert.False(_fileSystem.FileExists(outputPath)); + } - var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); + [Fact] + public async Task SingleDesignTimeInput_CompileThrows_ShouldDeleteOutputFile() + { + var outputPath = Path.Combine(_outputPath, "File1.cs.dll"); + _fileSystem.AddFile(outputPath, DateTime.UtcNow.AddMinutes(-10)); - // We want our compilation to throw - _compilationCallback = (x, z) => throw new IOException("Boom!"); + var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); - await VerifyCompilation(0, inputs); + // We want our compilation to throw + _compilationCallback = (x, z) => throw new IOException("Boom!"); - Assert.Empty(_compilationResults); - Assert.False(_fileSystem.FileExists(outputPath)); - } + await VerifyCompilation(0, inputs); - public DesignTimeInputsCompilerTests() - { - _compilationCallback = CompilationCallBack; - - _fileSystem = new IFileSystemMock(); - - var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); - using var designTimeInputsSource = ProjectValueDataSourceFactory.Create(services); - - var changeTrackerMock = new Mock(); - changeTrackerMock.SetupGet(s => s.SourceBlock) - .Returns(designTimeInputsSource.SourceBlock); - - var telemetryService = ITelemetryServiceFactory.Create(); - var threadingService = IProjectThreadingServiceFactory.Create(); - var workspaceWriter = IWorkspaceWriterFactory.ImplementProjectContextAccessor(IWorkspaceMockFactory.Create()); - var unconfiguredProject = UnconfiguredProjectFactory.Create( - fullPath: Path.Combine(_projectFolder, "MyTestProj.csproj"), - projectAsynchronousTasksService: IProjectAsynchronousTasksServiceFactory.Create()); - - var compilerMock = new Mock(); - compilerMock.Setup(c => c.CompileAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) - .ReturnsAsync((IWorkspaceProjectContext context, string outputFile, ISet filesToCompile, CancellationToken token) => _compilationCallback(outputFile, filesToCompile)); - - _manager = new DesignTimeInputsCompiler(unconfiguredProject, - workspaceWriter, - threadingService, - changeTrackerMock.Object, - compilerMock.Object, - _fileSystem, - telemetryService) - { - CompileSynchronously = true - }; - } + Assert.Empty(_compilationResults); + Assert.False(_fileSystem.FileExists(outputPath)); + } - private bool CompilationCallBack(string output, ISet files) + public DesignTimeInputsCompilerTests() + { + _compilationCallback = CompilationCallBack; + + _fileSystem = new IFileSystemMock(); + + var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); + using var designTimeInputsSource = ProjectValueDataSourceFactory.Create(services); + + var changeTrackerMock = new Mock(); + changeTrackerMock.SetupGet(s => s.SourceBlock) + .Returns(designTimeInputsSource.SourceBlock); + + var telemetryService = ITelemetryServiceFactory.Create(); + var threadingService = IProjectThreadingServiceFactory.Create(); + var workspaceWriter = IWorkspaceWriterFactory.ImplementProjectContextAccessor(IWorkspaceMockFactory.Create()); + var unconfiguredProject = UnconfiguredProjectFactory.Create( + fullPath: Path.Combine(_projectFolder, "MyTestProj.csproj"), + projectAsynchronousTasksService: IProjectAsynchronousTasksServiceFactory.Create()); + + var compilerMock = new Mock(); + compilerMock.Setup(c => c.CompileAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .ReturnsAsync((IWorkspaceProjectContext context, string outputFile, ISet filesToCompile, CancellationToken token) => _compilationCallback(outputFile, filesToCompile)); + + _manager = new DesignTimeInputsCompiler(unconfiguredProject, + workspaceWriter, + threadingService, + changeTrackerMock.Object, + compilerMock.Object, + _fileSystem, + telemetryService) { - // "Create" our output file - _fileSystem.AddFile(output); - - _compilationResults.Add((output, files.Select(f => Path.GetFileName(f)).ToArray())); - if (_compilationResults.Count == _expectedCompilations) - { - _compilationOccurredCompletionSource?.SetResult(); - } + CompileSynchronously = true + }; + } - return true; - } + private bool CompilationCallBack(string output, ISet files) + { + // "Create" our output file + _fileSystem.AddFile(output); - private async Task VerifyCompilation(int numberOfDLLsExpected, DesignTimeInputs designTimeInputs) + _compilationResults.Add((output, files.Select(f => Path.GetFileName(f)).ToArray())); + if (_compilationResults.Count == _expectedCompilations) { - await VerifyDLLsCompiled(numberOfDLLsExpected, () => SendDesignTimeInputs(inputs: designTimeInputs)); + _compilationOccurredCompletionSource?.SetResult(); } - private async Task VerifyCompilation(int numberOfDLLsExpected, params string[] filesToChange) - { - await Task.Delay(1); + return true; + } - // Short delay so that files are actually newer than any previous output, since tests run fast - Thread.Sleep(1); + private async Task VerifyCompilation(int numberOfDLLsExpected, DesignTimeInputs designTimeInputs) + { + await VerifyDLLsCompiled(numberOfDLLsExpected, () => SendDesignTimeInputs(inputs: designTimeInputs)); + } - // Ensure our input files are in, and up to date, in the mock file system - foreach (string file in filesToChange) - { - var fullFilePath = Path.Combine(_projectFolder, file); - _fileSystem.AddFile(fullFilePath); - } + private async Task VerifyCompilation(int numberOfDLLsExpected, params string[] filesToChange) + { + await Task.Delay(1); - await VerifyDLLsCompiled(numberOfDLLsExpected, () => SendDesignTimeInputs(changedFiles: filesToChange)); - } + // Short delay so that files are actually newer than any previous output, since tests run fast + Thread.Sleep(1); - private async Task VerifyDLLsCompiled(int numberOfDLLsExpected, Action actionThatCausesCompilation) + // Ensure our input files are in, and up to date, in the mock file system + foreach (string file in filesToChange) { - int initialCompilations = _compilationResults.Count; - _expectedCompilations = initialCompilations + numberOfDLLsExpected; - _compilationOccurredCompletionSource = new TaskCompletionSource(); + var fullFilePath = Path.Combine(_projectFolder, file); + _fileSystem.AddFile(fullFilePath); + } - actionThatCausesCompilation(); + await VerifyDLLsCompiled(numberOfDLLsExpected, () => SendDesignTimeInputs(changedFiles: filesToChange)); + } + + private async Task VerifyDLLsCompiled(int numberOfDLLsExpected, Action actionThatCausesCompilation) + { + int initialCompilations = _compilationResults.Count; + _expectedCompilations = initialCompilations + numberOfDLLsExpected; + _compilationOccurredCompletionSource = new TaskCompletionSource(); - // Sadly, we need a timeout - var delay = Task.Delay(TestTimeoutMillisecondsDelay); + actionThatCausesCompilation(); - if (await Task.WhenAny(_compilationOccurredCompletionSource.Task, delay) == delay) + // Sadly, we need a timeout + var delay = Task.Delay(TestTimeoutMillisecondsDelay); + + if (await Task.WhenAny(_compilationOccurredCompletionSource.Task, delay) == delay) + { + var actualDLLs = _compilationResults.Count - initialCompilations; + if (numberOfDLLsExpected != actualDLLs) { - var actualDLLs = _compilationResults.Count - initialCompilations; - if (numberOfDLLsExpected != actualDLLs) - { - throw new AssertActualExpectedException(numberOfDLLsExpected, actualDLLs, $"Timed out after {TestTimeoutMillisecondsDelay}ms"); - } + throw new AssertActualExpectedException(numberOfDLLsExpected, actualDLLs, $"Timed out after {TestTimeoutMillisecondsDelay}ms"); } } + } - private void SendDesignTimeInputs(DesignTimeInputs? inputs = null, string[]? changedFiles = null) + private void SendDesignTimeInputs(DesignTimeInputs? inputs = null, string[]? changedFiles = null) + { + // Make everything full paths here, to allow for easier test authoring + if (inputs is not null) { - // Make everything full paths here, to allow for easier test authoring - if (inputs is not null) - { - inputs = new DesignTimeInputs(inputs.Inputs.Select(f => Path.Combine(_projectFolder, f)), inputs.SharedInputs.Select(f => Path.Combine(_projectFolder, f))); - } + inputs = new DesignTimeInputs(inputs.Inputs.Select(f => Path.Combine(_projectFolder, f)), inputs.SharedInputs.Select(f => Path.Combine(_projectFolder, f))); + } - _designTimeInputs = inputs ?? _designTimeInputs!; + _designTimeInputs = inputs ?? _designTimeInputs!; - // Ensure our input files are in the mock file system - foreach (string file in _designTimeInputs.Inputs.Concat(_designTimeInputs.SharedInputs)) - { - if (!_fileSystem.FileExists(file)) - { - _fileSystem.AddFile(file); - } - } - - IEnumerable changes; - if (changedFiles is not null) - { - changes = changedFiles.Select(f => new DesignTimeInputFileChange(Path.Combine(_projectFolder, f), false)); - } - else + // Ensure our input files are in the mock file system + foreach (string file in _designTimeInputs.Inputs.Concat(_designTimeInputs.SharedInputs)) + { + if (!_fileSystem.FileExists(file)) { - changes = _designTimeInputs.Inputs.Select(f => new DesignTimeInputFileChange(f, false)); + _fileSystem.AddFile(file); } - - _manager.ProcessDataflowChanges(new ProjectVersionedValue(new DesignTimeInputSnapshot(_designTimeInputs.Inputs, _designTimeInputs.SharedInputs, changes, _outputPath), Empty.ProjectValueVersions)); } - public void Dispose() + IEnumerable changes; + if (changedFiles is not null) { - _manager.Dispose(); + changes = changedFiles.Select(f => new DesignTimeInputFileChange(Path.Combine(_projectFolder, f), false)); } + else + { + changes = _designTimeInputs.Inputs.Select(f => new DesignTimeInputFileChange(f, false)); + } + + _manager.ProcessDataflowChanges(new ProjectVersionedValue(new DesignTimeInputSnapshot(_designTimeInputs.Inputs, _designTimeInputs.SharedInputs, changes, _outputPath), Empty.ProjectValueVersions)); + } + + public void Dispose() + { + _manager.Dispose(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsDataSourceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsDataSourceTests.cs index 86f69a2f19..1d0392c209 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsDataSourceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsDataSourceTests.cs @@ -4,176 +4,175 @@ // MemberData doesn't work anyway #nullable disable -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +public class DesignTimeInputsDataSourceTests { - public class DesignTimeInputsDataSourceTests + public static IEnumerable GetTestCases() { - public static IEnumerable GetTestCases() + return new[] { - return new[] + // A single design time input + new object[] { - // A single design time input - new object[] - { - """ - "CurrentState": { - "Compile": { - "Items": { - "File1.cs": { - "DesignTime": true, - "FullPath": "C:\\Project\\File1.cs" - } + """ + "CurrentState": { + "Compile": { + "Items": { + "File1.cs": { + "DesignTime": true, + "FullPath": "C:\\Project\\File1.cs" } } } - """, - new string[] { "C:\\Project\\File1.cs" }, - new string[] { } - }, - - // A single design time input, and a normal file - new object[] - { - """ - "CurrentState": { - "Compile": { - "Items": { - "File1.cs": { - "DesignTime": true, - "FullPath": "C:\\Project\\File1.cs" - }, - "File2.cs": { - "FullPath": "C:\\Project\\File2.cs" - } + } + """, + new string[] { "C:\\Project\\File1.cs" }, + new string[] { } + }, + + // A single design time input, and a normal file + new object[] + { + """ + "CurrentState": { + "Compile": { + "Items": { + "File1.cs": { + "DesignTime": true, + "FullPath": "C:\\Project\\File1.cs" + }, + "File2.cs": { + "FullPath": "C:\\Project\\File2.cs" } } } - """, - new string[] { "C:\\Project\\File1.cs" }, - new string[] { } - }, - - // A single design time input, and a single shared design time input - new object[] - { - """ - "CurrentState": { - "Compile": { - "Items": { - "File1.cs": { - "DesignTime": true, - "FullPath": "C:\\Project\\File1.cs" - }, - "File2.cs": { - "DesignTimeSharedInput": true, - "FullPath": "C:\\Project\\File2.cs" - } + } + """, + new string[] { "C:\\Project\\File1.cs" }, + new string[] { } + }, + + // A single design time input, and a single shared design time input + new object[] + { + """ + "CurrentState": { + "Compile": { + "Items": { + "File1.cs": { + "DesignTime": true, + "FullPath": "C:\\Project\\File1.cs" + }, + "File2.cs": { + "DesignTimeSharedInput": true, + "FullPath": "C:\\Project\\File2.cs" } } } - """, - new string[] { "C:\\Project\\File1.cs" }, - new string[] { "C:\\Project\\File2.cs" } - }, - - // A file that is both a design time and shared design time input - new object[] - { - """ - "CurrentState": { - "Compile": { - "Items": { - "File1.cs": { - "DesignTime": true, - "DesignTimeSharedInput": true, - "FullPath": "C:\\Project\\File1.cs" - } + } + """, + new string[] { "C:\\Project\\File1.cs" }, + new string[] { "C:\\Project\\File2.cs" } + }, + + // A file that is both a design time and shared design time input + new object[] + { + """ + "CurrentState": { + "Compile": { + "Items": { + "File1.cs": { + "DesignTime": true, + "DesignTimeSharedInput": true, + "FullPath": "C:\\Project\\File1.cs" } } } - """, - new string[] { "C:\\Project\\File1.cs" }, - new string[] { "C:\\Project\\File1.cs" } - }, - - // A design time input that is a linked file, and hence ignored - new object[] - { - """ - "CurrentState": { - "Compile": { - "Items": { - "File1.cs": { - "DesignTime": true, - "Link": "foo", - "FullPath": "C:\\Project\\File1.cs" - } + } + """, + new string[] { "C:\\Project\\File1.cs" }, + new string[] { "C:\\Project\\File1.cs" } + }, + + // A design time input that is a linked file, and hence ignored + new object[] + { + """ + "CurrentState": { + "Compile": { + "Items": { + "File1.cs": { + "DesignTime": true, + "Link": "foo", + "FullPath": "C:\\Project\\File1.cs" } } } - """, - new string[] { }, - new string[] { } - }, - }; - } - - [Theory] - [MemberData(nameof(GetTestCases))] - public async Task VerifyDesignTimeInputsProcessed(string projectState, string[] designTimeInputs, string[] sharedDesignTimeInputs) - { - using DesignTimeInputsDataSource dataSource = CreateDesignTimeInputsDataSource(out ProjectValueDataSource sourceItemsRuleSource); + } + """, + new string[] { }, + new string[] { } + }, + }; + } - const string defaultProjectConfig = - """ - "ProjectConfiguration": { - "Name": "Debug|AnyCPU", - "Dimensions": { - "Configuration": "Debug", - "Platform": "AnyCPU" - } + [Theory] + [MemberData(nameof(GetTestCases))] + public async Task VerifyDesignTimeInputsProcessed(string projectState, string[] designTimeInputs, string[] sharedDesignTimeInputs) + { + using DesignTimeInputsDataSource dataSource = CreateDesignTimeInputsDataSource(out ProjectValueDataSource sourceItemsRuleSource); + + const string defaultProjectConfig = + """ + "ProjectConfiguration": { + "Name": "Debug|AnyCPU", + "Dimensions": { + "Configuration": "Debug", + "Platform": "AnyCPU" } - """; + } + """; - // Create a block to receive the results of the block under test - DesignTimeInputs inputs = null; - var receiver = DataflowBlockSlim.CreateActionBlock>(val => - { - inputs = val.Value; - }); - dataSource.SourceBlock.LinkTo(receiver, DataflowOption.PropagateCompletion); + // Create a block to receive the results of the block under test + DesignTimeInputs inputs = null; + var receiver = DataflowBlockSlim.CreateActionBlock>(val => + { + inputs = val.Value; + }); + dataSource.SourceBlock.LinkTo(receiver, DataflowOption.PropagateCompletion); - // Construct our input value, including a default project config - var configUpdate = IProjectSubscriptionUpdateFactory.FromJson("{ " + projectState + "," + defaultProjectConfig + " }"); + // Construct our input value, including a default project config + var configUpdate = IProjectSubscriptionUpdateFactory.FromJson("{ " + projectState + "," + defaultProjectConfig + " }"); - // Send our input, and wait for our receiver to complete - await sourceItemsRuleSource.SendAndCompleteAsync(configUpdate, receiver); + // Send our input, and wait for our receiver to complete + await sourceItemsRuleSource.SendAndCompleteAsync(configUpdate, receiver); - // Assert - Assert.NotNull(inputs); - Assert.Equal(designTimeInputs, inputs.Inputs); - Assert.Equal(sharedDesignTimeInputs, inputs.SharedInputs); - } + // Assert + Assert.NotNull(inputs); + Assert.Equal(designTimeInputs, inputs.Inputs); + Assert.Equal(sharedDesignTimeInputs, inputs.SharedInputs); + } - private static DesignTimeInputsDataSource CreateDesignTimeInputsDataSource(out ProjectValueDataSource sourceItemsRuleSource) - { - var unconfiguredProjectServices = UnconfiguredProjectServicesFactory.Create( - projectService: IProjectServiceFactory.Create( - services: ProjectServicesFactory.Create( - threadingService: IProjectThreadingServiceFactory.Create(), - projectLockService: IProjectLockServiceFactory.Create()))); + private static DesignTimeInputsDataSource CreateDesignTimeInputsDataSource(out ProjectValueDataSource sourceItemsRuleSource) + { + var unconfiguredProjectServices = UnconfiguredProjectServicesFactory.Create( + projectService: IProjectServiceFactory.Create( + services: ProjectServicesFactory.Create( + threadingService: IProjectThreadingServiceFactory.Create(), + projectLockService: IProjectLockServiceFactory.Create()))); - var unconfiguredProject = UnconfiguredProjectFactory.Create( - unconfiguredProjectServices: unconfiguredProjectServices, - fullPath: @"C:\Project\Project.csproj"); + var unconfiguredProject = UnconfiguredProjectFactory.Create( + unconfiguredProjectServices: unconfiguredProjectServices, + fullPath: @"C:\Project\Project.csproj"); - sourceItemsRuleSource = new ProjectValueDataSource(unconfiguredProjectServices); + sourceItemsRuleSource = new ProjectValueDataSource(unconfiguredProjectServices); - var projectSubscriptionService = IActiveConfiguredProjectSubscriptionServiceFactory.Create(sourceItemsRuleSource: sourceItemsRuleSource); + var projectSubscriptionService = IActiveConfiguredProjectSubscriptionServiceFactory.Create(sourceItemsRuleSource: sourceItemsRuleSource); - var dataSource = new DesignTimeInputsDataSource(unconfiguredProject, unconfiguredProjectServices, projectSubscriptionService); + var dataSource = new DesignTimeInputsDataSource(unconfiguredProject, unconfiguredProjectServices, projectSubscriptionService); - return dataSource; - } + return dataSource; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcherTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcherTests.cs index 515900c37d..6faac271c7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcherTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/TempPE/DesignTimeInputsFileWatcherTests.cs @@ -8,152 +8,151 @@ // MemberData doesn't work anyway #nullable disable -namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE +namespace Microsoft.VisualStudio.ProjectSystem.VS.TempPE; + +public class DesignTimeInputsFileWatcherTests { - public class DesignTimeInputsFileWatcherTests - { - private const int TestTimeoutMillisecondsDelay = 1000; + private const int TestTimeoutMillisecondsDelay = 1000; - public static IEnumerable GetTestCases() + public static IEnumerable GetTestCases() + { + return new[] { - return new[] + // A single design time input + new object[] { - // A single design time input - new object[] - { - new string[] { "File1.cs" }, // design time inputs - new string[] { }, // shared design time inputs - new string[] { "File1.cs" }, // expected watched files - new string[] { }, // file change notifications to send - new string[] { } // file change notifications expected - }, - - // A design time input and a shared design time input - new object[] - { - new string[] { "File1.cs" }, - new string[] { "File2.cs" }, - new string[] { "File1.cs", "File2.cs" }, - new string[] { }, - new string[] { } - }, - - // A file that is both design time and shared, should only be watched once - new object[] - { - new string[] { "File1.cs" }, - new string[] { "File2.cs", "File1.cs" }, - new string[] { "File1.cs", "File2.cs" }, - new string[] { }, - new string[] { } - }, - - // A design time input that gets modified - new object[] - { - new string[] { "File1.cs" }, - new string[] { }, - new string[] { "File1.cs" }, - new string[] { "File1.cs", "File1.cs", "File1.cs" }, - new string[] { "File1.cs", "File1.cs", "File1.cs" } - }, - - // A design time input and a shared design time input, that both change, to ensure ordering is correct - new object[] - { - new string[] { "File1.cs" }, - new string[] { "File2.cs" }, - new string[] { "File1.cs", "File2.cs" }, - new string[] { "File1.cs", "File2.cs" }, - new string[] { "File1.cs", "File2.cs" } - }, - }; - } + new string[] { "File1.cs" }, // design time inputs + new string[] { }, // shared design time inputs + new string[] { "File1.cs" }, // expected watched files + new string[] { }, // file change notifications to send + new string[] { } // file change notifications expected + }, + + // A design time input and a shared design time input + new object[] + { + new string[] { "File1.cs" }, + new string[] { "File2.cs" }, + new string[] { "File1.cs", "File2.cs" }, + new string[] { }, + new string[] { } + }, + + // A file that is both design time and shared, should only be watched once + new object[] + { + new string[] { "File1.cs" }, + new string[] { "File2.cs", "File1.cs" }, + new string[] { "File1.cs", "File2.cs" }, + new string[] { }, + new string[] { } + }, + + // A design time input that gets modified + new object[] + { + new string[] { "File1.cs" }, + new string[] { }, + new string[] { "File1.cs" }, + new string[] { "File1.cs", "File1.cs", "File1.cs" }, + new string[] { "File1.cs", "File1.cs", "File1.cs" } + }, + + // A design time input and a shared design time input, that both change, to ensure ordering is correct + new object[] + { + new string[] { "File1.cs" }, + new string[] { "File2.cs" }, + new string[] { "File1.cs", "File2.cs" }, + new string[] { "File1.cs", "File2.cs" }, + new string[] { "File1.cs", "File2.cs" } + }, + }; + } - [Theory] - [MemberData(nameof(GetTestCases))] - internal async Task VerifyDesignTimeInputsWatched(string[] designTimeInputs, string[] sharedDesignTimeInputs, string[] watchedFiles, string[] fileChangeNotificationsToSend, string[] fileChangeNotificationsExpected) - { - var fileChangeService = new IVsAsyncFileChangeExMock(); + [Theory] + [MemberData(nameof(GetTestCases))] + internal async Task VerifyDesignTimeInputsWatched(string[] designTimeInputs, string[] sharedDesignTimeInputs, string[] watchedFiles, string[] fileChangeNotificationsToSend, string[] fileChangeNotificationsExpected) + { + var fileChangeService = new IVsAsyncFileChangeExMock(); - using DesignTimeInputsFileWatcher watcher = CreateDesignTimeInputsFileWatcher(fileChangeService, out ProjectValueDataSource source); + using DesignTimeInputsFileWatcher watcher = CreateDesignTimeInputsFileWatcher(fileChangeService, out ProjectValueDataSource source); - watcher.AllowSourceBlockCompletion = true; + watcher.AllowSourceBlockCompletion = true; - // Send our input. DesignTimeInputs expects full file paths - await source.SendAsync(new DesignTimeInputs(designTimeInputs.Select(f => f), sharedDesignTimeInputs.Select(f => f))); + // Send our input. DesignTimeInputs expects full file paths + await source.SendAsync(new DesignTimeInputs(designTimeInputs.Select(f => f), sharedDesignTimeInputs.Select(f => f))); - // The TaskCompletionSource is the thing we use to wait for the test to finish - var finished = new TaskCompletionSource(); + // The TaskCompletionSource is the thing we use to wait for the test to finish + var finished = new TaskCompletionSource(); - int notificationCount = 0; - // Create a block to receive the output - var receiver = DataflowBlockSlim.CreateActionBlock>(val => + int notificationCount = 0; + // Create a block to receive the output + var receiver = DataflowBlockSlim.CreateActionBlock>(val => + { + foreach (string file in val.Value) { - foreach (string file in val.Value) - { - Assert.Equal(fileChangeNotificationsExpected[notificationCount++], file); - } + Assert.Equal(fileChangeNotificationsExpected[notificationCount++], file); + } - // if we've seen every file, we're done - if (notificationCount == fileChangeNotificationsExpected.Length) - { - finished.SetResult(); - } - }); - watcher.SourceBlock.LinkTo(receiver, DataflowOption.PropagateCompletion); + // if we've seen every file, we're done + if (notificationCount == fileChangeNotificationsExpected.Length) + { + finished.SetResult(); + } + }); + watcher.SourceBlock.LinkTo(receiver, DataflowOption.PropagateCompletion); - // Send down our fake file changes - watcher.FilesChanged((uint)fileChangeNotificationsToSend.Length, fileChangeNotificationsToSend, null); + // Send down our fake file changes + watcher.FilesChanged((uint)fileChangeNotificationsToSend.Length, fileChangeNotificationsToSend, null); - source.SourceBlock.Complete(); + source.SourceBlock.Complete(); - await source.SourceBlock.Completion; + await source.SourceBlock.Completion; - watcher.SourceBlock.Complete(); + watcher.SourceBlock.Complete(); - await watcher.SourceBlock.Completion; + await watcher.SourceBlock.Completion; - // The timeout here is annoying, but even though our test is "smart" and waits for data, unfortunately if the code breaks the test is more likely to hang than fail - if (await Task.WhenAny(finished.Task, Task.Delay(TestTimeoutMillisecondsDelay)) != finished.Task) - { - throw new AssertActualExpectedException(fileChangeNotificationsExpected.Length, notificationCount, $"Timed out after {TestTimeoutMillisecondsDelay}ms"); - } + // The timeout here is annoying, but even though our test is "smart" and waits for data, unfortunately if the code breaks the test is more likely to hang than fail + if (await Task.WhenAny(finished.Task, Task.Delay(TestTimeoutMillisecondsDelay)) != finished.Task) + { + throw new AssertActualExpectedException(fileChangeNotificationsExpected.Length, notificationCount, $"Timed out after {TestTimeoutMillisecondsDelay}ms"); + } - // Observe the task in case of exceptions - await finished.Task; + // Observe the task in case of exceptions + await finished.Task; - // Dispose the watcher so that internal blocks complete (especially for tests that don't send any file changes) - await watcher.DisposeAsync(); + // Dispose the watcher so that internal blocks complete (especially for tests that don't send any file changes) + await watcher.DisposeAsync(); - // Make sure we watched all of the files we should - Assert.Equal(watchedFiles, fileChangeService.UniqueFilesWatched); + // Make sure we watched all of the files we should + Assert.Equal(watchedFiles, fileChangeService.UniqueFilesWatched); - // Should clean up and unwatch everything - Assert.Empty(fileChangeService.WatchedFiles.ToArray()); - } + // Should clean up and unwatch everything + Assert.Empty(fileChangeService.WatchedFiles.ToArray()); + } - private static DesignTimeInputsFileWatcher CreateDesignTimeInputsFileWatcher(IVsAsyncFileChangeEx fileChangeService, out ProjectValueDataSource source) - { - // Create our mock design time inputs data source, but with a source we can actually use - var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); - source = ProjectValueDataSourceFactory.Create(services); + private static DesignTimeInputsFileWatcher CreateDesignTimeInputsFileWatcher(IVsAsyncFileChangeEx fileChangeService, out ProjectValueDataSource source) + { + // Create our mock design time inputs data source, but with a source we can actually use + var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); + source = ProjectValueDataSourceFactory.Create(services); - var mock = new Mock(); - mock.SetupGet(s => s.SourceBlock) - .Returns(source.SourceBlock); + var mock = new Mock(); + mock.SetupGet(s => s.SourceBlock) + .Returns(source.SourceBlock); - var dataSource = mock.Object; + var dataSource = mock.Object; - var threadingService = IProjectThreadingServiceFactory.Create(); - var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject\MyProject.csproj"); - var unconfiguredProjectServices = IUnconfiguredProjectServicesFactory.Create( - projectService: IProjectServiceFactory.Create( - services: ProjectServicesFactory.Create( - threadingService: threadingService))); + var threadingService = IProjectThreadingServiceFactory.Create(); + var unconfiguredProject = UnconfiguredProjectFactory.Create(fullPath: @"C:\MyProject\MyProject.csproj"); + var unconfiguredProjectServices = IUnconfiguredProjectServicesFactory.Create( + projectService: IProjectServiceFactory.Create( + services: ProjectServicesFactory.Create( + threadingService: threadingService))); - // Create our class under test - return new DesignTimeInputsFileWatcher(unconfiguredProject, unconfiguredProjectServices, threadingService, dataSource, IVsServiceFactory.Create(fileChangeService)); - } + // Create our class under test + return new DesignTimeInputsFileWatcher(unconfiguredProject, unconfiguredProjectServices, threadingService, dataSource, IVsServiceFactory.Create(fileChangeService)); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UnconfiguredProjectVsServicesTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UnconfiguredProjectVsServicesTests.cs index 5b9ff68b80..bad1d319b7 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UnconfiguredProjectVsServicesTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UnconfiguredProjectVsServicesTests.cs @@ -2,95 +2,94 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public class UnconfiguredProjectVsServicesTests { - public class UnconfiguredProjectVsServicesTests + [Fact] + public void Constructor_ValueAsUnconfiguredProject_SetsVsHierarchyToHostObject() { - [Fact] - public void Constructor_ValueAsUnconfiguredProject_SetsVsHierarchyToHostObject() - { - var hierarchy = IVsHierarchyFactory.Create(); - var project = UnconfiguredProjectFactory.Create(hostObject: hierarchy); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project: project); + var hierarchy = IVsHierarchyFactory.Create(); + var project = UnconfiguredProjectFactory.Create(hostObject: hierarchy); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project: project); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(hierarchy, vsServices.VsHierarchy); - } + Assert.Same(hierarchy, vsServices.VsHierarchy); + } - [Fact] - public void Constructor_ValueAsUnconfiguredProject_SetsVsProjectToHostObject() - { - var hierarchy = IVsHierarchyFactory.Create(); - var project = UnconfiguredProjectFactory.Create(hostObject: hierarchy); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project: project); + [Fact] + public void Constructor_ValueAsUnconfiguredProject_SetsVsProjectToHostObject() + { + var hierarchy = IVsHierarchyFactory.Create(); + var project = UnconfiguredProjectFactory.Create(hostObject: hierarchy); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project: project); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(hierarchy, vsServices.VsProject); - } + Assert.Same(hierarchy, vsServices.VsProject); + } - [Fact] - public void Constructor_ValueAsCommonServices_SetsProjectToCommonServicesProject() - { - var project = UnconfiguredProjectFactory.Create(); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project: project); + [Fact] + public void Constructor_ValueAsCommonServices_SetsProjectToCommonServicesProject() + { + var project = UnconfiguredProjectFactory.Create(); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(project: project); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(project, vsServices.Project); - } + Assert.Same(project, vsServices.Project); + } - [Fact] - public void Constructor_ValueAsCommonServices_SetsThreadingServiceToCommonServicesThreadingService() - { - var threadingService = IProjectThreadingServiceFactory.Create(); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(threadingService: threadingService); + [Fact] + public void Constructor_ValueAsCommonServices_SetsThreadingServiceToCommonServicesThreadingService() + { + var threadingService = IProjectThreadingServiceFactory.Create(); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(threadingService: threadingService); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(threadingService, vsServices.ThreadingService); - } + Assert.Same(threadingService, vsServices.ThreadingService); + } - [Fact] - public void Constructor_ValueAsCommonServices_SetsActiveConfiguredProjectProjectToCommonServicesActiveConfiguredProject() - { - var project = UnconfiguredProjectFactory.Create(); - var projectProperties = ProjectPropertiesFactory.Create(project); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(configuredProject: projectProperties.ConfiguredProject); + [Fact] + public void Constructor_ValueAsCommonServices_SetsActiveConfiguredProjectProjectToCommonServicesActiveConfiguredProject() + { + var project = UnconfiguredProjectFactory.Create(); + var projectProperties = ProjectPropertiesFactory.Create(project); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(configuredProject: projectProperties.ConfiguredProject); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(projectProperties.ConfiguredProject, vsServices.ActiveConfiguredProject); - } + Assert.Same(projectProperties.ConfiguredProject, vsServices.ActiveConfiguredProject); + } - [Fact] - public void Constructor_ValueAsCommonServices_SetsActiveConfiguredProjectPropertiesToCommonServicesActiveConfiguredProjectProperties() - { - var project = UnconfiguredProjectFactory.Create(); - var projectProperties = ProjectPropertiesFactory.Create(project); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); + [Fact] + public void Constructor_ValueAsCommonServices_SetsActiveConfiguredProjectPropertiesToCommonServicesActiveConfiguredProjectProperties() + { + var project = UnconfiguredProjectFactory.Create(); + var projectProperties = ProjectPropertiesFactory.Create(project); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectProperties: projectProperties); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(projectProperties, vsServices.ActiveConfiguredProjectProperties); - } + Assert.Same(projectProperties, vsServices.ActiveConfiguredProjectProperties); + } - [Fact] - public void Constructor_ValueAsCommonServices_SetsProjectAccessorToCommonServicesProjectAccessor() - { - var projectAccessor = IProjectAccessorFactory.Create(); - var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectAccessor: projectAccessor); + [Fact] + public void Constructor_ValueAsCommonServices_SetsProjectAccessorToCommonServicesProjectAccessor() + { + var projectAccessor = IProjectAccessorFactory.Create(); + var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(projectAccessor: projectAccessor); - var vsServices = CreateInstance(commonServices); + var vsServices = CreateInstance(commonServices); - Assert.Same(projectAccessor, vsServices.ProjectAccessor); - } + Assert.Same(projectAccessor, vsServices.ProjectAccessor); + } - private static UnconfiguredProjectVsServices CreateInstance(IUnconfiguredProjectCommonServices commonServices) - { - var projectTree = new Lazy(() => IPhysicalProjectTreeFactory.Create()); - return new UnconfiguredProjectVsServices(commonServices, projectTree); - } + private static UnconfiguredProjectVsServices CreateInstance(IUnconfiguredProjectCommonServices commonServices) + { + var projectTree = new Lazy(() => IPhysicalProjectTreeFactory.Create()); + return new UnconfiguredProjectVsServices(commonServices, projectTree); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTestBase.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTestBase.cs index 3eaf66af77..cdd670df71 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTestBase.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTestBase.cs @@ -2,112 +2,111 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +public abstract class BuildUpToDateCheckTestBase { - public abstract class BuildUpToDateCheckTestBase - { - private static readonly IImmutableList _itemTypes = ImmutableList.Empty - .Add(new ItemType("None", upToDateCheckInput: true)) - .Add(new ItemType("Content", upToDateCheckInput: true)) - .Add(new ItemType("Compile", upToDateCheckInput: true)) - .Add(new ItemType("Resource", upToDateCheckInput: true)) - .Add(new ItemType("EmbeddedResource", upToDateCheckInput: true)); + private static readonly IImmutableList _itemTypes = ImmutableList.Empty + .Add(new ItemType("None", upToDateCheckInput: true)) + .Add(new ItemType("Content", upToDateCheckInput: true)) + .Add(new ItemType("Compile", upToDateCheckInput: true)) + .Add(new ItemType("Resource", upToDateCheckInput: true)) + .Add(new ItemType("EmbeddedResource", upToDateCheckInput: true)); - private protected static IProjectRuleSnapshotModel SimpleItems(params string[] items) + private protected static IProjectRuleSnapshotModel SimpleItems(params string[] items) + { + return new IProjectRuleSnapshotModel { - return new IProjectRuleSnapshotModel - { - Items = items.ToDictionary>(i => i, i => ImmutableStringDictionary.EmptyOrdinal) - }; - } + Items = items.ToDictionary>(i => i, i => ImmutableStringDictionary.EmptyOrdinal) + }; + } - private protected static IProjectRuleSnapshotModel ItemWithMetadata(string itemSpec, string metadataName, string metadataValue) + private protected static IProjectRuleSnapshotModel ItemWithMetadata(string itemSpec, string metadataName, string metadataValue) + { + return new IProjectRuleSnapshotModel { - return new IProjectRuleSnapshotModel + Items = new Dictionary> { - Items = new Dictionary> - { - { itemSpec, ImmutableStringDictionary.EmptyOrdinal.Add(metadataName, metadataValue) } - } - }; - } + { itemSpec, ImmutableStringDictionary.EmptyOrdinal.Add(metadataName, metadataValue) } + } + }; + } - private protected static IProjectRuleSnapshotModel ItemWithMetadata(string itemSpec, params (string MetadataName, string MetadataValue)[] metadata) + private protected static IProjectRuleSnapshotModel ItemWithMetadata(string itemSpec, params (string MetadataName, string MetadataValue)[] metadata) + { + return new IProjectRuleSnapshotModel { - return new IProjectRuleSnapshotModel + Items = new Dictionary> { - Items = new Dictionary> - { - { itemSpec, metadata.ToImmutableDictionary(pair => pair.MetadataName, pair => pair.MetadataValue, StringComparer.Ordinal) } - } - }; - } + { itemSpec, metadata.ToImmutableDictionary(pair => pair.MetadataName, pair => pair.MetadataValue, StringComparer.Ordinal) } + } + }; + } - private protected static IProjectRuleSnapshotModel ItemsWithMetadata(params (string itemSpec, string metadataName, string metadataValue)[] items) + private protected static IProjectRuleSnapshotModel ItemsWithMetadata(params (string itemSpec, string metadataName, string metadataValue)[] items) + { + return new IProjectRuleSnapshotModel { - return new IProjectRuleSnapshotModel - { - Items = items.ToDictionary(i => i.itemSpec, i => (IImmutableDictionary)ImmutableStringDictionary.EmptyOrdinal.Add(i.metadataName, i.metadataValue)) - }; - } + Items = items.ToDictionary(i => i.itemSpec, i => (IImmutableDictionary)ImmutableStringDictionary.EmptyOrdinal.Add(i.metadataName, i.metadataValue)) + }; + } - private protected static IProjectRuleSnapshotModel Union(params IProjectRuleSnapshotModel[] models) - { - var items = new Dictionary>(); + private protected static IProjectRuleSnapshotModel Union(params IProjectRuleSnapshotModel[] models) + { + var items = new Dictionary>(); - foreach (var model in models) + foreach (var model in models) + { + foreach ((string key, IImmutableDictionary value) in model.Items) { - foreach ((string key, IImmutableDictionary value) in model.Items) - { - items[key] = value; - } + items[key] = value; } - - return new IProjectRuleSnapshotModel { Items = items }; } - private protected static UpToDateCheckImplicitConfiguredInput UpdateState( - UpToDateCheckImplicitConfiguredInput priorState, - Dictionary? projectRuleSnapshot = null, - Dictionary? sourceRuleSnapshot = null, - bool itemRemovedFromSourceSnapshot = false) + return new IProjectRuleSnapshotModel { Items = items }; + } + + private protected static UpToDateCheckImplicitConfiguredInput UpdateState( + UpToDateCheckImplicitConfiguredInput priorState, + Dictionary? projectRuleSnapshot = null, + Dictionary? sourceRuleSnapshot = null, + bool itemRemovedFromSourceSnapshot = false) + { + return priorState.Update( + CreateUpdate(projectRuleSnapshot), + CreateUpdate(sourceRuleSnapshot, itemRemovedFromSourceSnapshot), + IProjectItemSchemaFactory.Create(_itemTypes), + IProjectCatalogSnapshotFactory.CreateWithDefaultMapping(_itemTypes)); + + static IProjectSubscriptionUpdate CreateUpdate(Dictionary? snapshotBySchemaName, bool itemRemovedFromSnapshot = false) { - return priorState.Update( - CreateUpdate(projectRuleSnapshot), - CreateUpdate(sourceRuleSnapshot, itemRemovedFromSourceSnapshot), - IProjectItemSchemaFactory.Create(_itemTypes), - IProjectCatalogSnapshotFactory.CreateWithDefaultMapping(_itemTypes)); + var snapshots = ImmutableStringDictionary.EmptyOrdinal; + var changes = ImmutableStringDictionary.EmptyOrdinal; - static IProjectSubscriptionUpdate CreateUpdate(Dictionary? snapshotBySchemaName, bool itemRemovedFromSnapshot = false) + if (snapshotBySchemaName is not null) { - var snapshots = ImmutableStringDictionary.EmptyOrdinal; - var changes = ImmutableStringDictionary.EmptyOrdinal; - - if (snapshotBySchemaName is not null) + foreach ((string schemaName, IProjectRuleSnapshotModel model) in snapshotBySchemaName) { - foreach ((string schemaName, IProjectRuleSnapshotModel model) in snapshotBySchemaName) - { - var change = itemRemovedFromSnapshot - ? new IProjectChangeDescriptionModel - { - Before = model, - After = new IProjectRuleSnapshotModel(), - Difference = new IProjectChangeDiffModel { AnyChanges = true, RemovedItems = model.Items.Select(a => a.Key).ToImmutableHashSet() } - } - : new IProjectChangeDescriptionModel - { - Before = new IProjectRuleSnapshotModel(), - After = model, - Difference = new IProjectChangeDiffModel { AnyChanges = true } - }; + var change = itemRemovedFromSnapshot + ? new IProjectChangeDescriptionModel + { + Before = model, + After = new IProjectRuleSnapshotModel(), + Difference = new IProjectChangeDiffModel { AnyChanges = true, RemovedItems = model.Items.Select(a => a.Key).ToImmutableHashSet() } + } + : new IProjectChangeDescriptionModel + { + Before = new IProjectRuleSnapshotModel(), + After = model, + Difference = new IProjectChangeDiffModel { AnyChanges = true } + }; - snapshots = snapshots.Add(schemaName, model.ToActualModel()); - changes = changes.Add(schemaName, change.ToActualModel()); - } + snapshots = snapshots.Add(schemaName, model.ToActualModel()); + changes = changes.Add(schemaName, change.ToActualModel()); } - - return IProjectSubscriptionUpdateFactory.Implement(snapshots, changes); } + + return IProjectSubscriptionUpdateFactory.Implement(snapshots, changes); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTests.cs index 68b7d85679..574a47bd8c 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/BuildUpToDateCheckTests.cs @@ -11,1746 +11,1696 @@ #pragma warning disable IDE0058 #pragma warning disable CS0649 // Field is never assigned to -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +public sealed class BuildUpToDateCheckTests : BuildUpToDateCheckTestBase, IDisposable { - public sealed class BuildUpToDateCheckTests : BuildUpToDateCheckTestBase, IDisposable + private const LogLevel _logLevel = LogLevel.Verbose; + + private const string _projectDir = @"C:\Dev\Solution\Project"; + private const string _projectPath = $@"{_projectDir}\Project.csproj"; + private const string _msBuildAllProjects = $@"{_projectPath};C:\Dev\Solution\Project2\Project2.csproj"; + private const string _outputPathRelative = $@"bin\Debug\"; + + private const string _inputPath = $@"{_projectDir}\Input.cs"; + private const string _builtPath = $@"{_projectDir}\{_outputPathRelative}Built.dll"; + + private readonly DateTime _projectFileTimeUtc = new(1999, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private readonly List _telemetryEvents = []; + private DateTime? _lastSuccessfulBuildStartTime; + + private readonly BuildUpToDateCheck _buildUpToDateCheck; + private readonly ITestOutputHelper _output; + private readonly IFileSystemMock _fileSystem; + private readonly Mock _persistence; + + // Values returned by mocks that may be modified in test cases as needed + private bool _isTaskQueueEmpty = true; + private bool _isFastUpToDateCheckEnabledInSettings = true; + private bool _isBuildAccelerationEnabledInSettings; + private bool? _isBuildAccelerationEnabledInProject; + private bool? _expectedIsBuildAccelerationEnabled; + private IEnumerable<(string Path, ImmutableArray CopyItems)> _copyItems = Enumerable.Empty<(string Path, ImmutableArray CopyItems)>(); + private bool _isCopyItemsComplete = true; + private IReadOnlyList? _duplicateCopyItemRelativeTargetPaths; + private IReadOnlyList? _targetsWithoutReferenceAssemblies; + + private UpToDateCheckConfiguredInput? _state; + private SolutionBuildContext? _currentSolutionBuildContext; + private bool _expectedUpToDate; + + public BuildUpToDateCheckTests(ITestOutputHelper output) { - private const LogLevel _logLevel = LogLevel.Verbose; + _output = output; + + // NOTE most of these mocks are only present to prevent NREs in Initialize + + var inputDataSource = new Mock(MockBehavior.Strict); + inputDataSource.SetupGet(o => o.SourceBlock) + .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); + + // Enable "Info" log level, as we assert logged messages in tests + var projectSystemOptions = new Mock(MockBehavior.Strict); + projectSystemOptions.Setup(o => o.GetFastUpToDateLoggingLevelAsync(It.IsAny())) + .ReturnsAsync(_logLevel); + projectSystemOptions.Setup(o => o.GetIsFastUpToDateCheckEnabledAsync(It.IsAny())) + .ReturnsAsync(() => _isFastUpToDateCheckEnabledInSettings); + projectSystemOptions.Setup(o => o.IsBuildAccelerationEnabledByDefaultAsync(It.IsAny())) + .ReturnsAsync(() => _isBuildAccelerationEnabledInSettings); + + var projectCommonServices = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); + var jointRuleSource = new ProjectValueDataSource(projectCommonServices); + var sourceItemsRuleSource = new ProjectValueDataSource(projectCommonServices); + var projectSnapshotSource = new ProjectValueDataSource(projectCommonServices); + var projectCatalogSource = new ProjectValueDataSource(projectCommonServices); + + var projectSubscriptionService = new Mock(MockBehavior.Strict); + projectSubscriptionService.SetupGet(o => o.JointRuleSource).Returns(jointRuleSource); + projectSubscriptionService.SetupGet(o => o.ProjectSource).Returns(projectSnapshotSource); + projectSubscriptionService.SetupGet(o => o.SourceItemsRuleSource).Returns(sourceItemsRuleSource); + projectSubscriptionService.SetupGet(o => o.ProjectCatalogSource).Returns(projectCatalogSource); + + var configuredProjectServices = ConfiguredProjectServicesFactory.Create(projectSubscriptionService: projectSubscriptionService.Object); + + var configuredProject = new Mock(MockBehavior.Strict); + configuredProject.SetupGet(c => c.Services).Returns(configuredProjectServices); + configuredProject.SetupGet(c => c.UnconfiguredProject).Returns(UnconfiguredProjectFactory.Create(fullPath: _projectPath)); + configuredProject.SetupGet(c => c.Capabilities).Returns(IProjectCapabilitiesScopeFactory.Create()); + + _persistence = new Mock(MockBehavior.Strict); + _persistence + .Setup(o => o.RestoreLastSuccessfulBuildStateAsync(It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(() => Task.FromResult(_lastSuccessfulBuildStartTime)); + + var projectAsynchronousTasksService = new Mock(MockBehavior.Strict); + projectAsynchronousTasksService.SetupGet(s => s.UnloadCancellationToken).Returns(CancellationToken.None); + projectAsynchronousTasksService.Setup(s => s.IsTaskQueueEmpty(ProjectCriticalOperation.Build)).Returns(() => _isTaskQueueEmpty); + + var guidService = ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(Guid.NewGuid()); + + _fileSystem = new IFileSystemMock(); + _fileSystem.AddFolder(_projectDir); + _fileSystem.AddFile(_projectPath, _projectFileTimeUtc); + + var upToDateCheckHost = new Mock(MockBehavior.Strict); + + var copyItemAggregator = new Mock(MockBehavior.Strict); + copyItemAggregator.Setup(o => o.TryGatherCopyItemsForProject(It.IsAny(), It.IsAny())).Returns(() => new CopyItemsResult(_isCopyItemsComplete, _copyItems, _duplicateCopyItemRelativeTargetPaths, _targetsWithoutReferenceAssemblies)); + + _currentSolutionBuildContext = new SolutionBuildContext(_fileSystem); + + var solutionBuildContextProvider = new Mock(MockBehavior.Strict); + solutionBuildContextProvider.SetupGet(o => o.CurrentSolutionBuildContext).Returns(() => _currentSolutionBuildContext); + + var solutionBuildEventListener = new Mock(MockBehavior.Strict); + solutionBuildEventListener.Setup( + o => o.NotifyProjectChecked( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Callback(( + bool upToDate, + bool? buildAccelerationEnabled, + BuildAccelerationResult result, + int configurationCount, + int copyCount, + int fileCount, + TimeSpan totalTime, + TimeSpan waitTime, + LogLevel logLevel) => + { + if (_expectedUpToDate != upToDate) + { + Assert.Fail($"Expected up-to-date to be {_expectedUpToDate} but was {upToDate}."); + } + else if (_logLevel != logLevel) + { + Assert.Fail($"Expected log level to be {_logLevel} but was {logLevel}."); + } + else if (_expectedIsBuildAccelerationEnabled != buildAccelerationEnabled) + { + Assert.Fail($"Expected build acceleration enablement to be {_expectedIsBuildAccelerationEnabled} but was {buildAccelerationEnabled}."); + } + }); - private const string _projectDir = @"C:\Dev\Solution\Project"; - private const string _projectPath = $@"{_projectDir}\Project.csproj"; - private const string _msBuildAllProjects = $@"{_projectPath};C:\Dev\Solution\Project2\Project2.csproj"; - private const string _outputPathRelative = $@"bin\Debug\"; + _buildUpToDateCheck = new BuildUpToDateCheck( + solutionBuildContextProvider.Object, + solutionBuildEventListener.Object, + inputDataSource.Object, + projectSystemOptions.Object, + configuredProject.Object, + _persistence.Object, + projectAsynchronousTasksService.Object, + ITelemetryServiceFactory.Create(_telemetryEvents.Add), + _fileSystem, + guidService, + upToDateCheckHost.Object, + copyItemAggregator.Object); + + _buildUpToDateCheck.UpToDateCheckers.Add(_buildUpToDateCheck); + } - private const string _inputPath = $@"{_projectDir}\Input.cs"; - private const string _builtPath = $@"{_projectDir}\{_outputPathRelative}Built.dll"; + public void Dispose() => _buildUpToDateCheck.Dispose(); + + private async Task SetupAsync( + Dictionary? projectSnapshot = null, + Dictionary? sourceSnapshot = null, + bool disableFastUpToDateCheck = false, + string outDir = _outputPathRelative, + DateTime? lastSuccessfulBuildStartTimeUtc = null, + DateTime? lastItemsChangedAtUtc = null, + UpToDateCheckImplicitConfiguredInput? upToDateCheckImplicitConfiguredInput = null, + bool itemRemovedFromSourceSnapshot = false) + { + upToDateCheckImplicitConfiguredInput ??= UpToDateCheckImplicitConfiguredInput.CreateEmpty(ProjectConfigurationFactory.Create("testConfiguration")); - private readonly DateTime _projectFileTimeUtc = new(1999, 1, 1, 0, 0, 0, DateTimeKind.Utc); + _lastSuccessfulBuildStartTime = lastSuccessfulBuildStartTimeUtc; - private readonly List _telemetryEvents = []; - private DateTime? _lastSuccessfulBuildStartTime; + projectSnapshot ??= new Dictionary(StringComparers.RuleNames); - private readonly BuildUpToDateCheck _buildUpToDateCheck; - private readonly ITestOutputHelper _output; - private readonly IFileSystemMock _fileSystem; - private readonly Mock _persistence; - - // Values returned by mocks that may be modified in test cases as needed - private bool _isTaskQueueEmpty = true; - private bool _isFastUpToDateCheckEnabledInSettings = true; - private bool _isBuildAccelerationEnabledInSettings; - private bool? _isBuildAccelerationEnabledInProject; - private bool? _expectedIsBuildAccelerationEnabled; - private IEnumerable<(string Path, ImmutableArray CopyItems)> _copyItems = Enumerable.Empty<(string Path, ImmutableArray CopyItems)>(); - private bool _isCopyItemsComplete = true; - private IReadOnlyList? _duplicateCopyItemRelativeTargetPaths; - private IReadOnlyList? _targetsWithoutReferenceAssemblies; - - private UpToDateCheckConfiguredInput? _state; - private SolutionBuildContext? _currentSolutionBuildContext; - private bool _expectedUpToDate; - - public BuildUpToDateCheckTests(ITestOutputHelper output) + if (!projectSnapshot.ContainsKey(ConfigurationGeneral.SchemaName)) { - _output = output; - - // NOTE most of these mocks are only present to prevent NREs in Initialize - - var inputDataSource = new Mock(MockBehavior.Strict); - inputDataSource.SetupGet(o => o.SourceBlock) - .Returns(DataflowBlockSlim.CreateBroadcastBlock>()); - - // Enable "Info" log level, as we assert logged messages in tests - var projectSystemOptions = new Mock(MockBehavior.Strict); - projectSystemOptions.Setup(o => o.GetFastUpToDateLoggingLevelAsync(It.IsAny())) - .ReturnsAsync(_logLevel); - projectSystemOptions.Setup(o => o.GetIsFastUpToDateCheckEnabledAsync(It.IsAny())) - .ReturnsAsync(() => _isFastUpToDateCheckEnabledInSettings); - projectSystemOptions.Setup(o => o.IsBuildAccelerationEnabledByDefaultAsync(It.IsAny())) - .ReturnsAsync(() => _isBuildAccelerationEnabledInSettings); - - var projectCommonServices = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy(); - var jointRuleSource = new ProjectValueDataSource(projectCommonServices); - var sourceItemsRuleSource = new ProjectValueDataSource(projectCommonServices); - var projectSnapshotSource = new ProjectValueDataSource(projectCommonServices); - var projectCatalogSource = new ProjectValueDataSource(projectCommonServices); - - var projectSubscriptionService = new Mock(MockBehavior.Strict); - projectSubscriptionService.SetupGet(o => o.JointRuleSource).Returns(jointRuleSource); - projectSubscriptionService.SetupGet(o => o.ProjectSource).Returns(projectSnapshotSource); - projectSubscriptionService.SetupGet(o => o.SourceItemsRuleSource).Returns(sourceItemsRuleSource); - projectSubscriptionService.SetupGet(o => o.ProjectCatalogSource).Returns(projectCatalogSource); - - var configuredProjectServices = ConfiguredProjectServicesFactory.Create(projectSubscriptionService: projectSubscriptionService.Object); - - var configuredProject = new Mock(MockBehavior.Strict); - configuredProject.SetupGet(c => c.Services).Returns(configuredProjectServices); - configuredProject.SetupGet(c => c.UnconfiguredProject).Returns(UnconfiguredProjectFactory.Create(fullPath: _projectPath)); - configuredProject.SetupGet(c => c.Capabilities).Returns(IProjectCapabilitiesScopeFactory.Create()); - - _persistence = new Mock(MockBehavior.Strict); - _persistence - .Setup(o => o.RestoreLastSuccessfulBuildStateAsync(It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(() => Task.FromResult(_lastSuccessfulBuildStartTime)); - - var projectAsynchronousTasksService = new Mock(MockBehavior.Strict); - projectAsynchronousTasksService.SetupGet(s => s.UnloadCancellationToken).Returns(CancellationToken.None); - projectAsynchronousTasksService.Setup(s => s.IsTaskQueueEmpty(ProjectCriticalOperation.Build)).Returns(() => _isTaskQueueEmpty); - - var guidService = ISafeProjectGuidServiceFactory.ImplementGetProjectGuidAsync(Guid.NewGuid()); - - _fileSystem = new IFileSystemMock(); - _fileSystem.AddFolder(_projectDir); - _fileSystem.AddFile(_projectPath, _projectFileTimeUtc); - - var upToDateCheckHost = new Mock(MockBehavior.Strict); - - var copyItemAggregator = new Mock(MockBehavior.Strict); - copyItemAggregator.Setup(o => o.TryGatherCopyItemsForProject(It.IsAny(), It.IsAny())).Returns(() => new CopyItemsResult(_isCopyItemsComplete, _copyItems, _duplicateCopyItemRelativeTargetPaths, _targetsWithoutReferenceAssemblies)); - - _currentSolutionBuildContext = new SolutionBuildContext(_fileSystem); - - var solutionBuildContextProvider = new Mock(MockBehavior.Strict); - solutionBuildContextProvider.SetupGet(o => o.CurrentSolutionBuildContext).Returns(() => _currentSolutionBuildContext); - - var solutionBuildEventListener = new Mock(MockBehavior.Strict); - solutionBuildEventListener.Setup( - o => o.NotifyProjectChecked( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Callback(( - bool upToDate, - bool? buildAccelerationEnabled, - BuildAccelerationResult result, - int configurationCount, - int copyCount, - int fileCount, - TimeSpan totalTime, - TimeSpan waitTime, - LogLevel logLevel) => - { - if (_expectedUpToDate != upToDate) - { - Assert.Fail($"Expected up-to-date to be {_expectedUpToDate} but was {upToDate}."); - } - else if (_logLevel != logLevel) - { - Assert.Fail($"Expected log level to be {_logLevel} but was {logLevel}."); - } - else if (_expectedIsBuildAccelerationEnabled != buildAccelerationEnabled) - { - Assert.Fail($"Expected build acceleration enablement to be {_expectedIsBuildAccelerationEnabled} but was {buildAccelerationEnabled}."); - } - }); - - _buildUpToDateCheck = new BuildUpToDateCheck( - solutionBuildContextProvider.Object, - solutionBuildEventListener.Object, - inputDataSource.Object, - projectSystemOptions.Object, - configuredProject.Object, - _persistence.Object, - projectAsynchronousTasksService.Object, - ITelemetryServiceFactory.Create(_telemetryEvents.Add), - _fileSystem, - guidService, - upToDateCheckHost.Object, - copyItemAggregator.Object); - - _buildUpToDateCheck.UpToDateCheckers.Add(_buildUpToDateCheck); + projectSnapshot[ConfigurationGeneral.SchemaName] = new IProjectRuleSnapshotModel + { + Properties = new Dictionary(StringComparers.PropertyNames) + { + { "MSBuildProjectFullPath", _projectPath }, + { "MSBuildProjectDirectory", _projectDir }, + { "MSBuildAllProjects", _msBuildAllProjects }, + { "OutputPath", outDir }, + { "OutDir", outDir }, + { ConfigurationGeneral.DisableFastUpToDateCheckProperty, disableFastUpToDateCheck.ToString() }, + { ConfigurationGeneral.AccelerateBuildsInVisualStudioProperty, _isBuildAccelerationEnabledInProject?.ToString() ?? "" } + } + }; } - public void Dispose() => _buildUpToDateCheck.Dispose(); + UpToDateCheckImplicitConfiguredInput configuredInput = UpdateState( + upToDateCheckImplicitConfiguredInput, + projectSnapshot, + sourceSnapshot, + itemRemovedFromSourceSnapshot: itemRemovedFromSourceSnapshot); - private async Task SetupAsync( - Dictionary? projectSnapshot = null, - Dictionary? sourceSnapshot = null, - bool disableFastUpToDateCheck = false, - string outDir = _outputPathRelative, - DateTime? lastSuccessfulBuildStartTimeUtc = null, - DateTime? lastItemsChangedAtUtc = null, - UpToDateCheckImplicitConfiguredInput? upToDateCheckImplicitConfiguredInput = null, - bool itemRemovedFromSourceSnapshot = false) + if (lastItemsChangedAtUtc is not null) { - upToDateCheckImplicitConfiguredInput ??= UpToDateCheckImplicitConfiguredInput.CreateEmpty(ProjectConfigurationFactory.Create("testConfiguration")); + configuredInput = configuredInput.WithLastItemsChangedAtUtc(lastItemsChangedAtUtc.Value); + } - _lastSuccessfulBuildStartTime = lastSuccessfulBuildStartTimeUtc; + _state = new UpToDateCheckConfiguredInput(ImmutableArray.Create(configuredInput)); - projectSnapshot ??= new Dictionary(StringComparers.RuleNames); + var subscription = new Mock(MockBehavior.Strict); - if (!projectSnapshot.ContainsKey(ConfigurationGeneral.SchemaName)) - { - projectSnapshot[ConfigurationGeneral.SchemaName] = new IProjectRuleSnapshotModel - { - Properties = new Dictionary(StringComparers.PropertyNames) - { - { "MSBuildProjectFullPath", _projectPath }, - { "MSBuildProjectDirectory", _projectDir }, - { "MSBuildAllProjects", _msBuildAllProjects }, - { "OutputPath", outDir }, - { "OutDir", outDir }, - { ConfigurationGeneral.DisableFastUpToDateCheckProperty, disableFastUpToDateCheck.ToString() }, - { ConfigurationGeneral.AccelerateBuildsInVisualStudioProperty, _isBuildAccelerationEnabledInProject?.ToString() ?? "" } - } - }; - } + subscription.Setup(s => s.EnsureInitialized()); - UpToDateCheckImplicitConfiguredInput configuredInput = UpdateState( - upToDateCheckImplicitConfiguredInput, - projectSnapshot, - sourceSnapshot, - itemRemovedFromSourceSnapshot: itemRemovedFromSourceSnapshot); + subscription + .Setup(s => s.UpdateLastSuccessfulBuildStartTimeUtcAsync(It.IsAny(), It.IsAny())) + .Callback((DateTime timeUtc, bool isRebuild) => _lastSuccessfulBuildStartTime = timeUtc) + .Returns(Task.CompletedTask); - if (lastItemsChangedAtUtc is not null) + subscription + .Setup(s => s.RunAsync(It.IsAny)>>>(), It.IsAny())) + .Returns(async (Func)>> func, CancellationToken token) => { - configuredInput = configuredInput.WithLastItemsChangedAtUtc(lastItemsChangedAtUtc.Value); - } + Assumes.NotNull(_state); + var result = await func(_state, _persistence.Object, token); + return result.Item1; + }); - _state = new UpToDateCheckConfiguredInput(ImmutableArray.Create(configuredInput)); + subscription.Setup(s => s.Dispose()); - var subscription = new Mock(MockBehavior.Strict); + await _buildUpToDateCheck.ActivateAsync(); - subscription.Setup(s => s.EnsureInitialized()); + _buildUpToDateCheck.TestAccess.SetSubscription(subscription.Object); - subscription - .Setup(s => s.UpdateLastSuccessfulBuildStartTimeUtcAsync(It.IsAny(), It.IsAny())) - .Callback((DateTime timeUtc, bool isRebuild) => _lastSuccessfulBuildStartTime = timeUtc) - .Returns(Task.CompletedTask); + return configuredInput; + } - subscription - .Setup(s => s.RunAsync(It.IsAny)>>>(), It.IsAny())) - .Returns(async (Func)>> func, CancellationToken token) => - { - Assumes.NotNull(_state); - var result = await func(_state, _persistence.Object, token); - return result.Item1; - }); + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task IsUpToDateCheckEnabledAsync_DelegatesToProjectSystemOptions(bool isEnabled) + { + _isFastUpToDateCheckEnabledInSettings = isEnabled; - subscription.Setup(s => s.Dispose()); + Assert.Equal( + isEnabled, + await _buildUpToDateCheck.IsUpToDateCheckEnabledAsync(CancellationToken.None)); + } - await _buildUpToDateCheck.ActivateAsync(); + [Theory] + [InlineData(BuildAction.Clean)] + [InlineData(BuildAction.Compile)] + [InlineData(BuildAction.Deploy)] + [InlineData(BuildAction.Link)] + [InlineData(BuildAction.Package)] + [InlineData(BuildAction.Rebuild)] + public async Task IsUpToDateAsync_False_NonBuildAction(BuildAction buildAction) + { + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - _buildUpToDateCheck.TestAccess.SetSubscription(subscription.Object); + await SetupAsync(lastSuccessfulBuildStartTimeUtc: lastBuildTime); - return configuredInput; - } + await AssertUpToDateAsync( + """ + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Project is up-to-date. + """); - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task IsUpToDateCheckEnabledAsync_DelegatesToProjectSystemOptions(bool isEnabled) - { - _isFastUpToDateCheckEnabledInSettings = isEnabled; + await AssertNotUpToDateAsync(buildAction: buildAction); + } - Assert.Equal( - isEnabled, - await _buildUpToDateCheck.IsUpToDateCheckEnabledAsync(CancellationToken.None)); - } + [Fact] + public async Task IsUpToDateAsync_False_BuildTasksActive() + { + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync(lastSuccessfulBuildStartTimeUtc: lastBuildTime); + + await AssertUpToDateAsync( + """ + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Project is up-to-date. + """); + + _isTaskQueueEmpty = false; + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Critical build tasks are running, not up-to-date. + """, + "CriticalTasks"); + } - [Theory] - [InlineData(BuildAction.Clean)] - [InlineData(BuildAction.Compile)] - [InlineData(BuildAction.Deploy)] - [InlineData(BuildAction.Link)] - [InlineData(BuildAction.Package)] - [InlineData(BuildAction.Rebuild)] - public async Task IsUpToDateAsync_False_NonBuildAction(BuildAction buildAction) + [Fact] + public async Task IsUpToDateAsync_False_ItemsChanged() + { + var projectSnapshot = new Dictionary { - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync(lastSuccessfulBuildStartTimeUtc: lastBuildTime); + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - await AssertUpToDateAsync( - """ - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Project is up-to-date. - """); - - await AssertNotUpToDateAsync(buildAction: buildAction); - } - - [Fact] - public async Task IsUpToDateAsync_False_BuildTasksActive() + var sourceSnapshot = new Dictionary { - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + [Compile.SchemaName] = SimpleItems("ItemPath1", "ItemPath2") + }; + + var outputTime = DateTime.UtcNow.AddMinutes(-3); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-2); + var itemChangedTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangedTime); + + _fileSystem.AddFile(_builtPath, outputTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + The set of project items was changed more recently ({ToLocalTime(itemChangedTime)}) than the last successful build start time ({ToLocalTime(lastBuildTime)}), not up-to-date. + Compile item added 'ItemPath1' + Compile item added 'ItemPath2' + """, + "ProjectItemsChangedSinceLastSuccessfulBuildStart"); + } - await SetupAsync(lastSuccessfulBuildStartTimeUtc: lastBuildTime); + [Fact] + public async Task IsUpToDateAsync_False_Disabled() + { + await SetupAsync(disableFastUpToDateCheck: true); - await AssertUpToDateAsync( - """ - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Project is up-to-date. - """); + await AssertNotUpToDateAsync( + "The 'DisableFastUpToDateCheck' property is 'true', not up-to-date.", + "Disabled"); + } - _isTaskQueueEmpty = false; + [Fact] + public async Task ValidateUpToDateAsync_True_Disabled() + { + await SetupAsync(disableFastUpToDateCheck: true); - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Critical build tasks are running, not up-to-date. - """, - "CriticalTasks"); - } + await ValidateUpToDateAsync(); + } - [Fact] - public async Task IsUpToDateAsync_False_ItemsChanged() + [Fact] + public async Task IsUpToDateAsync_False_OutputItemDoesNotExist() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("ItemPath1", "ItemPath2") - }; - - var outputTime = DateTime.UtcNow.AddMinutes(-3); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-2); - var itemChangedTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangedTime); - - _fileSystem.AddFile(_builtPath, outputTime); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - The set of project items was changed more recently ({ToLocalTime(itemChangedTime)}) than the last successful build start time ({ToLocalTime(lastBuildTime)}), not up-to-date. - Compile item added 'ItemPath1' - Compile item added 'ItemPath2' - """, - "ProjectItemsChangedSinceLastSuccessfulBuildStart"); - } + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; + + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync(projectSnapshot: projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Output '{_builtPath}' does not exist, not up-to-date. + """, + "OutputNotFound"); + } - [Fact] - public async Task IsUpToDateAsync_False_Disabled() + [Fact] + public async Task IsUpToDateAsync_False_ZeroFilesInProjectAfterItemDeletion() + { + var projectSnapshot = new Dictionary { - await SetupAsync(disableFastUpToDateCheck: true); + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + }; - await AssertNotUpToDateAsync( - "The 'DisableFastUpToDateCheck' property is 'true', not up-to-date.", - "Disabled"); - } - - [Fact] - public async Task ValidateUpToDateAsync_True_Disabled() + var sourceSnapshot = new Dictionary { - await SetupAsync(disableFastUpToDateCheck: true); - - await ValidateUpToDateAsync(); - } + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + var inputTime = DateTime.UtcNow.AddMinutes(-5); + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var builtTime = DateTime.UtcNow.AddMinutes(-2); + + _fileSystem.AddFile(_inputPath, inputTime); + _fileSystem.AddFile(_builtPath, builtTime); + + var priorState = await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(builtTime)}). Newest input is '{_inputPath}' ({ToLocalTime(inputTime)}). + Project is up-to-date. + """); + + lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + itemChangeTime = DateTime.UtcNow.AddMinutes(0); + + await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime, + upToDateCheckImplicitConfiguredInput: priorState, + itemRemovedFromSourceSnapshot: true); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + The set of project items was changed more recently ({ToLocalTime(itemChangeTime)}) than the last successful build start time ({ToLocalTime(lastBuildTime)}), not up-to-date. + Compile item removed 'Input.cs' + """, + "ProjectItemsChangedSinceLastSuccessfulBuildStart"); + } - [Fact] - public async Task IsUpToDateAsync_False_OutputItemDoesNotExist() + [Fact] + public async Task IsUpToDateAsync_False_InputItemDoesNotExist() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync(projectSnapshot: projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Output '{_builtPath}' does not exist, not up-to-date. - """, - "OutputNotFound"); - } - - [Fact] - public async Task IsUpToDateAsync_False_ZeroFilesInProjectAfterItemDeletion() + var sourceSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - var inputTime = DateTime.UtcNow.AddMinutes(-5); - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var builtTime = DateTime.UtcNow.AddMinutes(-2); - - _fileSystem.AddFile(_inputPath, inputTime); - _fileSystem.AddFile(_builtPath, builtTime); - - var priorState = await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(builtTime)}). Newest input is '{_inputPath}' ({ToLocalTime(inputTime)}). - Project is up-to-date. - """); - - lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - itemChangeTime = DateTime.UtcNow.AddMinutes(0); - - await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime, - upToDateCheckImplicitConfiguredInput: priorState, - itemRemovedFromSourceSnapshot: true); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - The set of project items was changed more recently ({ToLocalTime(itemChangeTime)}) than the last successful build start time ({ToLocalTime(lastBuildTime)}), not up-to-date. - Compile item removed 'Input.cs' - """, - "ProjectItemsChangedSinceLastSuccessfulBuildStart"); - } + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + var itemChangedTime = DateTime.UtcNow.AddMinutes(-3); + var builtTime = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangedTime); + + _fileSystem.AddFile(_builtPath, builtTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + Input Compile item '{_inputPath}' does not exist and is required, not up-to-date. + """, + "InputNotFound"); + } - [Fact] - public async Task IsUpToDateAsync_False_InputItemDoesNotExist() + [Fact] + public async Task IsUpToDateAsync_False_InputNewerThanBuiltOutput() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - var itemChangedTime = DateTime.UtcNow.AddMinutes(-3); - var builtTime = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangedTime); - - _fileSystem.AddFile(_builtPath, builtTime); + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - Input Compile item '{_inputPath}' does not exist and is required, not up-to-date. - """, - "InputNotFound"); - } - - [Fact] - public async Task IsUpToDateAsync_False_InputNewerThanBuiltOutput() + var sourceSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - var itemChangedTime = DateTime.UtcNow.AddMinutes(-4); - var builtTime = DateTime.UtcNow.AddMinutes(-3); - var inputTime = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangedTime); - - _fileSystem.AddFile(_builtPath, builtTime); - _fileSystem.AddFile(_inputPath, inputTime); + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + var itemChangedTime = DateTime.UtcNow.AddMinutes(-4); + var builtTime = DateTime.UtcNow.AddMinutes(-3); + var inputTime = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangedTime); + + _fileSystem.AddFile(_builtPath, builtTime); + _fileSystem.AddFile(_inputPath, inputTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + Input Compile item '{_inputPath}' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(builtTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - Input Compile item '{_inputPath}' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(builtTime)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } + [Fact] + public async Task IsUpToDateAsync_False_InputChangedBetweenLastBuildStartAndAndEarliestOutput() + { + // This test covers a race condition described in https://github.com/dotnet/project-system/issues/4014 - [Fact] - public async Task IsUpToDateAsync_False_InputChangedBetweenLastBuildStartAndAndEarliestOutput() + var projectSnapshot = new Dictionary { - // This test covers a race condition described in https://github.com/dotnet/project-system/issues/4014 - - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - // The rule is that no input item should be modified since the last successful build started. - - var t0 = DateTime.UtcNow.AddMinutes(-5); // t0 Output file timestamp - var t1 = DateTime.UtcNow.AddMinutes(-4); // t1 Input file timestamp - var t2 = DateTime.UtcNow.AddMinutes(-3); // t2 Check up-to-date (false) and build - var t3 = DateTime.UtcNow.AddMinutes(-2); // t3 Modify input file (during build) - var t4 = DateTime.UtcNow.AddMinutes(-1); // t4 Produce first (earliest) output DLL (from t0 input) - // t5 Check incorrectly claims everything up-to-date, as t3 > t2 (inputTime > lastBuildStartTime) - - _fileSystem.AddFile(_builtPath, t0); - _fileSystem.AddFile(_inputPath, t1); - - // Run test (t2) - await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t2); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - Input Compile item '{_inputPath}' is newer ({ToLocalTime(t1)}) than earliest output '{_builtPath}' ({ToLocalTime(t0)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - - // Modify input while build in progress (t3) - _fileSystem.AddFile(_inputPath, t3); - - // Update write time of output (t4) - _fileSystem.AddFile(_builtPath, t4); - - // Run check again (t5) - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - Input Compile item '{_inputPath}' ({ToLocalTime(t3)}) has been modified since the last successful build started ({ToLocalTime(t2)}), not up-to-date. - """, - "InputModifiedSinceLastSuccessfulBuildStart"); - } - - [Fact] - public async Task IsUpToDateAsync_True_BuildAfterRebuild() + var sourceSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - // If an input was modified since the last successful build started, not up-to-date. - - var t0 = DateTime.Now.Date; // t0 Output file timestamp - var t1 = t0.AddMinutes(1); // t1 Input file timestamp - // Check up-to-date (false) - var t2 = t0.AddMinutes(2); // t2 Start and complete a successful build) - var t3 = t0.AddMinutes(3); // t3 Modify input file - var t4 = t0.AddMinutes(4); // t4 Rebuild (firing build events but not checking up-to-date) - var t5 = t0.AddMinutes(5); // t5 Produce first (earliest) output DLL (from t0 input) - // t6 Check correctly claims everything up-to-date, as t3 < t5 - - _fileSystem.AddFile(_builtPath, t0); - _fileSystem.AddFile(_inputPath, t1); - - // Run test (t2) - await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t0.AddMinutes(-1)); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - Input Compile item '{_inputPath}' is newer ({ToLocalTime(t1)}) than earliest output '{_builtPath}' ({ToLocalTime(t0)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - - // Build (t2) - ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t2); - await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: false); - - // Modify input (t3) - _fileSystem.AddFile(_inputPath, t3); - - // Rebuild (t4) - ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t4); - await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: true); - - // Update output (t5) - _fileSystem.AddFile(_builtPath, t5); - - // Run check again (t6) - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(t5)}). Newest input is '{_inputPath}' ({ToLocalTime(t3)}). - Project is up-to-date. - """); - } + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + // The rule is that no input item should be modified since the last successful build started. + + var t0 = DateTime.UtcNow.AddMinutes(-5); // t0 Output file timestamp + var t1 = DateTime.UtcNow.AddMinutes(-4); // t1 Input file timestamp + var t2 = DateTime.UtcNow.AddMinutes(-3); // t2 Check up-to-date (false) and build + var t3 = DateTime.UtcNow.AddMinutes(-2); // t3 Modify input file (during build) + var t4 = DateTime.UtcNow.AddMinutes(-1); // t4 Produce first (earliest) output DLL (from t0 input) + // t5 Check incorrectly claims everything up-to-date, as t3 > t2 (inputTime > lastBuildStartTime) + + _fileSystem.AddFile(_builtPath, t0); + _fileSystem.AddFile(_inputPath, t1); + + // Run test (t2) + await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t2); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + Input Compile item '{_inputPath}' is newer ({ToLocalTime(t1)}) than earliest output '{_builtPath}' ({ToLocalTime(t0)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + + // Modify input while build in progress (t3) + _fileSystem.AddFile(_inputPath, t3); + + // Update write time of output (t4) + _fileSystem.AddFile(_builtPath, t4); + + // Run check again (t5) + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + Input Compile item '{_inputPath}' ({ToLocalTime(t3)}) has been modified since the last successful build started ({ToLocalTime(t2)}), not up-to-date. + """, + "InputModifiedSinceLastSuccessfulBuildStart"); + } - [Fact] - public async Task IsUpToDateAsync_True_NewProjectRebuiltThenBuilt() + [Fact] + public async Task IsUpToDateAsync_True_BuildAfterRebuild() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - // The rule is that no input item should be modified since the last successful build started. - - var t0 = DateTime.UtcNow.AddMinutes(-3); // t0 Input file timestamp - var t1 = DateTime.UtcNow.AddMinutes(-2); // t1 Rebuild (sets output file timestamp) - - _fileSystem.AddFile(_inputPath, t0); + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - _fileSystem.AddFile(_builtPath, t1); - - await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t1); - - // Rebuild (t1) - ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t1); - await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: true); - - // Run test (t2) - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(t1)}). Newest input is '{_inputPath}' ({ToLocalTime(t0)}). - Project is up-to-date. - """); - } - - [Fact] - public async Task IsUpToDateAsync_False_CompileItemNewerThanCustomOutput() + var sourceSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckOutput.SchemaName] = SimpleItems("Output") - }; + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + // If an input was modified since the last successful build started, not up-to-date. + + var t0 = DateTime.Now.Date; // t0 Output file timestamp + var t1 = t0.AddMinutes(1); // t1 Input file timestamp + // Check up-to-date (false) + var t2 = t0.AddMinutes(2); // t2 Start and complete a successful build) + var t3 = t0.AddMinutes(3); // t3 Modify input file + var t4 = t0.AddMinutes(4); // t4 Rebuild (firing build events but not checking up-to-date) + var t5 = t0.AddMinutes(5); // t5 Produce first (earliest) output DLL (from t0 input) + // t6 Check correctly claims everything up-to-date, as t3 < t5 + + _fileSystem.AddFile(_builtPath, t0); + _fileSystem.AddFile(_inputPath, t1); + + // Run test (t2) + await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t0.AddMinutes(-1)); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + Input Compile item '{_inputPath}' is newer ({ToLocalTime(t1)}) than earliest output '{_builtPath}' ({ToLocalTime(t0)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + + // Build (t2) + ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t2); + await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: false); + + // Modify input (t3) + _fileSystem.AddFile(_inputPath, t3); + + // Rebuild (t4) + ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t4); + await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: true); + + // Update output (t5) + _fileSystem.AddFile(_builtPath, t5); + + // Run check again (t6) + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(t5)}). Newest input is '{_inputPath}' ({ToLocalTime(t3)}). + Project is up-to-date. + """); + } - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; + [Fact] + public async Task IsUpToDateAsync_True_NewProjectRebuiltThenBuilt() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var outputTime = DateTime.UtcNow.AddMinutes(-3); - var compileItemTime = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + var sourceSnapshot = new Dictionary + { + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + // The rule is that no input item should be modified since the last successful build started. + + var t0 = DateTime.UtcNow.AddMinutes(-3); // t0 Input file timestamp + var t1 = DateTime.UtcNow.AddMinutes(-2); // t1 Rebuild (sets output file timestamp) + + _fileSystem.AddFile(_inputPath, t0); + + _fileSystem.AddFile(_builtPath, t1); + + await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t1); + + // Rebuild (t1) + ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t1); + await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: true); + + // Run test (t2) + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(t1)}). Newest input is '{_inputPath}' ({ToLocalTime(t0)}). + Project is up-to-date. + """); + } - await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + [Fact] + public async Task IsUpToDateAsync_False_CompileItemNewerThanCustomOutput() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckOutput.SchemaName] = SimpleItems("Output") + }; - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", outputTime); - _fileSystem.AddFile(_inputPath, compileItemTime); + var sourceSnapshot = new Dictionary + { + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var outputTime = DateTime.UtcNow.AddMinutes(-3); + var compileItemTime = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", outputTime); + _fileSystem.AddFile(_inputPath, compileItemTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckOutput outputs: + C:\Dev\Solution\Project\Output + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + Input Compile item '{_inputPath}' is newer ({ToLocalTime(compileItemTime)}) than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(outputTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckOutput outputs: - C:\Dev\Solution\Project\Output - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - Input Compile item '{_inputPath}' is newer ({ToLocalTime(compileItemTime)}) than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(outputTime)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } + [Theory] + [CombinatorialData] + public async Task IsUpToDateAsync_CopyReference_InputsOlderThanMarkerOutput( + bool? isBuildAccelerationEnabledInProject, + bool allReferencesProduceReferenceAssemblies, + bool hasDuplicateCopyItemRelativeTargetPaths) + { + SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies, hasDuplicateCopyItemRelativeTargetPaths: hasDuplicateCopyItemRelativeTargetPaths); - [Theory] - [CombinatorialData] - public async Task IsUpToDateAsync_CopyReference_InputsOlderThanMarkerOutput( - bool? isBuildAccelerationEnabledInProject, - bool allReferencesProduceReferenceAssemblies, - bool hasDuplicateCopyItemRelativeTargetPaths) + var projectSnapshot = new Dictionary { - SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies, hasDuplicateCopyItemRelativeTargetPaths: hasDuplicateCopyItemRelativeTargetPaths); - - var projectSnapshot = new Dictionary + [CopyUpToDateMarker.SchemaName] = SimpleItems("OutputMarker"), + [ResolvedCompilationReference.SchemaName] = new IProjectRuleSnapshotModel { - [CopyUpToDateMarker.SchemaName] = SimpleItems("OutputMarker"), - [ResolvedCompilationReference.SchemaName] = new IProjectRuleSnapshotModel + Items = new Dictionary>(StringComparers.ItemNames) { - Items = new Dictionary>(StringComparers.ItemNames) { - { - "Reference1", - ImmutableStringDictionary.EmptyOrdinal - .Add("CopyUpToDateMarker", "Reference1MarkerPath") - .Add("ResolvedPath", "Reference1ResolvedPath") - .Add("OriginalPath", "Reference1OriginalPath") - } + "Reference1", + ImmutableStringDictionary.EmptyOrdinal + .Add("CopyUpToDateMarker", "Reference1MarkerPath") + .Add("ResolvedPath", "Reference1ResolvedPath") + .Add("OriginalPath", "Reference1OriginalPath") } } - }; + } + }; - var lastBuildTime = DateTime.UtcNow.AddMinutes(-5); - var resolvedTime = DateTime.UtcNow.AddMinutes(-4); - var markerTime = DateTime.UtcNow.AddMinutes(-3); - var originalTime = DateTime.UtcNow.AddMinutes(-2); - var outputTime = DateTime.UtcNow.AddMinutes(-1); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-5); + var resolvedTime = DateTime.UtcNow.AddMinutes(-4); + var markerTime = DateTime.UtcNow.AddMinutes(-3); + var originalTime = DateTime.UtcNow.AddMinutes(-2); + var outputTime = DateTime.UtcNow.AddMinutes(-1); - await SetupAsync(projectSnapshot: projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); + await SetupAsync(projectSnapshot: projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\OutputMarker", outputTime); - _fileSystem.AddFile("Reference1ResolvedPath", resolvedTime); - _fileSystem.AddFile("Reference1MarkerPath", markerTime); - _fileSystem.AddFile("Reference1OriginalPath", originalTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\OutputMarker", outputTime); + _fileSystem.AddFile("Reference1ResolvedPath", resolvedTime); + _fileSystem.AddFile("Reference1MarkerPath", markerTime); + _fileSystem.AddFile("Reference1OriginalPath", originalTime); - if (isBuildAccelerationEnabledInProject is true) + if (isBuildAccelerationEnabledInProject is true) + { + if (allReferencesProduceReferenceAssemblies) { - if (allReferencesProduceReferenceAssemblies) + if (hasDuplicateCopyItemRelativeTargetPaths) { - if (hasDuplicateCopyItemRelativeTargetPaths) - { - _expectedIsBuildAccelerationEnabled = false; - - await AssertUpToDateAsync( - $""" - Build acceleration is not available for this project because it copies duplicate files to the output directory: 'appsettings.json', 'AnotherFile.dll' - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Reference1MarkerPath - Project is up-to-date. - """); - } - else - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Project is up-to-date. - """); - } + _expectedIsBuildAccelerationEnabled = false; + + await AssertUpToDateAsync( + $""" + Build acceleration is not available for this project because it copies duplicate files to the output directory: 'appsettings.json', 'AnotherFile.dll' + Comparing timestamps of inputs and outputs: + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Reference1MarkerPath + Project is up-to-date. + """); } else { - if (hasDuplicateCopyItemRelativeTargetPaths) - { - _expectedIsBuildAccelerationEnabled = false; - - await AssertUpToDateAsync( - $""" - Build acceleration is not available for this project because it copies duplicate files to the output directory: 'appsettings.json', 'AnotherFile.dll' - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Reference1MarkerPath - Project is up-to-date. - """); - } - else - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Project is up-to-date. - This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. - """); - } - } - } - else if (isBuildAccelerationEnabledInProject is false) - { - await AssertUpToDateAsync( - $""" - Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Reference1MarkerPath - Project is up-to-date. - """); - } - else if (isBuildAccelerationEnabledInProject is null) - { - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Reference1MarkerPath - Project is up-to-date. - """); - } - } - - [Theory] - [InlineData(null)] - [InlineData(false)] - public async Task IsUpToDateAsync_False_CopyReference_InputNewerThanMarkerOutput(bool? isBuildAccelerationEnabledInProject) - { - var projectSnapshot = new Dictionary - { - [CopyUpToDateMarker.SchemaName] = SimpleItems("OutputMarker"), - [ResolvedCompilationReference.SchemaName] = new IProjectRuleSnapshotModel - { - Items = new Dictionary>(StringComparers.ItemNames) - { - { - "Reference1", - ImmutableStringDictionary.EmptyOrdinal - .Add("CopyUpToDateMarker", "Reference1MarkerPath") - .Add("ResolvedPath", "Reference1ResolvedPath") - .Add("OriginalPath", "Reference1OriginalPath") - } - } + await AssertUpToDateAsync( + $""" + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Project is up-to-date. + """); } - }; - - var lastBuildTime = DateTime.UtcNow.AddMinutes(-5); - var outputTime = DateTime.UtcNow.AddMinutes(-4); - var resolvedTime = DateTime.UtcNow.AddMinutes(-3); - var markerTime = DateTime.UtcNow.AddMinutes(-2); - var originalTime = DateTime.UtcNow.AddMinutes(-1); - - // We only check copy markers when build acceleration is disabled (null or false). - SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies: true); - - await SetupAsync(projectSnapshot: projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\OutputMarker", outputTime); - _fileSystem.AddFile("Reference1ResolvedPath", resolvedTime); - _fileSystem.AddFile("Reference1MarkerPath", markerTime); - _fileSystem.AddFile("Reference1OriginalPath", originalTime); - - if (isBuildAccelerationEnabledInProject is false) - { - await AssertNotUpToDateAsync( - $""" - Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Input marker 'Reference1OriginalPath' is newer ({ToLocalTime(originalTime)}) than output marker 'C:\Dev\Solution\Project\OutputMarker' ({ToLocalTime(outputTime)}), not up-to-date. - """, - "InputMarkerNewerThanOutputMarker"); - } - else if (isBuildAccelerationEnabledInProject is null) - { - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Input marker 'Reference1OriginalPath' is newer ({ToLocalTime(originalTime)}) than output marker 'C:\Dev\Solution\Project\OutputMarker' ({ToLocalTime(outputTime)}), not up-to-date. - This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. - """, - "InputMarkerNewerThanOutputMarker"); } else { - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Comparing timestamps of copy marker inputs and output: - Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. - Adding input reference copy markers: - Reference1OriginalPath - Input marker 'Reference1OriginalPath' is newer ({ToLocalTime(originalTime)}) than output marker 'C:\Dev\Solution\Project\OutputMarker' ({ToLocalTime(outputTime)}), not up-to-date. - This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. - """, - "InputMarkerNewerThanOutputMarker"); - } - } - - [Fact] - public async Task IsUpToDateAsync_False_AnalyzerReference_NewerThanEarliestOutput() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [ResolvedAnalyzerReference.SchemaName] = ItemWithMetadata("Analyzer1", "ResolvedPath", "Analyzer1ResolvedPath") - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var outputTime = DateTime.UtcNow.AddMinutes(-3); - var inputTime = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - var analyzerItem = @"C:\Dev\Solution\Project\Analyzer1ResolvedPath"; - - _fileSystem.AddFile(_builtPath, outputTime); - _fileSystem.AddFile(analyzerItem, inputTime); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding ResolvedAnalyzerReference inputs: - {analyzerItem} - Input ResolvedAnalyzerReference item '{analyzerItem}' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } - - [Fact] - public async Task IsUpToDateAsync_False_CompilationReference_NewerThanEarliestOutput() - { - var resolvedReferencePath = @"C:\Dev\Solution\Project\Reference1ResolvedPath"; - - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [ResolvedCompilationReference.SchemaName] = new IProjectRuleSnapshotModel + if (hasDuplicateCopyItemRelativeTargetPaths) { - Items = new Dictionary>(StringComparers.ItemNames) - { - { - "Reference1", - ImmutableStringDictionary.EmptyOrdinal - .Add("CopyUpToDateMarker", "Reference1MarkerPath") - .Add("ResolvedPath", resolvedReferencePath) - .Add("OriginalPath", @"..\Project\Reference1OriginalPath") - } - } - } - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var outputTime = DateTime.UtcNow.AddMinutes(-2); - var inputTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(_builtPath, outputTime); - _fileSystem.AddFile(resolvedReferencePath, inputTime); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding ResolvedCompilationReference inputs: - {resolvedReferencePath} - Input ResolvedCompilationReference item 'C:\Dev\Solution\Project\Reference1ResolvedPath' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } - - [Fact] - public async Task IsUpToDateAsync_False_UpToDateCheckInput_NewerThanEarliestOutput() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckInput.SchemaName] = SimpleItems("Item1", "Item2") - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var outputTime = DateTime.UtcNow.AddMinutes(-2); - var inputTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(_builtPath, outputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Item1", inputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Item2", outputTime); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding UpToDateCheckInput inputs: - C:\Dev\Solution\Project\Item1 - Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Item1' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } - - [Fact] - public async Task IsUpToDateAsync_False_Sets_InputNewerThanOutput() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckInput.SchemaName] = ItemWithMetadata("Input1", "Set", "Set1"), - [UpToDateCheckOutput.SchemaName] = ItemWithMetadata("Output1", "Set", "Set1") - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var outputTime = DateTime.UtcNow.AddMinutes(-2); - var inputTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime); - _fileSystem.AddFile(_builtPath, outputTime); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). - Comparing timestamps of inputs and outputs in Set="Set1": - Adding UpToDateCheckOutput outputs in Set="Set1": - C:\Dev\Solution\Project\Output1 - Adding UpToDateCheckInput inputs in Set="Set1": - C:\Dev\Solution\Project\Input1 - Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input1' is newer ({ToLocalTime(inputTime)}) than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } - - [Fact] - public async Task IsUpToDateAsync_True_Sets_InputOlderThanOutput_MultipleSets() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckInput.SchemaName] = Union(ItemWithMetadata("Input1", "Set", "Set1"), ItemWithMetadata("Input2", "Set", "Set2")), - [UpToDateCheckOutput.SchemaName] = Union(ItemWithMetadata("Output1", "Set", "Set1"), ItemWithMetadata("Output2", "Set", "Set2")) - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-6); - var inputTime1 = DateTime.UtcNow.AddMinutes(-5); - var outputTime1 = DateTime.UtcNow.AddMinutes(-4); - var inputTime2 = DateTime.UtcNow.AddMinutes(-3); - var outputTime2 = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime1); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime1); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input2", inputTime2); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output2", outputTime2); - _fileSystem.AddFile(_builtPath, outputTime1); - - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime1)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). - Comparing timestamps of inputs and outputs in Set="Set1": - Adding UpToDateCheckOutput outputs in Set="Set1": - C:\Dev\Solution\Project\Output1 - Adding UpToDateCheckInput inputs in Set="Set1": - C:\Dev\Solution\Project\Input1 - In Set="Set1", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime1)}). Newest input is 'C:\Dev\Solution\Project\Input1' ({ToLocalTime(inputTime1)}). - Comparing timestamps of inputs and outputs in Set="Set2": - Adding UpToDateCheckOutput outputs in Set="Set2": - C:\Dev\Solution\Project\Output2 - Adding UpToDateCheckInput inputs in Set="Set2": - C:\Dev\Solution\Project\Input2 - In Set="Set2", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output2' ({ToLocalTime(outputTime2)}). Newest input is 'C:\Dev\Solution\Project\Input2' ({ToLocalTime(inputTime2)}). - Project is up-to-date. - """); - } - - [Fact] - public async Task IsUpToDateAsync_False_Sets_InputNewerThanOutput_MultipleSets() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckInput.SchemaName] = Union(ItemWithMetadata("Input1", "Set", "Set1"), ItemWithMetadata("Input2", "Set", "Set2")), - [UpToDateCheckOutput.SchemaName] = Union(ItemWithMetadata("Output1", "Set", "Set1"), ItemWithMetadata("Output2", "Set", "Set2")) - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-6); - var inputTime1 = DateTime.UtcNow.AddMinutes(-5); - var outputTime1 = DateTime.UtcNow.AddMinutes(-4); - var outputTime2 = DateTime.UtcNow.AddMinutes(-3); - var inputTime2 = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime1); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime1); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input2", inputTime2); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output2", outputTime2); - _fileSystem.AddFile(_builtPath, outputTime1); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime1)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). - Comparing timestamps of inputs and outputs in Set="Set1": - Adding UpToDateCheckOutput outputs in Set="Set1": - C:\Dev\Solution\Project\Output1 - Adding UpToDateCheckInput inputs in Set="Set1": - C:\Dev\Solution\Project\Input1 - In Set="Set1", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime1)}). Newest input is 'C:\Dev\Solution\Project\Input1' ({ToLocalTime(inputTime1)}). - Comparing timestamps of inputs and outputs in Set="Set2": - Adding UpToDateCheckOutput outputs in Set="Set2": - C:\Dev\Solution\Project\Output2 - Adding UpToDateCheckInput inputs in Set="Set2": - C:\Dev\Solution\Project\Input2 - Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input2' is newer ({ToLocalTime(inputTime2)}) than earliest output 'C:\Dev\Solution\Project\Output2' ({ToLocalTime(outputTime2)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); - } - - [Fact] - public async Task IsUpToDateAsync_False_Sets_InputNewerThanOutput_ItemInMultipleSets() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckInput.SchemaName] = ItemWithMetadata("Input.cs", "Set", "Set1;Set2"), - [UpToDateCheckOutput.SchemaName] = Union(ItemWithMetadata("Output1", "Set", "Set1"), ItemWithMetadata("Output2", "Set", "Set2")) - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-5); - var outputTime2 = DateTime.UtcNow.AddMinutes(-4); - var inputTime = DateTime.UtcNow.AddMinutes(-3); - var outputTime1 = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(_inputPath, inputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime1); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output2", outputTime2); - _fileSystem.AddFile(_builtPath, outputTime1); - - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime1)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). - Comparing timestamps of inputs and outputs in Set="Set1": - Adding UpToDateCheckOutput outputs in Set="Set1": - C:\Dev\Solution\Project\Output1 - Adding UpToDateCheckInput inputs in Set="Set1": - C:\Dev\Solution\Project\Input.cs - In Set="Set1", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime1)}). Newest input is '{_inputPath}' ({ToLocalTime(inputTime)}). - Comparing timestamps of inputs and outputs in Set="Set2": - Adding UpToDateCheckOutput outputs in Set="Set2": - C:\Dev\Solution\Project\Output2 - Adding UpToDateCheckInput inputs in Set="Set2": - C:\Dev\Solution\Project\Input.cs - Input UpToDateCheckInput item '{_inputPath}' is newer ({ToLocalTime(inputTime)}) than earliest output 'C:\Dev\Solution\Project\Output2' ({ToLocalTime(outputTime2)}), not up-to-date. - """, - "InputNewerThanEarliestOutput"); + _expectedIsBuildAccelerationEnabled = false; + + await AssertUpToDateAsync( + $""" + Build acceleration is not available for this project because it copies duplicate files to the output directory: 'appsettings.json', 'AnotherFile.dll' + Comparing timestamps of inputs and outputs: + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Reference1MarkerPath + Project is up-to-date. + """); + } + else + { + await AssertUpToDateAsync( + $""" + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Project is up-to-date. + This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. + """); + } + } } - - [Fact] - public async Task IsUpToDateAsync_True_Sets_InputOnly() + else if (isBuildAccelerationEnabledInProject is false) { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckInput.SchemaName] = ItemWithMetadata("Input1", "Set", "Set1"), - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var buildTime = DateTime.UtcNow.AddMinutes(-2); - var inputTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime); - _fileSystem.AddFile(_builtPath, buildTime); - await AssertUpToDateAsync( $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(buildTime)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). - Comparing timestamps of inputs and outputs in Set="Set1": - No build outputs defined in Set="Set1". + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Reference1MarkerPath Project is up-to-date. """); } - - [Fact] - public async Task IsUpToDateAsync_True_Sets_OutputOnly() + else if (isBuildAccelerationEnabledInProject is null) { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), - [UpToDateCheckOutput.SchemaName] = ItemWithMetadata("Output1", "Set", "Set1") - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-3); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-2); - var outputTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime); - _fileSystem.AddFile(_builtPath, outputTime); - await AssertUpToDateAsync( $""" Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). - Comparing timestamps of inputs and outputs in Set="Set1": - Adding UpToDateCheckOutput outputs in Set="Set1": - C:\Dev\Solution\Project\Output1 - No inputs defined in Set="Set1". + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Reference1MarkerPath Project is up-to-date. """); } + } - [Fact] - public async Task IsUpToDateAsync_False_Kinds_InputNewerThanOutput_WithIgnoredKind() + [Theory] + [InlineData(null)] + [InlineData(false)] + public async Task IsUpToDateAsync_False_CopyReference_InputNewerThanMarkerOutput(bool? isBuildAccelerationEnabledInProject) + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary + [CopyUpToDateMarker.SchemaName] = SimpleItems("OutputMarker"), + [ResolvedCompilationReference.SchemaName] = new IProjectRuleSnapshotModel { - [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("IgnoredBuilt.dll", "Kind", "Ignored")), - [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("IgnoredInput.cs", "Kind", "Ignored")), - [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("IgnoredOutput", "Kind", "Ignored")) - }; + Items = new Dictionary>(StringComparers.ItemNames) + { + { + "Reference1", + ImmutableStringDictionary.EmptyOrdinal + .Add("CopyUpToDateMarker", "Reference1MarkerPath") + .Add("ResolvedPath", "Reference1ResolvedPath") + .Add("OriginalPath", "Reference1OriginalPath") + } + } + } + }; + + var lastBuildTime = DateTime.UtcNow.AddMinutes(-5); + var outputTime = DateTime.UtcNow.AddMinutes(-4); + var resolvedTime = DateTime.UtcNow.AddMinutes(-3); + var markerTime = DateTime.UtcNow.AddMinutes(-2); + var originalTime = DateTime.UtcNow.AddMinutes(-1); - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var outputTime = DateTime.UtcNow.AddMinutes(-2); - var inputTime = DateTime.UtcNow.AddMinutes(-1); + // We only check copy markers when build acceleration is disabled (null or false). + SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies: true); - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + await SetupAsync(projectSnapshot: projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", inputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", outputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", outputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\OutputMarker", outputTime); + _fileSystem.AddFile("Reference1ResolvedPath", resolvedTime); + _fileSystem.AddFile("Reference1MarkerPath", markerTime); + _fileSystem.AddFile("Reference1OriginalPath", originalTime); + if (isBuildAccelerationEnabledInProject is false) + { await AssertNotUpToDateAsync( $""" - Ignoring up-to-date check items with Kind="Ignored" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: - Adding UpToDateCheckOutput outputs: - C:\Dev\Solution\Project\Output - Skipping 'C:\Dev\Solution\Project\IgnoredOutput' with ignored Kind="Ignored" - Adding UpToDateCheckBuilt outputs: - C:\Dev\Solution\Project\Built - Skipping 'C:\Dev\Solution\Project\IgnoredBuilt.dll' with ignored Kind="Ignored" - Adding newest import input: - {_projectPath} - Adding UpToDateCheckInput inputs: - C:\Dev\Solution\Project\Input - Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input' is newer ({ToLocalTime(inputTime)}) than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(outputTime)}), not up-to-date. + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Input marker 'Reference1OriginalPath' is newer ({ToLocalTime(originalTime)}) than output marker 'C:\Dev\Solution\Project\OutputMarker' ({ToLocalTime(outputTime)}), not up-to-date. """, - "InputNewerThanEarliestOutput", - ignoreKinds: "Ignored"); + "InputMarkerNewerThanOutputMarker"); } - - [Fact] - public async Task IsUpToDateAsync_False_Kinds_InputNewerThanOutput_NoKindIgnored() + else if (isBuildAccelerationEnabledInProject is null) { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("TaggedBuilt", "Kind", "Tagged")), - [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("TaggedInput", "Kind", "Tagged")), - [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("TaggedOutput", "Kind", "Tagged")) - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-6); - var input2Time = DateTime.UtcNow.AddMinutes(-5); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-4); - var output1Time = DateTime.UtcNow.AddMinutes(-3); - var output2Time = DateTime.UtcNow.AddMinutes(-2); - var input1Time = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", input1Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedInput", input2Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", output1Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedOutput", output2Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", output1Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedBuilt", output2Time); - await AssertNotUpToDateAsync( $""" Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: - Adding UpToDateCheckOutput outputs: - C:\Dev\Solution\Project\Output - C:\Dev\Solution\Project\TaggedOutput - Adding UpToDateCheckBuilt outputs: - C:\Dev\Solution\Project\Built - C:\Dev\Solution\Project\TaggedBuilt - Adding newest import input: - {_projectPath} - Adding UpToDateCheckInput inputs: - C:\Dev\Solution\Project\Input - Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input' is newer ({ToLocalTime(input1Time)}) than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(output1Time)}), not up-to-date. + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Input marker 'Reference1OriginalPath' is newer ({ToLocalTime(originalTime)}) than output marker 'C:\Dev\Solution\Project\OutputMarker' ({ToLocalTime(outputTime)}), not up-to-date. + This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. """, - "InputNewerThanEarliestOutput", - ignoreKinds: ""); + "InputMarkerNewerThanOutputMarker"); } - - [Fact] - public async Task IsUpToDateAsync_True_Kinds_InputNewerThanOutput_WithIgnoredKind() + else { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("IgnoredBuilt", "Kind", "Ignored")), - [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("IgnoredInput", "Kind", "Ignored")), - [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("IgnoredOutput", "Kind", "Ignored")) - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var inputTime = DateTime.UtcNow.AddMinutes(-3); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-2); - var outputTime = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", inputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", outputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", outputTime); - - await AssertUpToDateAsync( + await AssertNotUpToDateAsync( $""" - Ignoring up-to-date check items with Kind="Ignored" Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: - Adding UpToDateCheckOutput outputs: - C:\Dev\Solution\Project\Output - Skipping 'C:\Dev\Solution\Project\IgnoredOutput' with ignored Kind="Ignored" - Adding UpToDateCheckBuilt outputs: - C:\Dev\Solution\Project\Built - Skipping 'C:\Dev\Solution\Project\IgnoredBuilt' with ignored Kind="Ignored" - Adding newest import input: - {_projectPath} - Adding UpToDateCheckInput inputs: - C:\Dev\Solution\Project\Input - Skipping 'C:\Dev\Solution\Project\IgnoredInput' with ignored Kind="Ignored" - No inputs are newer than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(outputTime)}). Newest input is 'C:\Dev\Solution\Project\Input' ({ToLocalTime(inputTime)}). - Project is up-to-date. + No build outputs defined. + Comparing timestamps of copy marker inputs and output: + Write timestamp on output marker is {ToLocalTime(outputTime)} on 'C:\Dev\Solution\Project\OutputMarker'. + Adding input reference copy markers: + Reference1OriginalPath + Input marker 'Reference1OriginalPath' is newer ({ToLocalTime(originalTime)}) than output marker 'C:\Dev\Solution\Project\OutputMarker' ({ToLocalTime(outputTime)}), not up-to-date. + This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. """, - ignoreKinds: "Ignored"); + "InputMarkerNewerThanOutputMarker"); } + } - [Fact] - public async Task IsUpToDateAsync_True_Kinds_InputNewerThanOutput_NoKindIgnored() + [Fact] + public async Task IsUpToDateAsync_False_AnalyzerReference_NewerThanEarliestOutput() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("TaggedBuilt", "Kind", "Tagged")), - [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("TaggedInput", "Kind", "Tagged")), - [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("TaggedOutput", "Kind", "Tagged")) - }; - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-8); - var input2Time = DateTime.UtcNow.AddMinutes(-7); - var input1Time = DateTime.UtcNow.AddMinutes(-6); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-5); - var output4Time = DateTime.UtcNow.AddMinutes(-4); - var output3Time = DateTime.UtcNow.AddMinutes(-3); - var output2Time = DateTime.UtcNow.AddMinutes(-2); - var output1Time = DateTime.UtcNow.AddMinutes(-1); - - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", input2Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedInput", input1Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", output4Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedOutput", output3Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", output2Time); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedBuilt", output1Time); + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [ResolvedAnalyzerReference.SchemaName] = ItemWithMetadata("Analyzer1", "ResolvedPath", "Analyzer1ResolvedPath") + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var outputTime = DateTime.UtcNow.AddMinutes(-3); + var inputTime = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + var analyzerItem = @"C:\Dev\Solution\Project\Analyzer1ResolvedPath"; + + _fileSystem.AddFile(_builtPath, outputTime); + _fileSystem.AddFile(analyzerItem, inputTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding ResolvedAnalyzerReference inputs: + {analyzerItem} + Input ResolvedAnalyzerReference item '{analyzerItem}' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckOutput outputs: - C:\Dev\Solution\Project\Output - C:\Dev\Solution\Project\TaggedOutput - Adding UpToDateCheckBuilt outputs: - C:\Dev\Solution\Project\Built - C:\Dev\Solution\Project\TaggedBuilt - Adding newest import input: - {_projectPath} - Adding UpToDateCheckInput inputs: - C:\Dev\Solution\Project\Input - C:\Dev\Solution\Project\TaggedInput - No inputs are newer than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(output4Time)}). Newest input is 'C:\Dev\Solution\Project\TaggedInput' ({ToLocalTime(input1Time)}). - Project is up-to-date. - """, - ignoreKinds: ""); - } + [Fact] + public async Task IsUpToDateAsync_False_CompilationReference_NewerThanEarliestOutput() + { + var resolvedReferencePath = @"C:\Dev\Solution\Project\Reference1ResolvedPath"; - [Fact] - public async Task IsUpToDateAsync_False_BuiltItemWithSource_SourceIsNewerThanDestination() + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [ResolvedCompilationReference.SchemaName] = new IProjectRuleSnapshotModel { - [UpToDateCheckBuilt.SchemaName] = ItemWithMetadata("CopiedOutputDestination", UpToDateCheckBuilt.OriginalProperty, "CopiedOutputSource") - }; + Items = new Dictionary>(StringComparers.ItemNames) + { + { + "Reference1", + ImmutableStringDictionary.EmptyOrdinal + .Add("CopyUpToDateMarker", "Reference1MarkerPath") + .Add("ResolvedPath", resolvedReferencePath) + .Add("OriginalPath", @"..\Project\Reference1OriginalPath") + } + } + } + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var outputTime = DateTime.UtcNow.AddMinutes(-2); + var inputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(_builtPath, outputTime); + _fileSystem.AddFile(resolvedReferencePath, inputTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding ResolvedCompilationReference inputs: + {resolvedReferencePath} + Input ResolvedCompilationReference item 'C:\Dev\Solution\Project\Reference1ResolvedPath' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - var destinationPath = @"C:\Dev\Solution\Project\CopiedOutputDestination"; - var sourcePath = @"C:\Dev\Solution\Project\CopiedOutputSource"; + [Fact] + public async Task IsUpToDateAsync_False_UpToDateCheckInput_NewerThanEarliestOutput() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckInput.SchemaName] = SimpleItems("Item1", "Item2") + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var outputTime = DateTime.UtcNow.AddMinutes(-2); + var inputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(_builtPath, outputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Item1", inputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Item2", outputTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding UpToDateCheckInput inputs: + C:\Dev\Solution\Project\Item1 + Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Item1' is newer ({ToLocalTime(inputTime)}) than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var destinationTime = DateTime.UtcNow.AddMinutes(-2); - var sourceTime = DateTime.UtcNow.AddMinutes(-1); + [Fact] + public async Task IsUpToDateAsync_False_Sets_InputNewerThanOutput() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckInput.SchemaName] = ItemWithMetadata("Input1", "Set", "Set1"), + [UpToDateCheckOutput.SchemaName] = ItemWithMetadata("Output1", "Set", "Set1") + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var outputTime = DateTime.UtcNow.AddMinutes(-2); + var inputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime); + _fileSystem.AddFile(_builtPath, outputTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). + Comparing timestamps of inputs and outputs in Set="Set1": + Adding UpToDateCheckOutput outputs in Set="Set1": + C:\Dev\Solution\Project\Output1 + Adding UpToDateCheckInput inputs in Set="Set1": + C:\Dev\Solution\Project\Input1 + Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input1' is newer ({ToLocalTime(inputTime)}) than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + [Fact] + public async Task IsUpToDateAsync_True_Sets_InputOlderThanOutput_MultipleSets() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckInput.SchemaName] = Union(ItemWithMetadata("Input1", "Set", "Set1"), ItemWithMetadata("Input2", "Set", "Set2")), + [UpToDateCheckOutput.SchemaName] = Union(ItemWithMetadata("Output1", "Set", "Set1"), ItemWithMetadata("Output2", "Set", "Set2")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-6); + var inputTime1 = DateTime.UtcNow.AddMinutes(-5); + var outputTime1 = DateTime.UtcNow.AddMinutes(-4); + var inputTime2 = DateTime.UtcNow.AddMinutes(-3); + var outputTime2 = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime1); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime1); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input2", inputTime2); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output2", outputTime2); + _fileSystem.AddFile(_builtPath, outputTime1); + + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime1)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). + Comparing timestamps of inputs and outputs in Set="Set1": + Adding UpToDateCheckOutput outputs in Set="Set1": + C:\Dev\Solution\Project\Output1 + Adding UpToDateCheckInput inputs in Set="Set1": + C:\Dev\Solution\Project\Input1 + In Set="Set1", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime1)}). Newest input is 'C:\Dev\Solution\Project\Input1' ({ToLocalTime(inputTime1)}). + Comparing timestamps of inputs and outputs in Set="Set2": + Adding UpToDateCheckOutput outputs in Set="Set2": + C:\Dev\Solution\Project\Output2 + Adding UpToDateCheckInput inputs in Set="Set2": + C:\Dev\Solution\Project\Input2 + In Set="Set2", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output2' ({ToLocalTime(outputTime2)}). Newest input is 'C:\Dev\Solution\Project\Input2' ({ToLocalTime(inputTime2)}). + Project is up-to-date. + """); + } - _fileSystem.AddFile(destinationPath, destinationTime); - _fileSystem.AddFile(sourcePath, sourceTime); + [Fact] + public async Task IsUpToDateAsync_False_Sets_InputNewerThanOutput_MultipleSets() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckInput.SchemaName] = Union(ItemWithMetadata("Input1", "Set", "Set1"), ItemWithMetadata("Input2", "Set", "Set2")), + [UpToDateCheckOutput.SchemaName] = Union(ItemWithMetadata("Output1", "Set", "Set1"), ItemWithMetadata("Output2", "Set", "Set2")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-6); + var inputTime1 = DateTime.UtcNow.AddMinutes(-5); + var outputTime1 = DateTime.UtcNow.AddMinutes(-4); + var outputTime2 = DateTime.UtcNow.AddMinutes(-3); + var inputTime2 = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime1); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime1); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input2", inputTime2); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output2", outputTime2); + _fileSystem.AddFile(_builtPath, outputTime1); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime1)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). + Comparing timestamps of inputs and outputs in Set="Set1": + Adding UpToDateCheckOutput outputs in Set="Set1": + C:\Dev\Solution\Project\Output1 + Adding UpToDateCheckInput inputs in Set="Set1": + C:\Dev\Solution\Project\Input1 + In Set="Set1", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime1)}). Newest input is 'C:\Dev\Solution\Project\Input1' ({ToLocalTime(inputTime1)}). + Comparing timestamps of inputs and outputs in Set="Set2": + Adding UpToDateCheckOutput outputs in Set="Set2": + C:\Dev\Solution\Project\Output2 + Adding UpToDateCheckInput inputs in Set="Set2": + C:\Dev\Solution\Project\Input2 + Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input2' is newer ({ToLocalTime(inputTime2)}) than earliest output 'C:\Dev\Solution\Project\Output2' ({ToLocalTime(outputTime2)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking built output ({UpToDateCheckBuilt.SchemaName} with {UpToDateCheckBuilt.OriginalProperty} property) file: - Source {ToLocalTime(sourceTime)}: '{sourcePath}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath}' - Source is newer than build output destination, not up-to-date. - """, - "CopySourceNewer"); - } + [Fact] + public async Task IsUpToDateAsync_False_Sets_InputNewerThanOutput_ItemInMultipleSets() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckInput.SchemaName] = ItemWithMetadata("Input.cs", "Set", "Set1;Set2"), + [UpToDateCheckOutput.SchemaName] = Union(ItemWithMetadata("Output1", "Set", "Set1"), ItemWithMetadata("Output2", "Set", "Set2")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-5); + var outputTime2 = DateTime.UtcNow.AddMinutes(-4); + var inputTime = DateTime.UtcNow.AddMinutes(-3); + var outputTime1 = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(_inputPath, inputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime1); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output2", outputTime2); + _fileSystem.AddFile(_builtPath, outputTime1); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime1)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). + Comparing timestamps of inputs and outputs in Set="Set1": + Adding UpToDateCheckOutput outputs in Set="Set1": + C:\Dev\Solution\Project\Output1 + Adding UpToDateCheckInput inputs in Set="Set1": + C:\Dev\Solution\Project\Input.cs + In Set="Set1", no inputs are newer than earliest output 'C:\Dev\Solution\Project\Output1' ({ToLocalTime(outputTime1)}). Newest input is '{_inputPath}' ({ToLocalTime(inputTime)}). + Comparing timestamps of inputs and outputs in Set="Set2": + Adding UpToDateCheckOutput outputs in Set="Set2": + C:\Dev\Solution\Project\Output2 + Adding UpToDateCheckInput inputs in Set="Set2": + C:\Dev\Solution\Project\Input.cs + Input UpToDateCheckInput item '{_inputPath}' is newer ({ToLocalTime(inputTime)}) than earliest output 'C:\Dev\Solution\Project\Output2' ({ToLocalTime(outputTime2)}), not up-to-date. + """, + "InputNewerThanEarliestOutput"); + } - [Fact] - public async Task IsUpToDateAsync_False_BuiltItemWithSource_SourceDoesNotExist() + [Fact] + public async Task IsUpToDateAsync_True_Sets_InputOnly() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = ItemWithMetadata("CopiedOutputDestination", UpToDateCheckBuilt.OriginalProperty, "CopiedOutputSource") - }; + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckInput.SchemaName] = ItemWithMetadata("Input1", "Set", "Set1"), + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var buildTime = DateTime.UtcNow.AddMinutes(-2); + var inputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input1", inputTime); + _fileSystem.AddFile(_builtPath, buildTime); + + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(buildTime)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). + Comparing timestamps of inputs and outputs in Set="Set1": + No build outputs defined in Set="Set1". + Project is up-to-date. + """); + } - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + [Fact] + public async Task IsUpToDateAsync_True_Sets_OutputOnly() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll"), + [UpToDateCheckOutput.SchemaName] = ItemWithMetadata("Output1", "Set", "Set1") + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-3); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-2); + var outputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output1", outputTime); + _fileSystem.AddFile(_builtPath, outputTime); + + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(outputTime)}). Newest input is '{_projectPath}' ({ToLocalTime(_projectFileTimeUtc)}). + Comparing timestamps of inputs and outputs in Set="Set1": + Adding UpToDateCheckOutput outputs in Set="Set1": + C:\Dev\Solution\Project\Output1 + No inputs defined in Set="Set1". + Project is up-to-date. + """); + } - await SetupAsync(projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); + [Fact] + public async Task IsUpToDateAsync_False_Kinds_InputNewerThanOutput_WithIgnoredKind() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("IgnoredBuilt.dll", "Kind", "Ignored")), + [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("IgnoredInput.cs", "Kind", "Ignored")), + [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("IgnoredOutput", "Kind", "Ignored")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var outputTime = DateTime.UtcNow.AddMinutes(-2); + var inputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", inputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", outputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", outputTime); + + await AssertNotUpToDateAsync( + $""" + Ignoring up-to-date check items with Kind="Ignored" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckOutput outputs: + C:\Dev\Solution\Project\Output + Skipping 'C:\Dev\Solution\Project\IgnoredOutput' with ignored Kind="Ignored" + Adding UpToDateCheckBuilt outputs: + C:\Dev\Solution\Project\Built + Skipping 'C:\Dev\Solution\Project\IgnoredBuilt.dll' with ignored Kind="Ignored" + Adding newest import input: + {_projectPath} + Adding UpToDateCheckInput inputs: + C:\Dev\Solution\Project\Input + Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input' is newer ({ToLocalTime(inputTime)}) than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(outputTime)}), not up-to-date. + """, + "InputNewerThanEarliestOutput", + ignoreKinds: "Ignored"); + } - var destinationPath = @"C:\Dev\Solution\Project\CopiedOutputDestination"; - var sourcePath = @"C:\Dev\Solution\Project\CopiedOutputSource"; + [Fact] + public async Task IsUpToDateAsync_False_Kinds_InputNewerThanOutput_NoKindIgnored() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("TaggedBuilt", "Kind", "Tagged")), + [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("TaggedInput", "Kind", "Tagged")), + [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("TaggedOutput", "Kind", "Tagged")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-6); + var input2Time = DateTime.UtcNow.AddMinutes(-5); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-4); + var output1Time = DateTime.UtcNow.AddMinutes(-3); + var output2Time = DateTime.UtcNow.AddMinutes(-2); + var input1Time = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", input1Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedInput", input2Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", output1Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedOutput", output2Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", output1Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedBuilt", output2Time); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckOutput outputs: + C:\Dev\Solution\Project\Output + C:\Dev\Solution\Project\TaggedOutput + Adding UpToDateCheckBuilt outputs: + C:\Dev\Solution\Project\Built + C:\Dev\Solution\Project\TaggedBuilt + Adding newest import input: + {_projectPath} + Adding UpToDateCheckInput inputs: + C:\Dev\Solution\Project\Input + Input UpToDateCheckInput item 'C:\Dev\Solution\Project\Input' is newer ({ToLocalTime(input1Time)}) than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(output1Time)}), not up-to-date. + """, + "InputNewerThanEarliestOutput", + ignoreKinds: ""); + } - _fileSystem.AddFile(destinationPath); + [Fact] + public async Task IsUpToDateAsync_True_Kinds_InputNewerThanOutput_WithIgnoredKind() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("IgnoredBuilt", "Kind", "Ignored")), + [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("IgnoredInput", "Kind", "Ignored")), + [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("IgnoredOutput", "Kind", "Ignored")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var inputTime = DateTime.UtcNow.AddMinutes(-3); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-2); + var outputTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", inputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", outputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", outputTime); + + await AssertUpToDateAsync( + $""" + Ignoring up-to-date check items with Kind="Ignored" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckOutput outputs: + C:\Dev\Solution\Project\Output + Skipping 'C:\Dev\Solution\Project\IgnoredOutput' with ignored Kind="Ignored" + Adding UpToDateCheckBuilt outputs: + C:\Dev\Solution\Project\Built + Skipping 'C:\Dev\Solution\Project\IgnoredBuilt' with ignored Kind="Ignored" + Adding newest import input: + {_projectPath} + Adding UpToDateCheckInput inputs: + C:\Dev\Solution\Project\Input + Skipping 'C:\Dev\Solution\Project\IgnoredInput' with ignored Kind="Ignored" + No inputs are newer than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(outputTime)}). Newest input is 'C:\Dev\Solution\Project\Input' ({ToLocalTime(inputTime)}). + Project is up-to-date. + """, + ignoreKinds: "Ignored"); + } - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking built output ({UpToDateCheckBuilt.SchemaName} with {UpToDateCheckBuilt.OriginalProperty} property) file: - Source '{sourcePath}' does not exist for build into target '{destinationPath}', not up-to-date. - """, - "CopySourceNotFound"); - } + [Fact] + public async Task IsUpToDateAsync_True_Kinds_InputNewerThanOutput_NoKindIgnored() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = ItemsWithMetadata(("Built", "Kind", ""), ("TaggedBuilt", "Kind", "Tagged")), + [UpToDateCheckInput.SchemaName] = ItemsWithMetadata(("Input", "Kind", ""), ("TaggedInput", "Kind", "Tagged")), + [UpToDateCheckOutput.SchemaName] = ItemsWithMetadata(("Output", "Kind", ""), ("TaggedOutput", "Kind", "Tagged")) + }; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-8); + var input2Time = DateTime.UtcNow.AddMinutes(-7); + var input1Time = DateTime.UtcNow.AddMinutes(-6); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-5); + var output4Time = DateTime.UtcNow.AddMinutes(-4); + var output3Time = DateTime.UtcNow.AddMinutes(-3); + var output2Time = DateTime.UtcNow.AddMinutes(-2); + var output1Time = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Input", input2Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedInput", input1Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Output", output4Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedOutput", output3Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\Built", output2Time); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\TaggedBuilt", output1Time); + + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckOutput outputs: + C:\Dev\Solution\Project\Output + C:\Dev\Solution\Project\TaggedOutput + Adding UpToDateCheckBuilt outputs: + C:\Dev\Solution\Project\Built + C:\Dev\Solution\Project\TaggedBuilt + Adding newest import input: + {_projectPath} + Adding UpToDateCheckInput inputs: + C:\Dev\Solution\Project\Input + C:\Dev\Solution\Project\TaggedInput + No inputs are newer than earliest output 'C:\Dev\Solution\Project\Output' ({ToLocalTime(output4Time)}). Newest input is 'C:\Dev\Solution\Project\TaggedInput' ({ToLocalTime(input1Time)}). + Project is up-to-date. + """, + ignoreKinds: ""); + } - [Fact] - public async Task IsUpToDateAsync_False_BuiltItemWithSource_DestinationDoesNotExist() + [Fact] + public async Task IsUpToDateAsync_False_BuiltItemWithSource_SourceIsNewerThanDestination() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = ItemWithMetadata("CopiedOutputDestination", UpToDateCheckBuilt.OriginalProperty, "CopiedOutputSource") - }; + [UpToDateCheckBuilt.SchemaName] = ItemWithMetadata("CopiedOutputDestination", UpToDateCheckBuilt.OriginalProperty, "CopiedOutputSource") + }; + + var destinationPath = @"C:\Dev\Solution\Project\CopiedOutputDestination"; + var sourcePath = @"C:\Dev\Solution\Project\CopiedOutputSource"; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var destinationTime = DateTime.UtcNow.AddMinutes(-2); + var sourceTime = DateTime.UtcNow.AddMinutes(-1); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(destinationPath, destinationTime); + _fileSystem.AddFile(sourcePath, sourceTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking built output ({UpToDateCheckBuilt.SchemaName} with {UpToDateCheckBuilt.OriginalProperty} property) file: + Source {ToLocalTime(sourceTime)}: '{sourcePath}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath}' + Source is newer than build output destination, not up-to-date. + """, + "CopySourceNewer"); + } - var destinationPath = @"C:\Dev\Solution\Project\CopiedOutputDestination"; - var sourcePath = @"C:\Dev\Solution\Project\CopiedOutputSource"; + [Fact] + public async Task IsUpToDateAsync_False_BuiltItemWithSource_SourceDoesNotExist() + { + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = ItemWithMetadata("CopiedOutputDestination", UpToDateCheckBuilt.OriginalProperty, "CopiedOutputSource") + }; - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var sourceTime = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); - await SetupAsync( - projectSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + await SetupAsync(projectSnapshot, lastSuccessfulBuildStartTimeUtc: lastBuildTime); - _fileSystem.AddFile(sourcePath, sourceTime); + var destinationPath = @"C:\Dev\Solution\Project\CopiedOutputDestination"; + var sourcePath = @"C:\Dev\Solution\Project\CopiedOutputSource"; - await AssertNotUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking built output ({UpToDateCheckBuilt.SchemaName} with {UpToDateCheckBuilt.OriginalProperty} property) file: - Source {ToLocalTime(sourceTime)}: '{sourcePath}' - Destination '{destinationPath}' does not exist for copy from '{sourcePath}', not up-to-date. - """, - "CopyDestinationNotFound"); - } + _fileSystem.AddFile(destinationPath); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking built output ({UpToDateCheckBuilt.SchemaName} with {UpToDateCheckBuilt.OriginalProperty} property) file: + Source '{sourcePath}' does not exist for build into target '{destinationPath}', not up-to-date. + """, + "CopySourceNotFound"); + } - [Theory] - [CombinatorialData] - public async Task IsUpToDateAsync_CopyToOutputDirectory_SourceIsNewerThanDestination(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) + [Fact] + public async Task IsUpToDateAsync_False_BuiltItemWithSource_DestinationDoesNotExist() + { + var projectSnapshot = new Dictionary { - SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); + [UpToDateCheckBuilt.SchemaName] = ItemWithMetadata("CopiedOutputDestination", UpToDateCheckBuilt.OriginalProperty, "CopiedOutputSource") + }; + + var destinationPath = @"C:\Dev\Solution\Project\CopiedOutputDestination"; + var sourcePath = @"C:\Dev\Solution\Project\CopiedOutputSource"; + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var sourceTime = DateTime.UtcNow.AddMinutes(-2); + + await SetupAsync( + projectSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(sourcePath, sourceTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking built output ({UpToDateCheckBuilt.SchemaName} with {UpToDateCheckBuilt.OriginalProperty} property) file: + Source {ToLocalTime(sourceTime)}: '{sourcePath}' + Destination '{destinationPath}' does not exist for copy from '{sourcePath}', not up-to-date. + """, + "CopyDestinationNotFound"); + } - var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; - var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; - var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; - var destinationPath2 = @"C:\Dev\Solution\Project\bin\Debug\Item2"; + [Theory] + [CombinatorialData] + public async Task IsUpToDateAsync_CopyToOutputDirectory_SourceIsNewerThanDestination(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) + { + SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); - SetCopyItems( - new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), - new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); + var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; + var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; + var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; + var destinationPath2 = @"C:\Dev\Solution\Project\bin\Debug\Item2"; - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var destinationTime = DateTime.UtcNow.AddMinutes(-2); - var sourceTime = DateTime.UtcNow.AddMinutes(-1); + SetCopyItems( + new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), + new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); - await SetupAsync( - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var destinationTime = DateTime.UtcNow.AddMinutes(-2); + var sourceTime = DateTime.UtcNow.AddMinutes(-1); - _fileSystem.AddFile(sourcePath1, sourceTime); - _fileSystem.AddFile(sourcePath2, sourceTime); - _fileSystem.AddFile(destinationPath1, destinationTime); - _fileSystem.AddFile(destinationPath2, destinationTime); + await SetupAsync( + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); - if (isBuildAccelerationEnabledInProject is true) - { - if (allReferencesProduceReferenceAssemblies) - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - """); - } - else - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. - """); - } - } - else if (isBuildAccelerationEnabledInProject is false) + _fileSystem.AddFile(sourcePath1, sourceTime); + _fileSystem.AddFile(sourcePath2, sourceTime); + _fileSystem.AddFile(destinationPath1, destinationTime); + _fileSystem.AddFile(destinationPath2, destinationTime); + + if (isBuildAccelerationEnabledInProject is true) + { + if (allReferencesProduceReferenceAssemblies) { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -1758,15 +1708,23 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. - """, - "CopyToOutputDirectorySourceNewer"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + """); } - else if (isBuildAccelerationEnabledInProject is null) + else { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -1774,99 +1732,91 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. - This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. - """, - "CopyToOutputDirectorySourceNewer"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. + """); } } - - [Theory] - [CombinatorialData] - public async Task IsUpToDateAsync_CopyToOutputDirectory_SourceIsNewerThanDestination_TargetPath(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) + else if (isBuildAccelerationEnabledInProject is false) + { + await AssertNotUpToDateAsync( + $""" + Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' + Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. + """, + "CopyToOutputDirectorySourceNewer"); + } + else if (isBuildAccelerationEnabledInProject is null) { - SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' + Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. + This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. + """, + "CopyToOutputDirectorySourceNewer"); + } + } - var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; - var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; - var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; - var destinationPath2 = @"C:\Dev\Solution\Project\bin\Debug\Item2"; + [Theory] + [CombinatorialData] + public async Task IsUpToDateAsync_CopyToOutputDirectory_SourceIsNewerThanDestination_TargetPath(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) + { + SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); - SetCopyItems( - new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), - new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); + var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; + var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; + var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; + var destinationPath2 = @"C:\Dev\Solution\Project\bin\Debug\Item2"; - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var destinationTime = DateTime.UtcNow.AddMinutes(-2); - var sourceTime = DateTime.UtcNow.AddMinutes(-1); + SetCopyItems( + new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), + new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); - await SetupAsync( - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var destinationTime = DateTime.UtcNow.AddMinutes(-2); + var sourceTime = DateTime.UtcNow.AddMinutes(-1); - _fileSystem.AddFile(sourcePath1, sourceTime); - _fileSystem.AddFile(sourcePath2, sourceTime); - _fileSystem.AddFile(destinationPath1, destinationTime); - _fileSystem.AddFile(destinationPath2, destinationTime); + await SetupAsync( + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); - if (isBuildAccelerationEnabledInProject is true) - { - if (allReferencesProduceReferenceAssemblies) - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - """); - } - else - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. - """); - } - } - else if (isBuildAccelerationEnabledInProject is false) + _fileSystem.AddFile(sourcePath1, sourceTime); + _fileSystem.AddFile(sourcePath2, sourceTime); + _fileSystem.AddFile(destinationPath1, destinationTime); + _fileSystem.AddFile(destinationPath2, destinationTime); + + if (isBuildAccelerationEnabledInProject is true) + { + if (allReferencesProduceReferenceAssemblies) { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -1874,15 +1824,23 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. - """, - "CopyToOutputDirectorySourceNewer"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + """); } - else if (isBuildAccelerationEnabledInProject is null) + else { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -1890,102 +1848,94 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. - This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. - """, - "CopyToOutputDirectorySourceNewer"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. + """); } } + else if (isBuildAccelerationEnabledInProject is false) + { + await AssertNotUpToDateAsync( + $""" + Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' + Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. + """, + "CopyToOutputDirectorySourceNewer"); + } + else if (isBuildAccelerationEnabledInProject is null) + { + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' + Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. + This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. + """, + "CopyToOutputDirectorySourceNewer"); + } + } - [Theory] - [CombinatorialData] - public async Task IsUpToDateAsync_CopyToOutputDirectory_SourceIsNewerThanDestination_CustomOutDir(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) - { - SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); + [Theory] + [CombinatorialData] + public async Task IsUpToDateAsync_CopyToOutputDirectory_SourceIsNewerThanDestination_CustomOutDir(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) + { + SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); - const string outDirSnapshot = "newOutDir"; + const string outDirSnapshot = "newOutDir"; - var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; - var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; - var destinationPath1 = $@"C:\Dev\Solution\Project\{outDirSnapshot}\Item1"; - var destinationPath2 = $@"C:\Dev\Solution\Project\{outDirSnapshot}\Item2"; + var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; + var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; + var destinationPath1 = $@"C:\Dev\Solution\Project\{outDirSnapshot}\Item1"; + var destinationPath2 = $@"C:\Dev\Solution\Project\{outDirSnapshot}\Item2"; - SetCopyItems( - new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), - new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); + SetCopyItems( + new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), + new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var destinationTime = DateTime.UtcNow.AddMinutes(-2); - var sourceTime = DateTime.UtcNow.AddMinutes(-1); + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var destinationTime = DateTime.UtcNow.AddMinutes(-2); + var sourceTime = DateTime.UtcNow.AddMinutes(-1); - await SetupAsync( - outDir: outDirSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + await SetupAsync( + outDir: outDirSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); - _fileSystem.AddFile(destinationPath1, destinationTime); - _fileSystem.AddFile(destinationPath2, destinationTime); - _fileSystem.AddFile(sourcePath1, sourceTime); - _fileSystem.AddFile(sourcePath2, sourceTime); + _fileSystem.AddFile(destinationPath1, destinationTime); + _fileSystem.AddFile(destinationPath2, destinationTime); + _fileSystem.AddFile(sourcePath1, sourceTime); + _fileSystem.AddFile(sourcePath2, sourceTime); - if (isBuildAccelerationEnabledInProject is true) - { - if (allReferencesProduceReferenceAssemblies) - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - """); - } - else - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. - """); - } - } - else if (isBuildAccelerationEnabledInProject is false) + if (isBuildAccelerationEnabledInProject is true) + { + if (allReferencesProduceReferenceAssemblies) { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -1993,15 +1943,23 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. - """, - "CopyToOutputDirectorySourceNewer"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + """); } - else if (isBuildAccelerationEnabledInProject is null) + else { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -2009,34 +1967,38 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' - Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. - This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. - """, - "CopyToOutputDirectorySourceNewer"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath2}' + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. + """); } } - - [Fact] - public async Task IsUpToDateAsync_False_CopyToOutputDirectory_SourceDoesNotExist() + else if (isBuildAccelerationEnabledInProject is false) + { + await AssertNotUpToDateAsync( + $""" + Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' + Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. + """, + "CopyToOutputDirectorySourceNewer"); + } + else if (isBuildAccelerationEnabledInProject is null) { - var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; - var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; - var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; - - SetCopyItems( - new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), - new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); - - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var destinationTime = DateTime.UtcNow.AddMinutes(-2); - - await SetupAsync( - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); - - _fileSystem.AddFile(destinationPath1, destinationTime); - await AssertNotUpToDateAsync( $""" Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. @@ -2045,116 +2007,104 @@ No build outputs defined. Checking items to copy to the output directory: Checking copy items from project '{_projectPath}': Checking PreserveNewest item - Source '{sourcePath1}' does not exist, not up-to-date. + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination {ToLocalTime(destinationTime)}: '{destinationPath1}' + Item with CopyToOutputDirectory="PreserveNewest" source '{sourcePath1}' is newer than destination '{destinationPath1}', not up-to-date. + This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. """, - "CopyToOutputDirectorySourceNotFound"); + "CopyToOutputDirectorySourceNewer"); } + } + + [Fact] + public async Task IsUpToDateAsync_False_CopyToOutputDirectory_SourceDoesNotExist() + { + var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; + var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; + var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; + + SetCopyItems( + new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), + new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); + + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var destinationTime = DateTime.UtcNow.AddMinutes(-2); + + await SetupAsync( + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); + + _fileSystem.AddFile(destinationPath1, destinationTime); + + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source '{sourcePath1}' does not exist, not up-to-date. + """, + "CopyToOutputDirectorySourceNotFound"); + } - [Fact] - public async Task BuildAcceleration_DisabledDueToIncompatibleProjectReference() + [Fact] + public async Task BuildAcceleration_DisabledDueToIncompatibleProjectReference() + { + var projectSnapshot = new Dictionary { - var projectSnapshot = new Dictionary - { - [BuildAccelerationIncompatiblePackage.SchemaName] = SimpleItems("IncompatibleWithBuildAcceleration", "AnotherIncompatiblePackages"), - [PackageReference.SchemaName] = SimpleItems("NormalPackage", "IncompatibleWithBuildAcceleration") - }; + [BuildAccelerationIncompatiblePackage.SchemaName] = SimpleItems("IncompatibleWithBuildAcceleration", "AnotherIncompatiblePackages"), + [PackageReference.SchemaName] = SimpleItems("NormalPackage", "IncompatibleWithBuildAcceleration") + }; - await SetupAsync(projectSnapshot: projectSnapshot); + await SetupAsync(projectSnapshot: projectSnapshot); - _expectedIsBuildAccelerationEnabled = false; + _expectedIsBuildAccelerationEnabled = false; - await AssertNotUpToDateAsync( - """ - Build acceleration has been disabled for this project due to known incompatible NuGet package reference(s) 'IncompatibleWithBuildAcceleration'. See https://aka.ms/vs-build-acceleration. - The up-to-date check has not yet run for this project. Not up-to-date. - """, - "FirstRun", - skipValidation: true); - } + await AssertNotUpToDateAsync( + """ + Build acceleration has been disabled for this project due to known incompatible NuGet package reference(s) 'IncompatibleWithBuildAcceleration'. See https://aka.ms/vs-build-acceleration. + The up-to-date check has not yet run for this project. Not up-to-date. + """, + "FirstRun", + skipValidation: true); + } - [Theory] - [CombinatorialData] - public async Task IsUpToDateAsync_CopyToOutputDirectory_DestinationDoesNotExist(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) - { - SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); + [Theory] + [CombinatorialData] + public async Task IsUpToDateAsync_CopyToOutputDirectory_DestinationDoesNotExist(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies) + { + SetUpAcceleratedTestCase(isBuildAccelerationEnabledInProject, allReferencesProduceReferenceAssemblies); - var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; - var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; - var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; - var destinationPath2 = @"C:\Dev\Solution\Project\bin\Debug\Item2"; + var sourcePath1 = @"C:\Dev\Solution\Project\Item1"; + var sourcePath2 = @"C:\Dev\Solution\Project\Item2"; + var destinationPath1 = @"C:\Dev\Solution\Project\bin\Debug\Item1"; + var destinationPath2 = @"C:\Dev\Solution\Project\bin\Debug\Item2"; - SetCopyItems( - new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), - new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); + SetCopyItems( + new CopyItem(sourcePath1, "Item1", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: false), + new CopyItem(sourcePath2, "Item2", BuildUpToDateCheck.CopyType.PreserveNewest, isBuildAccelerationOnly: true)); - var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); - var sourceTime = DateTime.UtcNow.AddMinutes(-2); + var itemChangeTime = DateTime.UtcNow.AddMinutes(-4); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-3); + var sourceTime = DateTime.UtcNow.AddMinutes(-2); - await SetupAsync( - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangeTime); + await SetupAsync( + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangeTime); - _fileSystem.AddFile(sourcePath1, sourceTime); - _fileSystem.AddFile(sourcePath2, sourceTime); + _fileSystem.AddFile(sourcePath1, sourceTime); + _fileSystem.AddFile(sourcePath2, sourceTime); - if (isBuildAccelerationEnabledInProject is true) - { - if (allReferencesProduceReferenceAssemblies) - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination '{destinationPath1}' does not exist. - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination '{destinationPath2}' does not exist. - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - """); - } - else - { - await AssertUpToDateAsync( - $""" - Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - No build outputs defined. - Checking items to copy to the output directory: - Checking copy items from project '{_projectPath}': - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath1}' - Destination '{destinationPath1}' does not exist. - Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. - Checking PreserveNewest item - Source {ToLocalTime(sourceTime)}: '{sourcePath2}' - Destination '{destinationPath2}' does not exist. - Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. - Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): - From '{sourcePath1}' to '{destinationPath1}'. - From '{sourcePath2}' to '{destinationPath2}'. - Build acceleration copied 2 files. - Project is up-to-date. - This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. - """); - } - } - else if (isBuildAccelerationEnabledInProject is false) + if (isBuildAccelerationEnabledInProject is true) + { + if (allReferencesProduceReferenceAssemblies) { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -2162,15 +2112,23 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination '{destinationPath1}' does not exist. - Destination '{destinationPath1}' does not exist, not up-to-date. - """, - "CopyToOutputDirectoryDestinationNotFound"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination '{destinationPath2}' does not exist. + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + """); } - else if (isBuildAccelerationEnabledInProject is null) + else { - await AssertNotUpToDateAsync( + await AssertUpToDateAsync( $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Build acceleration is enabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. Comparing timestamps of inputs and outputs: No build outputs defined. Checking items to copy to the output directory: @@ -2178,424 +2136,465 @@ No build outputs defined. Checking PreserveNewest item Source {ToLocalTime(sourceTime)}: '{sourcePath1}' Destination '{destinationPath1}' does not exist. - Destination '{destinationPath1}' does not exist, not up-to-date. - This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. - """, - "CopyToOutputDirectoryDestinationNotFound"); + Remembering the need to copy file '{sourcePath1}' to '{destinationPath1}'. + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath2}' + Destination '{destinationPath2}' does not exist. + Remembering the need to copy file '{sourcePath2}' to '{destinationPath2}'. + Copying 2 files to accelerate build (https://aka.ms/vs-build-acceleration): + From '{sourcePath1}' to '{destinationPath1}'. + From '{sourcePath2}' to '{destinationPath2}'. + Build acceleration copied 2 files. + Project is up-to-date. + This project has enabled build acceleration, but not all referenced projects produce a reference assembly. Ensure projects producing the following outputs have the 'ProduceReferenceAssembly' MSBuild property set to 'true': 'WithoutReferenceAssembly1', 'WithoutReferenceAssembly2'. See https://aka.ms/vs-build-acceleration for more information. + """); } } - - [Fact] - public void ComputeItemHash() + else if (isBuildAccelerationEnabledInProject is false) { - Assert.Equal( - HashItems(("Compile", new[] { "Path1" })), - HashItems(("Compile", new[] { "Path1" }))); - - // Order independent - Assert.Equal( - HashItems(("Compile", new[] { "Path1", "Path2" })), - HashItems(("Compile", new[] { "Path2", "Path1" }))); - - // Item type dependent - Assert.NotEqual( - HashItems(("Compile", new[] { "Path1" })), - HashItems(("None", new[] { "Path1" }))); - - // Adding an item causes a difference - Assert.NotEqual( - HashItems(("Compile", new[] { "Path1" })), - HashItems(("Compile", new[] { "Path1", "Path2" }))); - - static int HashItems(params (string ItemType, string[] Paths)[] items) - { - var itemsByItemType = items.ToImmutableDictionary( - i => i.ItemType, - i => i.Paths.ToImmutableArray()); - - return BuildUpToDateCheck.ComputeItemHash(itemsByItemType); - } + await AssertNotUpToDateAsync( + $""" + Build acceleration is disabled for this project via the 'AccelerateBuildsInVisualStudio' MSBuild property. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination '{destinationPath1}' does not exist. + Destination '{destinationPath1}' does not exist, not up-to-date. + """, + "CopyToOutputDirectoryDestinationNotFound"); } - - [Theory] - [InlineData("", 371857150)] - [InlineData("Hello, World!", 562640209)] - [InlineData("Test", -871204762)] - [InlineData("TEst", -2094096442)] - [InlineData("Some text", -1076058823)] - [InlineData("AAAAAAAAAAAAAAAAAAAAA", 1397387549)] - [InlineData("1(*!)#2*$&@_fa!f(#(b$(12", -2128933005)] - public void GetStableHashCode_Matches_NetFrameworkX64(string str, int netFrameworkX64HashPrecomputed) + else if (isBuildAccelerationEnabledInProject is null) { - int hash = BuildUpToDateCheck.GetStableHashCode(str); - - Assert.Equal(hash, netFrameworkX64HashPrecomputed); + await AssertNotUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + No build outputs defined. + Checking items to copy to the output directory: + Checking copy items from project '{_projectPath}': + Checking PreserveNewest item + Source {ToLocalTime(sourceTime)}: '{sourcePath1}' + Destination '{destinationPath1}' does not exist. + Destination '{destinationPath1}' does not exist, not up-to-date. + This project appears to be a candidate for build acceleration. To opt in, set the 'AccelerateBuildsInVisualStudio' MSBuild property to 'true'. See https://aka.ms/vs-build-acceleration. + """, + "CopyToOutputDirectoryDestinationNotFound"); } + } - [Fact] - public void GetStableHashCode_SameText_SameHash() + [Fact] + public void ComputeItemHash() + { + Assert.Equal( + HashItems(("Compile", new[] { "Path1" })), + HashItems(("Compile", new[] { "Path1" }))); + + // Order independent + Assert.Equal( + HashItems(("Compile", new[] { "Path1", "Path2" })), + HashItems(("Compile", new[] { "Path2", "Path1" }))); + + // Item type dependent + Assert.NotEqual( + HashItems(("Compile", new[] { "Path1" })), + HashItems(("None", new[] { "Path1" }))); + + // Adding an item causes a difference + Assert.NotEqual( + HashItems(("Compile", new[] { "Path1" })), + HashItems(("Compile", new[] { "Path1", "Path2" }))); + + static int HashItems(params (string ItemType, string[] Paths)[] items) { - string str1 = "SomeText"; - string str2 = "SomeText"; + var itemsByItemType = items.ToImmutableDictionary( + i => i.ItemType, + i => i.Paths.ToImmutableArray()); - int h1 = BuildUpToDateCheck.GetStableHashCode(str1); - int h2 = BuildUpToDateCheck.GetStableHashCode(str2); - - Assert.Equal(h1, h2); + return BuildUpToDateCheck.ComputeItemHash(itemsByItemType); } + } - [Fact] - public void GetStableHashCode_DifferentText_DifferentHash() - { - string str1 = "SomeText"; - string str2 = "SomeTexT"; - - int h1 = BuildUpToDateCheck.GetStableHashCode(str1); - int h2 = BuildUpToDateCheck.GetStableHashCode(str2); - - Assert.NotEqual(h1, h2); - } + [Theory] + [InlineData("", 371857150)] + [InlineData("Hello, World!", 562640209)] + [InlineData("Test", -871204762)] + [InlineData("TEst", -2094096442)] + [InlineData("Some text", -1076058823)] + [InlineData("AAAAAAAAAAAAAAAAAAAAA", 1397387549)] + [InlineData("1(*!)#2*$&@_fa!f(#(b$(12", -2128933005)] + public void GetStableHashCode_Matches_NetFrameworkX64(string str, int netFrameworkX64HashPrecomputed) + { + int hash = BuildUpToDateCheck.GetStableHashCode(str); - [Fact] - public async Task IsUpToDateAsync_True_InputNewerThatBuiltOutput_TargetFrameworkDoesNotMatchBuild() - { - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; + Assert.Equal(hash, netFrameworkX64HashPrecomputed); + } - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("ItemPath1") - }; + [Fact] + public void GetStableHashCode_SameText_SameHash() + { + string str1 = "SomeText"; + string str2 = "SomeText"; - var itemChangedTime = DateTime.UtcNow.AddMinutes(-4); - var outputTime = DateTime.UtcNow.AddMinutes(-3); - var inputTime = DateTime.UtcNow.AddMinutes(-2); - var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + int h1 = BuildUpToDateCheck.GetStableHashCode(str1); + int h2 = BuildUpToDateCheck.GetStableHashCode(str2); - var configuredInput = UpToDateCheckImplicitConfiguredInput.CreateEmpty( - ProjectConfigurationFactory.Create("TargetFramework", "alphaFramework")); + Assert.Equal(h1, h2); + } - await SetupAsync( - projectSnapshot, - sourceSnapshot, - lastSuccessfulBuildStartTimeUtc: lastBuildTime, - lastItemsChangedAtUtc: itemChangedTime, - upToDateCheckImplicitConfiguredInput: configuredInput); + [Fact] + public void GetStableHashCode_DifferentText_DifferentHash() + { + string str1 = "SomeText"; + string str2 = "SomeTexT"; - _fileSystem.AddFile(_builtPath, outputTime); - _fileSystem.AddFile(@"C:\Dev\Solution\Project\ItemPath1", inputTime); + int h1 = BuildUpToDateCheck.GetStableHashCode(str1); + int h2 = BuildUpToDateCheck.GetStableHashCode(str2); - await AssertUpToDateAsync( - """ - Project is up-to-date. - """, - targetFramework: "betaFramework"); - } + Assert.NotEqual(h1, h2); + } - [Fact] - public async Task IsUpToDateAsync_True_LogsOtherUpToDateCheckProviders() + [Fact] + public async Task IsUpToDateAsync_True_InputNewerThatBuiltOutput_TargetFrameworkDoesNotMatchBuild() + { + var projectSnapshot = new Dictionary { - _buildUpToDateCheck.UpToDateCheckers.Add(new MockProvider()); - - var projectSnapshot = new Dictionary - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") - }; - - var sourceSnapshot = new Dictionary - { - [Compile.SchemaName] = SimpleItems("Input.cs") - }; - - // The rule is that no input item should be modified since the last successful build started. - - var t0 = DateTime.UtcNow.AddMinutes(-3); // t0 Input file timestamp - var t1 = DateTime.UtcNow.AddMinutes(-2); // t1 Rebuild (sets output file timestamp) + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - _fileSystem.AddFile(_inputPath, t0); - _fileSystem.AddFile(_builtPath, t1); + var sourceSnapshot = new Dictionary + { + [Compile.SchemaName] = SimpleItems("ItemPath1") + }; + + var itemChangedTime = DateTime.UtcNow.AddMinutes(-4); + var outputTime = DateTime.UtcNow.AddMinutes(-3); + var inputTime = DateTime.UtcNow.AddMinutes(-2); + var lastBuildTime = DateTime.UtcNow.AddMinutes(-1); + + var configuredInput = UpToDateCheckImplicitConfiguredInput.CreateEmpty( + ProjectConfigurationFactory.Create("TargetFramework", "alphaFramework")); + + await SetupAsync( + projectSnapshot, + sourceSnapshot, + lastSuccessfulBuildStartTimeUtc: lastBuildTime, + lastItemsChangedAtUtc: itemChangedTime, + upToDateCheckImplicitConfiguredInput: configuredInput); + + _fileSystem.AddFile(_builtPath, outputTime); + _fileSystem.AddFile(@"C:\Dev\Solution\Project\ItemPath1", inputTime); + + await AssertUpToDateAsync( + """ + Project is up-to-date. + """, + targetFramework: "betaFramework"); + } - await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t1); + [Fact] + public async Task IsUpToDateAsync_True_LogsOtherUpToDateCheckProviders() + { + _buildUpToDateCheck.UpToDateCheckers.Add(new MockProvider()); - // Rebuild (t1) - ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t1); - await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: true); + var projectSnapshot = new Dictionary + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems(@"bin\Debug\Built.dll") + }; - // Run test (t2) - await AssertUpToDateAsync( - $""" - Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. - Comparing timestamps of inputs and outputs: - Adding UpToDateCheckBuilt outputs: - {_builtPath} - Adding newest import input: - {_projectPath} - Adding Compile inputs: - {_inputPath} - No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(t1)}). Newest input is '{_inputPath}' ({ToLocalTime(t0)}). - Project is up-to-date. - Note that other checks may still determine the project is not up-to-date. The following checks are present: - - Microsoft.VisualStudio.ProjectSystem.VS.UpToDate.BuildUpToDateCheckTests+MockProvider - """); - } + var sourceSnapshot = new Dictionary + { + [Compile.SchemaName] = SimpleItems("Input.cs") + }; + + // The rule is that no input item should be modified since the last successful build started. + + var t0 = DateTime.UtcNow.AddMinutes(-3); // t0 Input file timestamp + var t1 = DateTime.UtcNow.AddMinutes(-2); // t1 Rebuild (sets output file timestamp) + + _fileSystem.AddFile(_inputPath, t0); + _fileSystem.AddFile(_builtPath, t1); + + await SetupAsync(projectSnapshot, sourceSnapshot, lastSuccessfulBuildStartTimeUtc: t1); + + // Rebuild (t1) + ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildStarting(buildStartTimeUtc: t1); + await ((IProjectBuildEventListener)_buildUpToDateCheck).NotifyBuildCompletedAsync(wasSuccessful: true, isRebuild: true); + + // Run test (t2) + await AssertUpToDateAsync( + $""" + Build acceleration is not enabled for this project. See https://aka.ms/vs-build-acceleration. + Comparing timestamps of inputs and outputs: + Adding UpToDateCheckBuilt outputs: + {_builtPath} + Adding newest import input: + {_projectPath} + Adding Compile inputs: + {_inputPath} + No inputs are newer than earliest output '{_builtPath}' ({ToLocalTime(t1)}). Newest input is '{_inputPath}' ({ToLocalTime(t0)}). + Project is up-to-date. + Note that other checks may still determine the project is not up-to-date. The following checks are present: + - Microsoft.VisualStudio.ProjectSystem.VS.UpToDate.BuildUpToDateCheckTests+MockProvider + """); + } - #region Test helpers + #region Test helpers - private void SetCopyItems(params CopyItem[] items) + private void SetCopyItems(params CopyItem[] items) + { + _copyItems = new (string Path, ImmutableArray CopyItems)[] { - _copyItems = new (string Path, ImmutableArray CopyItems)[] - { - (_projectPath, items.ToImmutableArray()) - }; - } + (_projectPath, items.ToImmutableArray()) + }; + } - private static string ToLocalTime(DateTime time) - { - return time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"); - } + private static string ToLocalTime(DateTime time) + { + return time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fff"); + } - private void SetUpAcceleratedTestCase(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies, bool hasDuplicateCopyItemRelativeTargetPaths = false) - { - _isBuildAccelerationEnabledInProject = isBuildAccelerationEnabledInProject; - _expectedIsBuildAccelerationEnabled = isBuildAccelerationEnabledInProject; - _targetsWithoutReferenceAssemblies = allReferencesProduceReferenceAssemblies ? null : new[] { "WithoutReferenceAssembly1", "WithoutReferenceAssembly2" }; - _duplicateCopyItemRelativeTargetPaths = hasDuplicateCopyItemRelativeTargetPaths ? new[] { "appsettings.json", "AnotherFile.dll" } : null; - } + private void SetUpAcceleratedTestCase(bool? isBuildAccelerationEnabledInProject, bool allReferencesProduceReferenceAssemblies, bool hasDuplicateCopyItemRelativeTargetPaths = false) + { + _isBuildAccelerationEnabledInProject = isBuildAccelerationEnabledInProject; + _expectedIsBuildAccelerationEnabled = isBuildAccelerationEnabledInProject; + _targetsWithoutReferenceAssemblies = allReferencesProduceReferenceAssemblies ? null : new[] { "WithoutReferenceAssembly1", "WithoutReferenceAssembly2" }; + _duplicateCopyItemRelativeTargetPaths = hasDuplicateCopyItemRelativeTargetPaths ? new[] { "appsettings.json", "AnotherFile.dll" } : null; + } - private async Task AssertNotUpToDateAsync(string? expectedLogOutput = null, string? telemetryReason = null, BuildAction buildAction = BuildAction.Build, string ignoreKinds = "", string targetFramework = "", bool skipValidation = false) - { - _expectedUpToDate = false; + private async Task AssertNotUpToDateAsync(string? expectedLogOutput = null, string? telemetryReason = null, BuildAction buildAction = BuildAction.Build, string ignoreKinds = "", string targetFramework = "", bool skipValidation = false) + { + _expectedUpToDate = false; - var writer = new AssertWriter(_output, expectedLogOutput ?? ""); + var writer = new AssertWriter(_output, expectedLogOutput ?? ""); - var isUpToDate = await _buildUpToDateCheck.IsUpToDateAsync(buildAction, writer, CreateGlobalProperties(ignoreKinds, targetFramework)); + var isUpToDate = await _buildUpToDateCheck.IsUpToDateAsync(buildAction, writer, CreateGlobalProperties(ignoreKinds, targetFramework)); - writer.Assert(); + writer.Assert(); - if (telemetryReason is not null) - AssertTelemetryFailureEvent(telemetryReason, ignoreKinds); - else - Assert.Empty(_telemetryEvents); + if (telemetryReason is not null) + AssertTelemetryFailureEvent(telemetryReason, ignoreKinds); + else + Assert.Empty(_telemetryEvents); - Assert.False(isUpToDate, "Expected not up-to-date, but was."); + Assert.False(isUpToDate, "Expected not up-to-date, but was."); - if (!skipValidation && buildAction == BuildAction.Build) + if (!skipValidation && buildAction == BuildAction.Build) + { + if (telemetryReason == "Disabled") { - if (telemetryReason == "Disabled") - { - await ValidateUpToDateAsync(); - } - else - { - await ValidateNotUpToDateAsync(); - } + await ValidateUpToDateAsync(); + } + else + { + await ValidateNotUpToDateAsync(); } } + } - private async Task AssertUpToDateAsync(string expectedLogOutput, string ignoreKinds = "", string targetFramework = "") - { - _expectedUpToDate = true; + private async Task AssertUpToDateAsync(string expectedLogOutput, string ignoreKinds = "", string targetFramework = "") + { + _expectedUpToDate = true; - var writer = new AssertWriter(_output, expectedLogOutput); + var writer = new AssertWriter(_output, expectedLogOutput); - bool isUpToDate = await _buildUpToDateCheck.IsUpToDateAsync(BuildAction.Build, writer, CreateGlobalProperties(ignoreKinds, targetFramework)); + bool isUpToDate = await _buildUpToDateCheck.IsUpToDateAsync(BuildAction.Build, writer, CreateGlobalProperties(ignoreKinds, targetFramework)); - // Check the output message first, as if other checks would fail, - // the actual output should explain what's going on. - writer.Assert(); + // Check the output message first, as if other checks would fail, + // the actual output should explain what's going on. + writer.Assert(); - Assert.True(isUpToDate); + Assert.True(isUpToDate); - AssertTelemetrySuccessEvent(ignoreKinds); + AssertTelemetrySuccessEvent(ignoreKinds); - await ValidateUpToDateAsync(); - } + await ValidateUpToDateAsync(); + } - private async Task ValidateUpToDateAsync() - { - IBuildUpToDateCheckValidator validator = _buildUpToDateCheck; + private async Task ValidateUpToDateAsync() + { + IBuildUpToDateCheckValidator validator = _buildUpToDateCheck; - (bool isUpToDate, string? failureReason, string? failureDescription) = await validator.ValidateUpToDateAsync(); + (bool isUpToDate, string? failureReason, string? failureDescription) = await validator.ValidateUpToDateAsync(); - Assert.True(isUpToDate); - Assert.Null(failureReason); - Assert.Null(failureDescription); - } + Assert.True(isUpToDate); + Assert.Null(failureReason); + Assert.Null(failureDescription); + } - private async Task ValidateNotUpToDateAsync() - { - IBuildUpToDateCheckValidator validator = _buildUpToDateCheck; + private async Task ValidateNotUpToDateAsync() + { + IBuildUpToDateCheckValidator validator = _buildUpToDateCheck; - (bool isUpToDate, string? failureReason, string? failureDescription) = await validator.ValidateUpToDateAsync(); + (bool isUpToDate, string? failureReason, string? failureDescription) = await validator.ValidateUpToDateAsync(); - Assert.False(isUpToDate); - Assert.NotNull(failureReason); - Assert.NotNull(failureDescription); - } + Assert.False(isUpToDate); + Assert.NotNull(failureReason); + Assert.NotNull(failureDescription); + } - private void AssertTelemetryFailureEvent(string reason, string ignoreKinds) - { - var telemetryEvent = Assert.Single(_telemetryEvents); + private void AssertTelemetryFailureEvent(string reason, string ignoreKinds) + { + var telemetryEvent = Assert.Single(_telemetryEvents); - Assert.Equal(TelemetryEventName.UpToDateCheckFail, telemetryEvent.EventName); - Assert.NotNull(telemetryEvent.Properties); - Assert.Equal(10, telemetryEvent.Properties.Count); + Assert.Equal(TelemetryEventName.UpToDateCheckFail, telemetryEvent.EventName); + Assert.NotNull(telemetryEvent.Properties); + Assert.Equal(10, telemetryEvent.Properties.Count); - var reasonProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.FailReason)); - Assert.Equal(reason, reasonProp.Value); + var reasonProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.FailReason)); + Assert.Equal(reason, reasonProp.Value); - var durationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.DurationMillis)); - var duration = Assert.IsType(durationProp.Value); - Assert.True(duration > 0.0); + var durationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.DurationMillis)); + var duration = Assert.IsType(durationProp.Value); + Assert.True(duration > 0.0); - var waitDurationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.WaitDurationMillis)); - var waitDuration = Assert.IsType(waitDurationProp.Value); - Assert.True(waitDuration > 0.0); + var waitDurationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.WaitDurationMillis)); + var waitDuration = Assert.IsType(waitDurationProp.Value); + Assert.True(waitDuration > 0.0); - var fileCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.FileCount)); - var fileCount = Assert.IsType(fileCountProp.Value); - Assert.True(fileCount >= 0); + var fileCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.FileCount)); + var fileCount = Assert.IsType(fileCountProp.Value); + Assert.True(fileCount >= 0); - var configurationCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.ConfigurationCount)); - var configurationCount = Assert.IsType(configurationCountProp.Value); - Assert.Equal(1, configurationCount); + var configurationCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.ConfigurationCount)); + var configurationCount = Assert.IsType(configurationCountProp.Value); + Assert.Equal(1, configurationCount); - var logLevelProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.LogLevel)); - var logLevel = Assert.IsType(logLevelProp.Value); - Assert.Equal(_logLevel, logLevel); + var logLevelProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.LogLevel)); + var logLevel = Assert.IsType(logLevelProp.Value); + Assert.Equal(_logLevel, logLevel); - var ignoreKindsProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.IgnoreKinds)); - var ignoreKindsStr = Assert.IsType(ignoreKindsProp.Value); - Assert.Equal(ignoreKinds, ignoreKindsStr); + var ignoreKindsProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.IgnoreKinds)); + var ignoreKindsStr = Assert.IsType(ignoreKindsProp.Value); + Assert.Equal(ignoreKinds, ignoreKindsStr); - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.Project)); - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.CheckNumber)); - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.AccelerationResult)); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.Project)); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.CheckNumber)); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.AccelerationResult)); - _telemetryEvents.Clear(); - } + _telemetryEvents.Clear(); + } - private void AssertTelemetrySuccessEvent(string ignoreKinds) - { - var telemetryEvent = Assert.Single(_telemetryEvents); + private void AssertTelemetrySuccessEvent(string ignoreKinds) + { + var telemetryEvent = Assert.Single(_telemetryEvents); + + Assert.Equal(TelemetryEventName.UpToDateCheckSuccess, telemetryEvent.EventName); - Assert.Equal(TelemetryEventName.UpToDateCheckSuccess, telemetryEvent.EventName); + Assert.NotNull(telemetryEvent.Properties); + Assert.Equal(10, telemetryEvent.Properties.Count); - Assert.NotNull(telemetryEvent.Properties); - Assert.Equal(10, telemetryEvent.Properties.Count); + var durationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.DurationMillis)); + var duration = Assert.IsType(durationProp.Value); + Assert.True(duration > 0.0); - var durationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.DurationMillis)); - var duration = Assert.IsType(durationProp.Value); - Assert.True(duration > 0.0); + var waitDurationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.WaitDurationMillis)); + var waitDuration = Assert.IsType(waitDurationProp.Value); + Assert.True(waitDuration > 0.0); - var waitDurationProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.WaitDurationMillis)); - var waitDuration = Assert.IsType(waitDurationProp.Value); - Assert.True(waitDuration > 0.0); + var fileCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.FileCount)); + var fileCount = Assert.IsType(fileCountProp.Value); + Assert.True(fileCount >= 0); - var fileCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.FileCount)); - var fileCount = Assert.IsType(fileCountProp.Value); - Assert.True(fileCount >= 0); + var configurationCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.ConfigurationCount)); + var configurationCount = Assert.IsType(configurationCountProp.Value); + Assert.True(configurationCount == 1); - var configurationCountProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.ConfigurationCount)); - var configurationCount = Assert.IsType(configurationCountProp.Value); - Assert.True(configurationCount == 1); + var logLevelProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.LogLevel)); + var logLevel = Assert.IsType(logLevelProp.Value); + Assert.True(logLevel == _logLevel); - var logLevelProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.LogLevel)); - var logLevel = Assert.IsType(logLevelProp.Value); - Assert.True(logLevel == _logLevel); + var ignoreKindsProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.IgnoreKinds)); + var ignoreKindsStr = Assert.IsType(ignoreKindsProp.Value); + Assert.Equal(ignoreKinds, ignoreKindsStr); - var ignoreKindsProp = Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.IgnoreKinds)); - var ignoreKindsStr = Assert.IsType(ignoreKindsProp.Value); - Assert.Equal(ignoreKinds, ignoreKindsStr); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.Project)); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.CheckNumber)); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.AcceleratedCopyCount)); + Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.AccelerationResult)); + + _telemetryEvents.Clear(); + } - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.Project)); - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.CheckNumber)); - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.AcceleratedCopyCount)); - Assert.Single(telemetryEvent.Properties.Where(p => p.Name == TelemetryPropertyName.UpToDateCheck.AccelerationResult)); + private static ImmutableDictionary CreateGlobalProperties(string ignoreKinds, string targetFramework) + { + var globalProperties = ImmutableDictionary.Empty; - _telemetryEvents.Clear(); + if (ignoreKinds.Length != 0) + { + globalProperties = globalProperties.SetItem(BuildUpToDateCheck.FastUpToDateCheckIgnoresKindsGlobalPropertyName, ignoreKinds); } - private static ImmutableDictionary CreateGlobalProperties(string ignoreKinds, string targetFramework) + if (targetFramework.Length != 0) { - var globalProperties = ImmutableDictionary.Empty; + globalProperties = globalProperties.SetItem(BuildUpToDateCheck.TargetFrameworkGlobalPropertyName, targetFramework); + } - if (ignoreKinds.Length != 0) - { - globalProperties = globalProperties.SetItem(BuildUpToDateCheck.FastUpToDateCheckIgnoresKindsGlobalPropertyName, ignoreKinds); - } + return globalProperties; + } - if (targetFramework.Length != 0) - { - globalProperties = globalProperties.SetItem(BuildUpToDateCheck.TargetFrameworkGlobalPropertyName, targetFramework); - } + private sealed class AssertWriter : TextWriter + { + private readonly StringBuilder _actual = new(); + private readonly ITestOutputHelper _output; + private readonly string _expected; - return globalProperties; + public AssertWriter(ITestOutputHelper output, string expected) + { + _output = output; + _expected = expected; } - private sealed class AssertWriter : TextWriter + public override Encoding Encoding { get; } = Encoding.UTF8; + + public override void WriteLine(string value) { - private readonly StringBuilder _actual = new(); - private readonly ITestOutputHelper _output; - private readonly string _expected; + value = value.Substring("FastUpToDate: ".Length); + value = value.Substring(0, value.Length - $" ({Path.GetFileNameWithoutExtension(_projectPath)})".Length); - public AssertWriter(ITestOutputHelper output, string expected) - { - _output = output; - _expected = expected; - } + _actual.AppendLine(value); + } - public override Encoding Encoding { get; } = Encoding.UTF8; + public void Assert() + { + var actual = _actual.ToString(); - public override void WriteLine(string value) + // Verbose output includes a line "Up-to-date check completed in {N} ms". + // We cannot predict the value of N, so we validate the text is there, and remove it before comparing. + int index = actual.IndexOf("Up-to-date check completed in ", StringComparison.Ordinal); + if (index != -1) { - value = value.Substring("FastUpToDate: ".Length); - value = value.Substring(0, value.Length - $" ({Path.GetFileNameWithoutExtension(_projectPath)})".Length); - - _actual.AppendLine(value); + actual = actual.Substring(0, index).TrimEnd(); } + actual = actual.TrimEnd(); - public void Assert() + if (!string.Equals(_expected, actual)) { - var actual = _actual.ToString(); - - // Verbose output includes a line "Up-to-date check completed in {N} ms". - // We cannot predict the value of N, so we validate the text is there, and remove it before comparing. - int index = actual.IndexOf("Up-to-date check completed in ", StringComparison.Ordinal); - if (index != -1) - { - actual = actual.Substring(0, index).TrimEnd(); - } - actual = actual.TrimEnd(); - - if (!string.Equals(_expected, actual)) - { - _output.WriteLine("Expected:"); - _output.WriteLine(_expected); - _output.WriteLine(""); - _output.WriteLine("Actual:"); - _output.WriteLine(actual); - } - - Xunit.Assert.Equal(_expected, actual); + _output.WriteLine("Expected:"); + _output.WriteLine(_expected); + _output.WriteLine(""); + _output.WriteLine("Actual:"); + _output.WriteLine(actual); } + + Xunit.Assert.Equal(_expected, actual); } + } - private sealed class MockProvider : IBuildUpToDateCheckProvider + private sealed class MockProvider : IBuildUpToDateCheckProvider + { + Task IBuildUpToDateCheckProvider.IsUpToDateAsync(BuildAction buildAction, TextWriter logger, CancellationToken cancellationToken) { - Task IBuildUpToDateCheckProvider.IsUpToDateAsync(BuildAction buildAction, TextWriter logger, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - Task IBuildUpToDateCheckProvider.IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } - #endregion + Task IBuildUpToDateCheckProvider.IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } } + + #endregion } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputTests.cs index e81eb21ee9..67bbd8382e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/UpToDate/UpToDateCheckImplicitConfiguredInputTests.cs @@ -2,58 +2,57 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; -namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate +namespace Microsoft.VisualStudio.ProjectSystem.VS.UpToDate; + +public sealed class UpToDateCheckImplicitConfiguredInputTests : BuildUpToDateCheckTestBase { - public sealed class UpToDateCheckImplicitConfiguredInputTests : BuildUpToDateCheckTestBase + [Fact] + public void Update_InitialItemDataDoesNotUpdateLastItemsChangedAtUtc() { - [Fact] - public void Update_InitialItemDataDoesNotUpdateLastItemsChangedAtUtc() - { - // This test covers a false negative described in https://github.com/dotnet/project-system/issues/5386 - // where the initial snapshot of items sets LastItemsChangedAtUtc, so if a project is up to date when - // it is loaded, then the items are considered changed *after* the last build, but MSBuild's up-to-date - // check will determine the project doesn't require a rebuild and so the output timestamps won't update. - // This previously left the project in a state where it would be considered out of date endlessly. + // This test covers a false negative described in https://github.com/dotnet/project-system/issues/5386 + // where the initial snapshot of items sets LastItemsChangedAtUtc, so if a project is up to date when + // it is loaded, then the items are considered changed *after* the last build, but MSBuild's up-to-date + // check will determine the project doesn't require a rebuild and so the output timestamps won't update. + // This previously left the project in a state where it would be considered out of date endlessly. - var projectSnapshot = new Dictionary() - { - [UpToDateCheckBuilt.SchemaName] = SimpleItems("BuiltOutputPath1") - }; + var projectSnapshot = new Dictionary() + { + [UpToDateCheckBuilt.SchemaName] = SimpleItems("BuiltOutputPath1") + }; - var sourceSnapshot1 = new Dictionary() - { - [Compile.SchemaName] = SimpleItems("ItemPath1") - }; + var sourceSnapshot1 = new Dictionary() + { + [Compile.SchemaName] = SimpleItems("ItemPath1") + }; - var sourceSnapshot2 = new Dictionary() - { - [Compile.SchemaName] = SimpleItems("ItemPath1", "ItemPath2") - }; + var sourceSnapshot2 = new Dictionary() + { + [Compile.SchemaName] = SimpleItems("ItemPath1", "ItemPath2") + }; - var state = UpToDateCheckImplicitConfiguredInput.CreateEmpty(ProjectConfigurationFactory.Create("testConfiguration")); + var state = UpToDateCheckImplicitConfiguredInput.CreateEmpty(ProjectConfigurationFactory.Create("testConfiguration")); - Assert.Null(state.LastItemsChangedAtUtc); + Assert.Null(state.LastItemsChangedAtUtc); - // Initial change does NOT set LastItemsChangedAtUtc - state = UpdateState( - state, - projectSnapshot, - sourceSnapshot1); + // Initial change does NOT set LastItemsChangedAtUtc + state = UpdateState( + state, + projectSnapshot, + sourceSnapshot1); - Assert.Null(state.LastItemsChangedAtUtc); + Assert.Null(state.LastItemsChangedAtUtc); - // Broadcasting an update with no change to items does NOT set LastItemsChangedAtUtc - state = UpdateState(state); + // Broadcasting an update with no change to items does NOT set LastItemsChangedAtUtc + state = UpdateState(state); - Assert.Null(state.LastItemsChangedAtUtc); + Assert.Null(state.LastItemsChangedAtUtc); - // Broadcasting changed items DOES set LastItemsChangedAtUtc - state = UpdateState( - state, - projectSnapshot, - sourceSnapshot2); + // Broadcasting changed items DOES set LastItemsChangedAtUtc + state = UpdateState( + state, + projectSnapshot, + sourceSnapshot2); - Assert.NotNull(state.LastItemsChangedAtUtc); - } + Assert.NotNull(state.LastItemsChangedAtUtc); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/StringExtensions.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/StringExtensions.cs index c32a3adbb6..cb75c4f564 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/StringExtensions.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/StringExtensions.cs @@ -3,28 +3,27 @@ using System.Xml; using Microsoft.Build.Construction; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +public static class StringExtensions { - public static class StringExtensions + public static ProjectRootElement AsProjectRootElement(this string @string) { - public static ProjectRootElement AsProjectRootElement(this string @string) + var stringReader = new StringReader(@string); + var xmlReader = new XmlTextReader(stringReader) { - var stringReader = new StringReader(@string); - var xmlReader = new XmlTextReader(stringReader) - { - DtdProcessing = DtdProcessing.Prohibit - }; - var root = ProjectRootElement.Create(xmlReader); - return root; - } + DtdProcessing = DtdProcessing.Prohibit + }; + var root = ProjectRootElement.Create(xmlReader); + return root; + } - public static string SaveAndGetChanges(this ProjectRootElement root) - { - var tempFile = Path.GetTempFileName(); - root.Save(tempFile); - var result = File.ReadAllText(tempFile); - File.Delete(tempFile); - return result; - } + public static string SaveAndGetChanges(this ProjectRootElement root) + { + var tempFile = Path.GetTempFileName(); + root.Save(tempFile); + var result = File.ReadAllText(tempFile); + File.Delete(tempFile); + return result; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/TestUtil.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/TestUtil.cs index 96012a7333..a8c9772548 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/TestUtil.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Utilities/TestUtil.cs @@ -1,34 +1,33 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities +namespace Microsoft.VisualStudio.ProjectSystem.VS.Utilities; + +internal static class TestUtil { - internal static class TestUtil + /// + /// Asynchronously runs the specified on an STA thread. + /// + public static Task RunStaTestAsync(Action action) { - /// - /// Asynchronously runs the specified on an STA thread. - /// - public static Task RunStaTestAsync(Action action) - { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); - var thread = new Thread(ThreadMethod); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); + var thread = new Thread(ThreadMethod); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); - return tcs.Task; + return tcs.Task; - void ThreadMethod() + void ThreadMethod() + { + try { - try - { - action(); + action(); - tcs.SetResult(); - } - catch (Exception e) - { - tcs.SetException(e); - } + tcs.SetResult(); + } + catch (Exception e) + { + tcs.SetException(e); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProviderTests.cs index 0bdaa9c295..1b94d52f1e 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectCompatibilityProviderTests.cs @@ -2,37 +2,36 @@ using Microsoft.Build.Construction; -namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic; + +public class VisualBasicProjectCompatibilityProviderTests { - public class VisualBasicProjectCompatibilityProviderTests + [Fact] + public async Task IsProjectCompatibleAsync_ReturnsTrue() { - [Fact] - public async Task IsProjectCompatibleAsync_ReturnsTrue() - { - var provider = CreateInstance(); + var provider = CreateInstance(); - var element = ProjectRootElement.Create(); + var element = ProjectRootElement.Create(); - var result = await provider.IsProjectCompatibleAsync(element); + var result = await provider.IsProjectCompatibleAsync(element); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task IsProjectNeedBeUpgradedAsync_ReturnsFalse() - { - var provider = CreateInstance(); + [Fact] + public async Task IsProjectNeedBeUpgradedAsync_ReturnsFalse() + { + var provider = CreateInstance(); - var element = ProjectRootElement.Create(); + var element = ProjectRootElement.Create(); - var result = await provider.IsProjectNeedBeUpgradedAsync(element); + var result = await provider.IsProjectNeedBeUpgradedAsync(element); - Assert.False(result); - } + Assert.False(result); + } - private static VisualBasicProjectCompatibilityProvider CreateInstance() - { - return new VisualBasicProjectCompatibilityProvider(); - } + private static VisualBasicProjectCompatibilityProvider CreateInstance() + { + return new VisualBasicProjectCompatibilityProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProviderTests.cs index d31f3cd611..e271bd18ac 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VisualBasic/VisualBasicProjectTypeGuidProviderTests.cs @@ -1,22 +1,21 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic +namespace Microsoft.VisualStudio.ProjectSystem.VS.VisualBasic; + +public class VisualBasicProjectTypeGuidProviderTests { - public class VisualBasicProjectTypeGuidProviderTests + [Fact] + public void ProjectTypeGuid_ReturnsNonEmptyGuid() { - [Fact] - public void ProjectTypeGuid_ReturnsNonEmptyGuid() - { - var provider = CreateInstance(); + var provider = CreateInstance(); - // Handshake between the project system and factory around the actual guid value so we do not test - // for a specified guid, other than to confirm it's not empty - Assert.NotEqual(Guid.Empty, provider.ProjectTypeGuid); - } + // Handshake between the project system and factory around the actual guid value so we do not test + // for a specified guid, other than to confirm it's not empty + Assert.NotEqual(Guid.Empty, provider.ProjectTypeGuid); + } - private static VisualBasicProjectTypeGuidProvider CreateInstance() - { - return new VisualBasicProjectTypeGuidProvider(); - } + private static VisualBasicProjectTypeGuidProvider CreateInstance() + { + return new VisualBasicProjectTypeGuidProvider(); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VsUIServiceTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VsUIServiceTests.cs index e397334a68..b30de14cb0 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VsUIServiceTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/VsUIServiceTests.cs @@ -6,106 +6,105 @@ using System.Runtime.InteropServices; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS +namespace Microsoft.VisualStudio.ProjectSystem.VS; + +public class VsUIServiceTests { - public class VsUIServiceTests + [Fact] + public void Constructor_NullAsServiceProvider_ThrowsArgumentNull() { - [Fact] - public void Constructor_NullAsServiceProvider_ThrowsArgumentNull() + var joinableTaskContext = new JoinableTaskContext(); + + Assert.Throws("serviceProvider", () => { - var joinableTaskContext = new JoinableTaskContext(); + return new VsUIService(null!, joinableTaskContext); + }); + } - Assert.Throws("serviceProvider", () => - { - return new VsUIService(null!, joinableTaskContext); - }); - } + [Fact] + public void Constructor_NullAsThreadingService_ThrowsArgumentNull() + { + var serviceProvider = SVsServiceProviderFactory.Create(); - [Fact] - public void Constructor_NullAsThreadingService_ThrowsArgumentNull() + Assert.Throws("joinableTaskContext", () => { - var serviceProvider = SVsServiceProviderFactory.Create(); + return new VsUIService(serviceProvider, null!); + }); + } - Assert.Throws("joinableTaskContext", () => - { - return new VsUIService(serviceProvider, null!); - }); - } + [Fact] + public async Task Value_MustBeCalledOnUIThread() + { + var service = CreateInstance(); - [Fact] - public async Task Value_MustBeCalledOnUIThread() + var exception = await Assert.ThrowsAsync(() => { - var service = CreateInstance(); - - var exception = await Assert.ThrowsAsync(() => + return Task.Run(() => { - return Task.Run(() => - { - _ = service.Value; - }); + _ = service.Value; }); + }); - Assert.Equal(VSConstants.RPC_E_WRONG_THREAD, exception.HResult); - } + Assert.Equal(VSConstants.RPC_E_WRONG_THREAD, exception.HResult); + } - [Fact] - public void Value_WhenMissingService_ReturnsNull() - { - var serviceProvider = IServiceProviderFactory.ImplementGetService(type => null); + [Fact] + public void Value_WhenMissingService_ReturnsNull() + { + var serviceProvider = IServiceProviderFactory.ImplementGetService(type => null); - var service = CreateInstance(serviceProvider: serviceProvider); + var service = CreateInstance(serviceProvider: serviceProvider); - var result = service.Value; + var result = service.Value; - Assert.Null(result); - } + Assert.Null(result); + } - [Fact] - public void Value_ReturnsGetService() - { - object input = new object(); + [Fact] + public void Value_ReturnsGetService() + { + object input = new object(); - var serviceProvider = IServiceProviderFactory.ImplementGetService(type => - { - if (type == typeof(string)) - return input; + var serviceProvider = IServiceProviderFactory.ImplementGetService(type => + { + if (type == typeof(string)) + return input; - return null; - }); + return null; + }); - var service = CreateInstance(serviceProvider: serviceProvider); + var service = CreateInstance(serviceProvider: serviceProvider); - var result = service.Value; + var result = service.Value; - Assert.Same(input, result); - } + Assert.Same(input, result); + } - [Fact] - public void Value_CachesResult() + [Fact] + public void Value_CachesResult() + { + var serviceProvider = IServiceProviderFactory.ImplementGetService(type => { - var serviceProvider = IServiceProviderFactory.ImplementGetService(type => - { - return new object(); - }); + return new object(); + }); - var service = CreateInstance(serviceProvider: serviceProvider); + var service = CreateInstance(serviceProvider: serviceProvider); - var result1 = service.Value; - var result2 = service.Value; + var result1 = service.Value; + var result2 = service.Value; - Assert.Same(result1, result2); - } + Assert.Same(result1, result2); + } - private static VsUIService CreateInstance( - IServiceProvider? serviceProvider = null, - JoinableTaskContext? joinableTaskContext = null) - where TService : class - where TInterface : class - { - serviceProvider ??= SVsServiceProviderFactory.Create(); - joinableTaskContext ??= new JoinableTaskContext(); + private static VsUIService CreateInstance( + IServiceProvider? serviceProvider = null, + JoinableTaskContext? joinableTaskContext = null) + where TService : class + where TInterface : class + { + serviceProvider ??= SVsServiceProviderFactory.Create(); + joinableTaskContext ??= new JoinableTaskContext(); - return new VsUIService(serviceProvider, joinableTaskContext); - } + return new VsUIService(serviceProvider, joinableTaskContext); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitContextTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitContextTests.cs index 6aedbfe35f..e749596252 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitContextTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitContextTests.cs @@ -2,86 +2,85 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting +namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting; + +public static class VisualStudioWaitContextTests { - public static class VisualStudioWaitContextTests + [Fact] + public static void CancellationToken() { - [Fact] - public static void CancellationToken() - { - var cancellable = Create("Title", "Message", allowCancel: true); + var cancellable = Create("Title", "Message", allowCancel: true); - Assert.True(cancellable.CancellationToken.CanBeCanceled); + Assert.True(cancellable.CancellationToken.CanBeCanceled); - var nonCancellable = Create("Title", "Message", allowCancel: false); + var nonCancellable = Create("Title", "Message", allowCancel: false); - Assert.False(nonCancellable.CancellationToken.CanBeCanceled); - } + Assert.False(nonCancellable.CancellationToken.CanBeCanceled); + } - [Fact] - public static void CreateWrongType_Test() - { - Assert.ThrowsAny(() => _ = CreateWrongType(string.Empty, string.Empty, false)); - } + [Fact] + public static void CreateWrongType_Test() + { + Assert.ThrowsAny(() => _ = CreateWrongType(string.Empty, string.Empty, false)); + } - private delegate void CreateInstanceCallback(out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog); + private delegate void CreateInstanceCallback(out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog); - private static VisualStudioWaitContext Create(string title, string message, bool allowCancel) - { - var threadedWaitDialogFactoryMock = new Mock(); - var threadedWaitDialogMock = new Mock(); - threadedWaitDialogMock.Setup(m => m.StartWaitDialogWithCallback( - It.IsNotNull(), - It.IsNotNull(), - It.Is(s => s == null), - It.Is(s => s == null), - It.Is(s => s == null), - It.IsAny(), - It.IsInRange(0, int.MaxValue, Moq.Range.Inclusive), - It.Is(v => !v), - It.Is(i => i == 0), - It.Is(i => i == 0), - It.IsNotNull())) - .Callback((string szWaitCaption, - string szWaitMessage, - string szProgressText, - object varStatusBmpAnim, - string szStatusBarText, - bool fIsCancelable, - int iDelayToShowDialog, - bool fShowProgress, - int iTotalSteps, - int iCurrentStep, - IVsThreadedWaitDialogCallback pCallback) => - { - Assert.Equal(title, szWaitCaption); - Assert.Equal(message, szWaitMessage); - Assert.Equal(allowCancel, fIsCancelable); - }); - threadedWaitDialogMock.Setup(m => m.EndWaitDialog(out It.Ref.IsAny)); - var threadedWaitDialog = threadedWaitDialogMock.Object; + private static VisualStudioWaitContext Create(string title, string message, bool allowCancel) + { + var threadedWaitDialogFactoryMock = new Mock(); + var threadedWaitDialogMock = new Mock(); + threadedWaitDialogMock.Setup(m => m.StartWaitDialogWithCallback( + It.IsNotNull(), + It.IsNotNull(), + It.Is(s => s == null), + It.Is(s => s == null), + It.Is(s => s == null), + It.IsAny(), + It.IsInRange(0, int.MaxValue, Moq.Range.Inclusive), + It.Is(v => !v), + It.Is(i => i == 0), + It.Is(i => i == 0), + It.IsNotNull())) + .Callback((string szWaitCaption, + string szWaitMessage, + string szProgressText, + object varStatusBmpAnim, + string szStatusBarText, + bool fIsCancelable, + int iDelayToShowDialog, + bool fShowProgress, + int iTotalSteps, + int iCurrentStep, + IVsThreadedWaitDialogCallback pCallback) => + { + Assert.Equal(title, szWaitCaption); + Assert.Equal(message, szWaitMessage); + Assert.Equal(allowCancel, fIsCancelable); + }); + threadedWaitDialogMock.Setup(m => m.EndWaitDialog(out It.Ref.IsAny)); + var threadedWaitDialog = threadedWaitDialogMock.Object; - threadedWaitDialogFactoryMock - .Setup(m => m.CreateInstance(out It.Ref.IsAny)) - .Callback(new CreateInstanceCallback((out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog) => - { - ppIVsThreadedWaitDialog = threadedWaitDialog; - })) - .Returns(HResult.OK); - return new VisualStudioWaitContext(threadedWaitDialogFactoryMock.Object, title, message, allowCancel); - } + threadedWaitDialogFactoryMock + .Setup(m => m.CreateInstance(out It.Ref.IsAny)) + .Callback(new CreateInstanceCallback((out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog) => + { + ppIVsThreadedWaitDialog = threadedWaitDialog; + })) + .Returns(HResult.OK); + return new VisualStudioWaitContext(threadedWaitDialogFactoryMock.Object, title, message, allowCancel); + } - private static VisualStudioWaitContext CreateWrongType(string title, string message, bool allowCancel) - { - var threadedWaitDialogFactoryMock = new Mock(); - threadedWaitDialogFactoryMock - .Setup(m => m.CreateInstance(out It.Ref.IsAny)) - .Callback(new CreateInstanceCallback((out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog) => - { - ppIVsThreadedWaitDialog = null!; - })) - .Returns(HResult.OK); - return new VisualStudioWaitContext(threadedWaitDialogFactoryMock.Object, title, message, allowCancel); - } + private static VisualStudioWaitContext CreateWrongType(string title, string message, bool allowCancel) + { + var threadedWaitDialogFactoryMock = new Mock(); + threadedWaitDialogFactoryMock + .Setup(m => m.CreateInstance(out It.Ref.IsAny)) + .Callback(new CreateInstanceCallback((out IVsThreadedWaitDialog2 ppIVsThreadedWaitDialog) => + { + ppIVsThreadedWaitDialog = null!; + })) + .Returns(HResult.OK); + return new VisualStudioWaitContext(threadedWaitDialogFactoryMock.Object, title, message, allowCancel); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitIndicatorTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitIndicatorTests.cs index 48a918a9d8..df0ed25443 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitIndicatorTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Waiting/VisualStudioWaitIndicatorTests.cs @@ -5,95 +5,94 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting +namespace Microsoft.VisualStudio.ProjectSystem.VS.Waiting; + +public class VisualStudioWaitIndicatorTests { - public class VisualStudioWaitIndicatorTests + [Fact] + public async Task Run_WhenAsyncMethodThrows_Throws() { - [Fact] - public async Task Run_WhenAsyncMethodThrows_Throws() - { - var (instance, _) = CreateInstance(); + var (instance, _) = CreateInstance(); - await Assert.ThrowsAsync( - () => instance.RunAsync("", "", false, _ => throw new Exception())); - } + await Assert.ThrowsAsync( + () => instance.RunAsync("", "", false, _ => throw new Exception())); + } - [Fact] - public async Task Run_WhenAsyncMethodThrowsWrapped_Throws() - { - var (instance, _) = CreateInstance(); - - await Assert.ThrowsAsync( - () => instance.RunAsync("", "", false, _ => Task.FromException(new Exception()))); - } - - [Fact] - public async Task Run_WhenAsyncMethodThrowsOperationCanceled_SetsIsCancelledToTrue() - { - var (instance, _) = CreateInstance(); + [Fact] + public async Task Run_WhenAsyncMethodThrowsWrapped_Throws() + { + var (instance, _) = CreateInstance(); + + await Assert.ThrowsAsync( + () => instance.RunAsync("", "", false, _ => Task.FromException(new Exception()))); + } + + [Fact] + public async Task Run_WhenAsyncMethodThrowsOperationCanceled_SetsIsCancelledToTrue() + { + var (instance, _) = CreateInstance(); + + var result = await instance.RunAsync("", "", false, _ => Task.FromException(new OperationCanceledException())); - var result = await instance.RunAsync("", "", false, _ => Task.FromException(new OperationCanceledException())); + Assert.True(result.IsCancelled); + } - Assert.True(result.IsCancelled); - } + [Fact] + public async Task Run_WhenAsyncMethodThrowsAggregateContainedOperationCanceled_SetsIsCancelledToTrue() + { + var (instance, _) = CreateInstance(); - [Fact] - public async Task Run_WhenAsyncMethodThrowsAggregateContainedOperationCanceled_SetsIsCancelledToTrue() + var result = await instance.RunAsync("", "", false, async _ => { - var (instance, _) = CreateInstance(); + await Task.WhenAll( + Task.Run(() => throw new OperationCanceledException()), + Task.Run(() => throw new OperationCanceledException())); - var result = await instance.RunAsync("", "", false, async _ => - { - await Task.WhenAll( - Task.Run(() => throw new OperationCanceledException()), - Task.Run(() => throw new OperationCanceledException())); + return ""; + }); - return ""; - }); + Assert.True(result.IsCancelled); + } - Assert.True(result.IsCancelled); - } + [Fact] + public async Task Run_WhenUserCancels_CancellationTokenIsCancelled() + { + var (instance, userCancel) = CreateInstance(isCancelable: true); - [Fact] - public async Task Run_WhenUserCancels_CancellationTokenIsCancelled() + CancellationToken? result = default; + await instance.RunAsync("", "", true, context => { - var (instance, userCancel) = CreateInstance(isCancelable: true); + userCancel(); - CancellationToken? result = default; - await instance.RunAsync("", "", true, context => - { - userCancel(); + result = context.CancellationToken; - result = context.CancellationToken; + return TaskResult.EmptyString; + }); - return TaskResult.EmptyString; - }); + Assert.NotNull(result); + Assert.True(result.Value.IsCancellationRequested); + } - Assert.NotNull(result); - Assert.True(result.Value.IsCancellationRequested); - } + [Fact] + public async Task Run_ReturnsResultOfAsyncMethod() + { + var (instance, _) = CreateInstance(); - [Fact] - public async Task Run_ReturnsResultOfAsyncMethod() + var result = await instance.RunAsync("", "", false, _ => { - var (instance, _) = CreateInstance(); - - var result = await instance.RunAsync("", "", false, _ => - { - return Task.FromResult("Hello"); - }); + return Task.FromResult("Hello"); + }); - Assert.Equal("Hello", result.Result); - } + Assert.Equal("Hello", result.Result); + } - private static (VisualStudioWaitIndicator, Action cancel) CreateInstance(string title = "", string message = "", bool isCancelable = false) - { - var joinableTaskContext = new JoinableTaskContext(); - var (threadedWaitDialogFactory, cancel) = IVsThreadedWaitDialogFactoryFactory.Create(title, message, isCancelable); - var threadedWaitDialogFactoryService = IVsUIServiceFactory.Create(threadedWaitDialogFactory); + private static (VisualStudioWaitIndicator, Action cancel) CreateInstance(string title = "", string message = "", bool isCancelable = false) + { + var joinableTaskContext = new JoinableTaskContext(); + var (threadedWaitDialogFactory, cancel) = IVsThreadedWaitDialogFactoryFactory.Create(title, message, isCancelable); + var threadedWaitDialogFactoryService = IVsUIServiceFactory.Create(threadedWaitDialogFactory); - var instance = new VisualStudioWaitIndicator(joinableTaskContext, threadedWaitDialogFactoryService); - return (instance, cancel); - } + var instance = new VisualStudioWaitIndicator(joinableTaskContext, threadedWaitDialogFactoryService); + return (instance, cancel); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilterTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilterTests.cs index ee3ff4669b..059a96dff6 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilterTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsAddItemFilterTests.cs @@ -1,57 +1,56 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms +namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; + +public class WindowsFormsAddItemFilterTests { - public class WindowsFormsAddItemFilterTests + [Theory] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Non-Windows Forms", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\General", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Extensions\dh64yf.343\ProjectTemplates\CSharp", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Windows Forms", true })] + public void FiltersItemTemplateFolders_NonWindowsForms(string templateDir, bool shouldBeFiltered) { - [Theory] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Non-Windows Forms", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\General", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Extensions\dh64yf.343\ProjectTemplates\CSharp", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Windows Forms", true })] - public void FiltersItemTemplateFolders_NonWindowsForms(string templateDir, bool shouldBeFiltered) - { - var filter = new WindowsFormsAddItemFilter(UnconfiguredProjectFactory.Create(scope: IProjectCapabilitiesScopeFactory.Create())); - - Guid guid = Guid.Empty; - var result = filter.FilterTreeItemByTemplateDir(ref guid, templateDir, out int filterResult); - - Assert.Equal(0, result); - Assert.Equal(shouldBeFiltered, filterResult == 1); - } - - [Theory] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Non-Windows Forms", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\General", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Extensions\dh64yf.343\ProjectTemplates\CSharp", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Windows Forms", false })] - public void FiltersItemTemplateFolders_WindowsForms(string templateDir, bool shouldBeFiltered) - { - var filter = new WindowsFormsAddItemFilter(UnconfiguredProjectFactory.Create(scope: IProjectCapabilitiesScopeFactory.Create(new string[] { "WindowsForms" }))); - - Guid guid = Guid.Empty; - var result = filter.FilterTreeItemByTemplateDir(ref guid, templateDir, out int filterResult); - - Assert.Equal(0, result); - Assert.Equal(shouldBeFiltered, filterResult == 1); - } - - [Theory] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\CSControlInheritanceWizard.vsz", true })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\CSFormInheritanceWizard.vsz", true })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\InheritedForm.vsz", true })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\InheritedControl.vsz", true })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Class.vsz", false })] - [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\ControlLibrary.vsz", false })] - public void FiltersItemTemplateFiles(string templateDir, bool shouldBeFiltered) - { - var filter = new WindowsFormsAddItemFilter(UnconfiguredProjectFactory.Create()); - - Guid guid = Guid.Empty; - var result = filter.FilterListItemByTemplateFile(ref guid, templateDir, out int filterResult); - - Assert.Equal(0, result); - Assert.Equal(shouldBeFiltered, filterResult == 1); - } + var filter = new WindowsFormsAddItemFilter(UnconfiguredProjectFactory.Create(scope: IProjectCapabilitiesScopeFactory.Create())); + + Guid guid = Guid.Empty; + var result = filter.FilterTreeItemByTemplateDir(ref guid, templateDir, out int filterResult); + + Assert.Equal(0, result); + Assert.Equal(shouldBeFiltered, filterResult == 1); + } + + [Theory] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Non-Windows Forms", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\General", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Extensions\dh64yf.343\ProjectTemplates\CSharp", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Windows Forms", false })] + public void FiltersItemTemplateFolders_WindowsForms(string templateDir, bool shouldBeFiltered) + { + var filter = new WindowsFormsAddItemFilter(UnconfiguredProjectFactory.Create(scope: IProjectCapabilitiesScopeFactory.Create(new string[] { "WindowsForms" }))); + + Guid guid = Guid.Empty; + var result = filter.FilterTreeItemByTemplateDir(ref guid, templateDir, out int filterResult); + + Assert.Equal(0, result); + Assert.Equal(shouldBeFiltered, filterResult == 1); + } + + [Theory] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\CSControlInheritanceWizard.vsz", true })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\CSFormInheritanceWizard.vsz", true })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\InheritedForm.vsz", true })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\InheritedControl.vsz", true })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\Class.vsz", false })] + [InlineData(new object[] { @"C:\Program Files\Visual Studio\Templates\ControlLibrary.vsz", false })] + public void FiltersItemTemplateFiles(string templateDir, bool shouldBeFiltered) + { + var filter = new WindowsFormsAddItemFilter(UnconfiguredProjectFactory.Create()); + + Guid guid = Guid.Empty; + var result = filter.FilterListItemByTemplateFile(ref guid, templateDir, out int filterResult); + + Assert.Equal(0, result); + Assert.Equal(shouldBeFiltered, filterResult == 1); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProviderTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProviderTests.cs index 34a0781425..177e9075ed 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProviderTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/WindowsForms/WindowsFormsEditorProviderTests.cs @@ -3,318 +3,317 @@ using Microsoft.VisualStudio.ProjectSystem.Properties; using Moq.Protected; -namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms +namespace Microsoft.VisualStudio.ProjectSystem.VS.WindowsForms; + +public class WindowsFormsEditorProviderTests { - public class WindowsFormsEditorProviderTests + [Fact] + public async Task GetSpecificEditorAsync_NullAsDocumentMoniker_ThrowsArgumentNull() { - [Fact] - public async Task GetSpecificEditorAsync_NullAsDocumentMoniker_ThrowsArgumentNull() + var provider = CreateInstance(); + + await Assert.ThrowsAsync("documentMoniker", () => { - var provider = CreateInstance(); + return provider.GetSpecificEditorAsync(null!); + }); + } - await Assert.ThrowsAsync("documentMoniker", () => - { - return provider.GetSpecificEditorAsync(null!); - }); - } + [Fact] + public async Task SetUseGlobalEditorAsync_NullAsDocumentMoniker_ThrowsArgumentNull() + { + var provider = CreateInstance(); - [Fact] - public async Task SetUseGlobalEditorAsync_NullAsDocumentMoniker_ThrowsArgumentNull() + await Assert.ThrowsAsync("documentMoniker", () => { - var provider = CreateInstance(); + return provider.SetUseGlobalEditorAsync(null!, false); + }); + } - await Assert.ThrowsAsync("documentMoniker", () => - { - return provider.SetUseGlobalEditorAsync(null!, false); - }); - } + [Fact] + public async Task GetSpecificEditorAsync_EmptyAsDocumentMoniker_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public async Task GetSpecificEditorAsync_EmptyAsDocumentMoniker_ThrowsArgument() + await Assert.ThrowsAsync("documentMoniker", () => { - var provider = CreateInstance(); + return provider.GetSpecificEditorAsync(string.Empty); + }); + } - await Assert.ThrowsAsync("documentMoniker", () => - { - return provider.GetSpecificEditorAsync(string.Empty); - }); - } + [Fact] + public async Task SetUseGlobalEditorAsync_EmptyAsDocumentMoniker_ThrowsArgument() + { + var provider = CreateInstance(); - [Fact] - public async Task SetUseGlobalEditorAsync_EmptyAsDocumentMoniker_ThrowsArgument() + await Assert.ThrowsAsync("documentMoniker", () => { - var provider = CreateInstance(); + return provider.SetUseGlobalEditorAsync(string.Empty, false); + }); + } - await Assert.ThrowsAsync("documentMoniker", () => - { - return provider.SetUseGlobalEditorAsync(string.Empty, false); - }); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenNoProjectSpecificEditorProviders_ReturnsNull() + { + var provider = CreateInstance(); - [Fact] - public async Task GetSpecificEditorAsync_WhenNoProjectSpecificEditorProviders_ReturnsNull() - { - var provider = CreateInstance(); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenNoDefaultProjectSpecificEditorProviders_ReturnsNull() + { + var editorProvider = IProjectSpecificEditorProviderFactory.ImplementGetSpecificEditorAsync(); + var provider = CreateInstance(); - [Fact] - public async Task GetSpecificEditorAsync_WhenNoDefaultProjectSpecificEditorProviders_ReturnsNull() - { - var editorProvider = IProjectSpecificEditorProviderFactory.ImplementGetSpecificEditorAsync(); - var provider = CreateInstance(); + provider.ProjectSpecificEditorProviders.Add("NotDefault", editorProvider); - provider.ProjectSpecificEditorProviders.Add("NotDefault", editorProvider); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenFileNotInProject_ReturnsNull() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + """); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - [Fact] - public async Task GetSpecificEditorAsync_WhenFileNotInProject_ReturnsNull() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - """); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task SetUseGlobalEditorAsync_WhenFileNotInProject_ReturnsFalse() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + """); + var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", true); - [Fact] - public async Task SetUseGlobalEditorAsync_WhenFileNotInProject_ReturnsFalse() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - """); - var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", true); + Assert.False(result); + } - Assert.False(result); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenFileNotCompileItem_ReturnsNull() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs, FilePath: "C:\Foo.cs", ItemType: None + """); - [Fact] - public async Task GetSpecificEditorAsync_WhenFileNotCompileItem_ReturnsNull() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs, FilePath: "C:\Foo.cs", ItemType: None - """); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task SetUseGlobalEditorAsync_WhenFileNotCompileItem_ReturnsFalse() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs, FilePath: "C:\Foo.cs", ItemType: None + """); - [Fact] - public async Task SetUseGlobalEditorAsync_WhenFileNotCompileItem_ReturnsFalse() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs, FilePath: "C:\Foo.cs", ItemType: None - """); + var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", false); - var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", false); + Assert.False(result); + } - Assert.False(result); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenCompileItemWithNoSubType_ReturnsNull() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile + """); - [Fact] - public async Task GetSpecificEditorAsync_WhenCompileItemWithNoSubType_ReturnsNull() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile - """); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task SetUseGlobalEditorAsync_WhenCompileItemWithNoSubType_ReturnsFalse() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile + """); - [Fact] - public async Task SetUseGlobalEditorAsync_WhenCompileItemWithNoSubType_ReturnsFalse() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile - """); + var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", false); - var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", false); + Assert.False(result); + } - Assert.False(result); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenCompileItemWithUnrecognizedSubType_ReturnsNull() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Code + """); - [Fact] - public async Task GetSpecificEditorAsync_WhenCompileItemWithUnrecognizedSubType_ReturnsNull() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Code - """); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task SetUseGlobalEditorAsync_WhenCompileItemWithUnrecognizedSubType_ReturnsFalse() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Code + """); - [Fact] - public async Task SetUseGlobalEditorAsync_WhenCompileItemWithUnrecognizedSubType_ReturnsFalse() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Code - """); + var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", false); - var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", false); + Assert.False(result); + } - Assert.False(result); - } + [Fact] + public async Task GetSpecificEditorAsync_WhenParentIsSourceFile_ReturnsNull() + { // Let's folks double-click the designer file to open it as text + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs (flags: {SourceFile}) + Foo.Designer.cs, FilePath: "C:\Foo.Designer.cs", ItemType: Compile, SubType: Designer - [Fact] - public async Task GetSpecificEditorAsync_WhenParentIsSourceFile_ReturnsNull() - { // Let's folks double-click the designer file to open it as text - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs (flags: {SourceFile}) - Foo.Designer.cs, FilePath: "C:\Foo.Designer.cs", ItemType: Compile, SubType: Designer + """); - """); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.Designer.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.Designer.cs"); + Assert.Null(result); + } - Assert.Null(result); - } + [Fact] + public async Task SetUseGlobalEditorAsync_WhenParentIsSourceFile_ReturnsFalse() + { + var provider = CreateInstanceWithDefaultEditorProvider( + """ + Project + Foo.cs (flags: {SourceFile}) + Foo.Designer.cs, FilePath: "C:\Foo.Designer.cs", ItemType: Compile, SubType: Designer - [Fact] - public async Task SetUseGlobalEditorAsync_WhenParentIsSourceFile_ReturnsFalse() - { - var provider = CreateInstanceWithDefaultEditorProvider( - """ - Project - Foo.cs (flags: {SourceFile}) - Foo.Designer.cs, FilePath: "C:\Foo.Designer.cs", ItemType: Compile, SubType: Designer + """); - """); + var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.Designer.cs", false); - var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.Designer.cs", false); + Assert.False(result); + } - Assert.False(result); - } + [Theory] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Form + """, true)] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Designer + """, true)] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: UserControl + """, true)] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Component + """, false)] + public async Task GetSpecificEditorAsync_WhenMarkedWithRecognizedSubType_ReturnsResult(string tree, bool useDesignerByDefault) + { + var defaultEditorFactory = Guid.NewGuid(); - [Theory] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Form - """, true)] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Designer - """, true)] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: UserControl - """, true)] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Component - """, false)] - public async Task GetSpecificEditorAsync_WhenMarkedWithRecognizedSubType_ReturnsResult(string tree, bool useDesignerByDefault) - { - var defaultEditorFactory = Guid.NewGuid(); + var options = IProjectSystemOptionsFactory.ImplementGetUseDesignerByDefaultAsync((_, defaultValue, _) => defaultValue); + var provider = CreateInstanceWithDefaultEditorProvider(tree, options, defaultEditorFactory); - var options = IProjectSystemOptionsFactory.ImplementGetUseDesignerByDefaultAsync((_, defaultValue, _) => defaultValue); - var provider = CreateInstanceWithDefaultEditorProvider(tree, options, defaultEditorFactory); + var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); - var result = await provider.GetSpecificEditorAsync(@"C:\Foo.cs"); + Assert.NotNull(result); + Assert.NotEmpty(result.DisplayName); + Assert.Equal(VSConstants.LOGVIEWID.Designer_guid, result.DefaultView); + Assert.Equal(useDesignerByDefault, result.IsDefaultEditor); + Assert.Equal(defaultEditorFactory, result.EditorFactory); + } - Assert.NotNull(result); - Assert.NotEmpty(result.DisplayName); - Assert.Equal(VSConstants.LOGVIEWID.Designer_guid, result.DefaultView); - Assert.Equal(useDesignerByDefault, result.IsDefaultEditor); - Assert.Equal(defaultEditorFactory, result.EditorFactory); - } + [Theory] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Form + """, + "Form")] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Designer + """, + "Designer")] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: UserControl + """, + "UserControl")] + [InlineData( + """ + Project + Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Component + """, + "Component")] + public async Task SetUseGlobalEditorAsync_WhenMarkedWithRecognizedSubType_ReturnsTrue(string tree, string expectedCategory) + { + string? categoryResult = null; + bool? valueResult = null; + var options = IProjectSystemOptionsFactory.ImplementSetUseDesignerByDefaultAsync((category, value, _) => { categoryResult = category; valueResult = value; return Task.CompletedTask; }); + var provider = CreateInstanceWithDefaultEditorProvider(tree, options); - [Theory] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Form - """, - "Form")] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Designer - """, - "Designer")] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: UserControl - """, - "UserControl")] - [InlineData( - """ - Project - Foo.txt, FilePath: "C:\Foo.cs", ItemType: Compile, SubType: Component - """, - "Component")] - public async Task SetUseGlobalEditorAsync_WhenMarkedWithRecognizedSubType_ReturnsTrue(string tree, string expectedCategory) - { - string? categoryResult = null; - bool? valueResult = null; - var options = IProjectSystemOptionsFactory.ImplementSetUseDesignerByDefaultAsync((category, value, _) => { categoryResult = category; valueResult = value; return Task.CompletedTask; }); - var provider = CreateInstanceWithDefaultEditorProvider(tree, options); + var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", useGlobalEditor: true); - var result = await provider.SetUseGlobalEditorAsync(@"C:\Foo.cs", useGlobalEditor: true); + Assert.True(result); + Assert.False(valueResult); + Assert.Equal(expectedCategory, categoryResult); + } - Assert.True(result); - Assert.False(valueResult); - Assert.Equal(expectedCategory, categoryResult); - } + private static WindowsFormsEditorProvider CreateInstanceWithDefaultEditorProvider(string projectTree, IProjectSystemOptions? options = null, Guid defaultEditorFactory = default) + { + var tree = ProjectTreeParser.Parse(projectTree); - private static WindowsFormsEditorProvider CreateInstanceWithDefaultEditorProvider(string projectTree, IProjectSystemOptions? options = null, Guid defaultEditorFactory = default) - { - var tree = ProjectTreeParser.Parse(projectTree); + var defaultEditorProvider = IProjectSpecificEditorProviderFactory.ImplementGetSpecificEditorAsync(defaultEditorFactory); - var defaultEditorProvider = IProjectSpecificEditorProviderFactory.ImplementGetSpecificEditorAsync(defaultEditorFactory); + var provider = CreateInstance(projectTree: IPhysicalProjectTreeFactory.Create(currentTree: tree), options: options); + provider.ProjectSpecificEditorProviders.Add("Default", defaultEditorProvider); - var provider = CreateInstance(projectTree: IPhysicalProjectTreeFactory.Create(currentTree: tree), options: options); - provider.ProjectSpecificEditorProviders.Add("Default", defaultEditorProvider); + return provider; + } - return provider; - } + private static WindowsFormsEditorProvider CreateInstance(UnconfiguredProject? unconfiguredProject = null, IPhysicalProjectTree? projectTree = null, IProjectSystemOptions? options = null) + { + var project = ConfiguredProjectFactory.Create(); + unconfiguredProject ??= UnconfiguredProjectFactory.Create(configuredProject: project); + projectTree ??= IPhysicalProjectTreeFactory.Create(); + options ??= IProjectSystemOptionsFactory.Create(); + var asyncTasks = IProjectAsynchronousTasksServiceFactory.Create(); - private static WindowsFormsEditorProvider CreateInstance(UnconfiguredProject? unconfiguredProject = null, IPhysicalProjectTree? projectTree = null, IProjectSystemOptions? options = null) - { - var project = ConfiguredProjectFactory.Create(); - unconfiguredProject ??= UnconfiguredProjectFactory.Create(configuredProject: project); - projectTree ??= IPhysicalProjectTreeFactory.Create(); - options ??= IProjectSystemOptionsFactory.Create(); - var asyncTasks = IProjectAsynchronousTasksServiceFactory.Create(); - - var provider = new Mock(unconfiguredProject, asyncTasks, projectTree.AsLazy(), options.AsLazy()); - provider.Protected().Setup("GetBrowseObjectProperties", ItExpr.IsAny(), ItExpr.IsAny()) - .Returns((ConfiguredProject configuredProject, IProjectItemTree node) => node.BrowseObjectProperties); - - return provider.Object; - } + var provider = new Mock(unconfiguredProject, asyncTasks, projectTree.AsLazy(), options.AsLazy()); + provider.Protected().Setup("GetBrowseObjectProperties", ItExpr.IsAny(), ItExpr.IsAny()) + .Returns((ConfiguredProject configuredProject, IProjectItemTree node) => node.BrowseObjectProperties); + + return provider.Object; } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Xproj/XprojProjectFactoryTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Xproj/XprojProjectFactoryTests.cs index 5ce373736e..aba9893d53 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Xproj/XprojProjectFactoryTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Xproj/XprojProjectFactoryTests.cs @@ -2,38 +2,37 @@ using Microsoft.VisualStudio.Shell.Interop; -namespace Microsoft.VisualStudio.ProjectSystem.VS.Xproj +namespace Microsoft.VisualStudio.ProjectSystem.VS.Xproj; + +public sealed class XprojProjectFactoryTests { - public sealed class XprojProjectFactoryTests + [Fact] + public void UpgradeCheck() { - [Fact] - public void UpgradeCheck() - { - string projectPath = "foo\\bar.xproj"; + string projectPath = "foo\\bar.xproj"; #pragma warning disable VSSDK005 // Avoid instantiating JoinableTaskContext - var factory = new XprojProjectFactory(new Threading.JoinableTaskContext()); + var factory = new XprojProjectFactory(new Threading.JoinableTaskContext()); #pragma warning restore VSSDK005 // Avoid instantiating JoinableTaskContext - var loggedMessages = new List(); - var logger = IVsUpgradeLoggerFactory.CreateLogger(loggedMessages); - - factory.UpgradeProject_CheckOnly( - fileName: projectPath, - logger, - out uint upgradeRequired, - out Guid migratedProjectFactor, - out uint upgradeProjectCapabilityFlags); - - Assert.Equal((uint)__VSPPROJECTUPGRADEVIAFACTORYREPAIRFLAGS.VSPUVF_PROJECT_DEPRECATED, upgradeRequired); - Assert.Equal(typeof(XprojProjectFactory).GUID, migratedProjectFactor); - Assert.Equal(default, upgradeProjectCapabilityFlags); - - LogMessage message = Assert.Single(loggedMessages); - Assert.Equal(projectPath, message.File); - Assert.Equal(Path.GetFileNameWithoutExtension(projectPath), message.Project); - Assert.Equal((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, message.Level); - Assert.Equal(VSResources.XprojNotSupported, message.Message); - } + var loggedMessages = new List(); + var logger = IVsUpgradeLoggerFactory.CreateLogger(loggedMessages); + + factory.UpgradeProject_CheckOnly( + fileName: projectPath, + logger, + out uint upgradeRequired, + out Guid migratedProjectFactor, + out uint upgradeProjectCapabilityFlags); + + Assert.Equal((uint)__VSPPROJECTUPGRADEVIAFACTORYREPAIRFLAGS.VSPUVF_PROJECT_DEPRECATED, upgradeRequired); + Assert.Equal(typeof(XprojProjectFactory).GUID, migratedProjectFactor); + Assert.Equal(default, upgradeProjectCapabilityFlags); + + LogMessage message = Assert.Single(loggedMessages); + Assert.Equal(projectPath, message.File); + Assert.Equal(Path.GetFileNameWithoutExtension(projectPath), message.Project); + Assert.Equal((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, message.Level); + Assert.Equal(VSResources.XprojNotSupported, message.Message); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/PackageContentTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/PackageContentTests.cs index 9b9c83018b..6c4f749991 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/PackageContentTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/PackageContentTests.cs @@ -5,69 +5,68 @@ using VerifyTests; using VerifyXunit; -namespace Microsoft.VisualStudio.Setup +namespace Microsoft.VisualStudio.Setup; + +[UsesVerify] +public sealed class PackageContentTests { - [UsesVerify] - public sealed class PackageContentTests - { - // These files are only added as part of signing. - private const string DigitalSignature = "package/services/digital-signature"; - private const string SettingsRegistrationFileSuffix = "ManagedProjectSystem.registration.json"; - private const string Rels = "_rels/.rels"; + // These files are only added as part of signing. + private const string DigitalSignature = "package/services/digital-signature"; + private const string SettingsRegistrationFileSuffix = "ManagedProjectSystem.registration.json"; + private const string Rels = "_rels/.rels"; - [Fact] - public Task ProjectSystem() - { - IEnumerable files = GetPackageContents("ProjectSystem.vsix"); - VerifierSettings.ScrubLinesContaining(DigitalSignature, Rels, SettingsRegistrationFileSuffix); - return Verifier.Verify(files); - } + [Fact] + public Task ProjectSystem() + { + IEnumerable files = GetPackageContents("ProjectSystem.vsix"); + VerifierSettings.ScrubLinesContaining(DigitalSignature, Rels, SettingsRegistrationFileSuffix); + return Verifier.Verify(files); + } - [Fact] - public Task VisualStudioEditorsSetup() - { - IEnumerable files = GetPackageContents("VisualStudioEditorsSetup.vsix"); - VerifierSettings.ScrubLinesContaining(DigitalSignature, Rels, SettingsRegistrationFileSuffix); - return Verifier.Verify(files); - } + [Fact] + public Task VisualStudioEditorsSetup() + { + IEnumerable files = GetPackageContents("VisualStudioEditorsSetup.vsix"); + VerifierSettings.ScrubLinesContaining(DigitalSignature, Rels, SettingsRegistrationFileSuffix); + return Verifier.Verify(files); + } - [Fact] - public Task CommonFiles() - { - IEnumerable files = GetPackageContents("Microsoft.VisualStudio.ProjectSystem.Managed.CommonFiles.vsix"); - VerifierSettings.ScrubLinesContaining(DigitalSignature); - VerifierSettings.ScrubLinesContaining(SettingsRegistrationFileSuffix); - // manifest.json is the last line for non-signed builds. - // It will not contain a comma in this situation, so we need special logic for that. - VerifierSettings.ScrubLinesWithReplace(s => s.EndsWith("manifest.json") ? " manifest.json," : s); - return Verifier.Verify(files); - } + [Fact] + public Task CommonFiles() + { + IEnumerable files = GetPackageContents("Microsoft.VisualStudio.ProjectSystem.Managed.CommonFiles.vsix"); + VerifierSettings.ScrubLinesContaining(DigitalSignature); + VerifierSettings.ScrubLinesContaining(SettingsRegistrationFileSuffix); + // manifest.json is the last line for non-signed builds. + // It will not contain a comma in this situation, so we need special logic for that. + VerifierSettings.ScrubLinesWithReplace(s => s.EndsWith("manifest.json") ? " manifest.json," : s); + return Verifier.Verify(files); + } - private static IEnumerable GetPackageContents(string vsixName) - { - var rootPath = RepoUtil.FindRepoRootPath(); + private static IEnumerable GetPackageContents(string vsixName) + { + var rootPath = RepoUtil.FindRepoRootPath(); #if DEBUG - var config = "Debug"; + var config = "Debug"; #elif RELEASE - var config = "Release"; + var config = "Release"; #else #error Unexpected configuration #endif - // D:\repos\project-system\artifacts\Debug\VSSetup\Insertion\ProjectSystem.vsix + // D:\repos\project-system\artifacts\Debug\VSSetup\Insertion\ProjectSystem.vsix - var vsixPath = Path.Combine( - rootPath, - "artifacts", - config, - "VSSetup", - "Insertion", - vsixName); + var vsixPath = Path.Combine( + rootPath, + "artifacts", + config, + "VSSetup", + "Insertion", + vsixName); - using var archive = ZipFile.OpenRead(vsixPath); + using var archive = ZipFile.OpenRead(vsixPath); - return archive.Entries.Select(entry => entry.FullName).OrderBy(fn => fn); - } + return archive.Entries.Select(entry => entry.FullName).OrderBy(fn => fn); } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/SwrTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/SwrTests.cs index 2b3b41ba7f..e28f754526 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/SwrTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Setup/SwrTests.cs @@ -5,157 +5,156 @@ using Microsoft.VisualStudio.Utilities; using Xunit.Abstractions; -namespace Microsoft.VisualStudio.Setup +namespace Microsoft.VisualStudio.Setup; + +public sealed class SwrTests { - public sealed class SwrTests - { - private static readonly Regex _swrFolderPattern = new(@"^\s*folder\s+""(?[^""]+)""\s*$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); - private static readonly Regex _swrFilePattern = new(@"^\s*file\s+source=""(?[^""]+)""\s*$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); - private static readonly Regex _xlfFilePattern = new(@"^(?.+\.xaml)\.(?[^.]+)\.xlf$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex _swrFolderPattern = new(@"^\s*folder\s+""(?[^""]+)""\s*$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex _swrFilePattern = new(@"^\s*file\s+source=""(?[^""]+)""\s*$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + private static readonly Regex _xlfFilePattern = new(@"^(?.+\.xaml)\.(?[^.]+)\.xlf$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); - private readonly ITestOutputHelper _output; + private readonly ITestOutputHelper _output; - private const string _xamlFolderPrefix = @"InstallDir:MSBuild\Microsoft\VisualStudio\Managed"; - private const string _xamlFilePrefix = @"$(VisualStudioXamlRulesDir)"; + private const string _xamlFolderPrefix = @"InstallDir:MSBuild\Microsoft\VisualStudio\Managed"; + private const string _xamlFilePrefix = @"$(VisualStudioXamlRulesDir)"; - public SwrTests(ITestOutputHelper output) => _output = output; + public SwrTests(ITestOutputHelper output) => _output = output; - [Fact] - public void CommonFiles_ContainsAllXamlFiles() + [Fact] + public void CommonFiles_ContainsAllXamlFiles() + { + var rootPath = RepoUtil.FindRepoRootPath(); + + var commonFilesFileName = "CommonFiles.swr"; + var swrPath = Path.Combine( + rootPath, + "setup", + "Microsoft.VisualStudio.ProjectSystem.Managed.CommonFiles", + commonFilesFileName); + + var setupFilesByCulture = ReadSwrFiles(swrPath) + .Select(ParseSwrFile) + .Where(pair => pair.File.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + .ToLookup(pair => pair.Culture, pair => pair.File); + + var rulesPath1 = Path.Combine( + rootPath, + "src", + "Microsoft.VisualStudio.ProjectSystem.Managed", + "ProjectSystem", + "Rules"); + var rulesPath2 = Path.Combine( + rootPath, + "src", + "Microsoft.VisualStudio.ProjectSystem.Managed.VS", + "ProjectSystem", + "VS", + "Rules"); + + var ruleFilesByCulture = Directory.EnumerateFiles(rulesPath1, "*", SearchOption.AllDirectories) + .Concat(Directory.EnumerateFiles(rulesPath2, "*", SearchOption.AllDirectories)) + .Select(ParseRepoFile) + .Where(pair => pair.Culture is not null) + .ToLookup(pair => pair.Culture!, pair => pair.File); + + var setupCultures = setupFilesByCulture.Select(p => p.Key).ToList(); + var ruleCultures = ruleFilesByCulture.Select(p => p.Key).ToList(); + + Assert.True( + setupCultures.ToHashSet().SetEquals(ruleCultures), + "Set of cultures must match."); + + var guilty = false; + + foreach (var culture in setupCultures) { - var rootPath = RepoUtil.FindRepoRootPath(); - - var commonFilesFileName = "CommonFiles.swr"; - var swrPath = Path.Combine( - rootPath, - "setup", - "Microsoft.VisualStudio.ProjectSystem.Managed.CommonFiles", - commonFilesFileName); - - var setupFilesByCulture = ReadSwrFiles(swrPath) - .Select(ParseSwrFile) - .Where(pair => pair.File.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) - .ToLookup(pair => pair.Culture, pair => pair.File); - - var rulesPath1 = Path.Combine( - rootPath, - "src", - "Microsoft.VisualStudio.ProjectSystem.Managed", - "ProjectSystem", - "Rules"); - var rulesPath2 = Path.Combine( - rootPath, - "src", - "Microsoft.VisualStudio.ProjectSystem.Managed.VS", - "ProjectSystem", - "VS", - "Rules"); - - var ruleFilesByCulture = Directory.EnumerateFiles(rulesPath1, "*", SearchOption.AllDirectories) - .Concat(Directory.EnumerateFiles(rulesPath2, "*", SearchOption.AllDirectories)) - .Select(ParseRepoFile) - .Where(pair => pair.Culture is not null) - .ToLookup(pair => pair.Culture!, pair => pair.File); - - var setupCultures = setupFilesByCulture.Select(p => p.Key).ToList(); - var ruleCultures = ruleFilesByCulture.Select(p => p.Key).ToList(); - - Assert.True( - setupCultures.ToHashSet().SetEquals(ruleCultures), - "Set of cultures must match."); - - var guilty = false; - - foreach (var culture in setupCultures) - { - var setupFiles = setupFilesByCulture[culture]; - var ruleFiles = ruleFilesByCulture[culture]; + var setupFiles = setupFilesByCulture[culture]; + var ruleFiles = ruleFilesByCulture[culture]; - var embeddedRules = RuleServices.GetAllEmbeddedRules() - .Select(name => name + ".xaml") - .ToList(); + var embeddedRules = RuleServices.GetAllEmbeddedRules() + .Select(name => name + ".xaml") + .ToList(); - // Exclude the ones that are embedded, they won't be installed - ruleFiles = ruleFiles.Except(embeddedRules).ToList(); + // Exclude the ones that are embedded, they won't be installed + ruleFiles = ruleFiles.Except(embeddedRules).ToList(); - foreach (var missing in ruleFiles.Except(setupFiles, StringComparer.OrdinalIgnoreCase)) - { - guilty = true; - _output.WriteLine($"- Missing file {missing} in culture {culture}"); - } - - foreach (var extra in setupFiles.Except(ruleFiles, StringComparer.OrdinalIgnoreCase)) - { - guilty = true; - _output.WriteLine($"- Extra file {extra} in culture {culture}"); - } + foreach (var missing in ruleFiles.Except(setupFiles, StringComparer.OrdinalIgnoreCase)) + { + guilty = true; + _output.WriteLine($"- Missing file {missing} in culture {culture}"); } - Assert.False(guilty, $"There are setup errors in {commonFilesFileName}. See test output for details."); + foreach (var extra in setupFiles.Except(ruleFiles, StringComparer.OrdinalIgnoreCase)) + { + guilty = true; + _output.WriteLine($"- Extra file {extra} in culture {culture}"); + } + } - return; + Assert.False(guilty, $"There are setup errors in {commonFilesFileName}. See test output for details."); - static (string Culture, string File) ParseSwrFile((string Folder, string File) item) - { - var culture = item.Folder.Substring(_xamlFolderPrefix.Length).TrimStart('\\'); - var fileName = culture.Length == 0 - ? item.File.Substring(_xamlFilePrefix.Length) - : item.File.Substring(_xamlFilePrefix.Length + culture.Length + 1); + return; - return (culture, fileName); - } + static (string Culture, string File) ParseSwrFile((string Folder, string File) item) + { + var culture = item.Folder.Substring(_xamlFolderPrefix.Length).TrimStart('\\'); + var fileName = culture.Length == 0 + ? item.File.Substring(_xamlFilePrefix.Length) + : item.File.Substring(_xamlFilePrefix.Length + culture.Length + 1); - static (string? Culture, string? File) ParseRepoFile(string path) - { - var fileName = Path.GetFileName(path); + return (culture, fileName); + } - if (fileName.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) - { - return ("", fileName); - } + static (string? Culture, string? File) ParseRepoFile(string path) + { + var fileName = Path.GetFileName(path); - var match = _xlfFilePattern.Match(fileName); + if (fileName.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + { + return ("", fileName); + } - if (match.Success) - { - return (match.Groups["culture"].Value, match.Groups["filename"].Value); - } + var match = _xlfFilePattern.Match(fileName); - return (null, null); + if (match.Success) + { + return (match.Groups["culture"].Value, match.Groups["filename"].Value); } + + return (null, null); } + } - private static IEnumerable<(string Folder, string File)> ReadSwrFiles(string path) - { - // Parse data with the following structure repeated. - // There may be other lines in the file which are ignored for our purposes. - // - // folder "folder\path" - // file source = "file\path1" - // file source = "file\path2" + private static IEnumerable<(string Folder, string File)> ReadSwrFiles(string path) + { + // Parse data with the following structure repeated. + // There may be other lines in the file which are ignored for our purposes. + // + // folder "folder\path" + // file source = "file\path1" + // file source = "file\path2" - string? folder = null; + string? folder = null; - foreach (var line in File.ReadLines(path)) + foreach (var line in File.ReadLines(path)) + { + var folderMatch = _swrFolderPattern.Match(line); + if (folderMatch.Success) { - var folderMatch = _swrFolderPattern.Match(line); - if (folderMatch.Success) - { - folder = folderMatch.Groups["path"].Value; - continue; - } + folder = folderMatch.Groups["path"].Value; + continue; + } + + var fileMatch = _swrFilePattern.Match(line); + if (fileMatch.Success) + { + if (folder is null) + throw new FileFormatException("'file' entry appears before a 'folder' entry."); + var file = fileMatch.Groups["path"].Value; - var fileMatch = _swrFilePattern.Match(line); - if (fileMatch.Success) + if (folder.StartsWith(_xamlFolderPrefix) && file.StartsWith(_xamlFilePrefix)) { - if (folder is null) - throw new FileFormatException("'file' entry appears before a 'folder' entry."); - var file = fileMatch.Groups["path"].Value; - - if (folder.StartsWith(_xamlFolderPrefix) && file.StartsWith(_xamlFilePrefix)) - { - yield return (folder, file); - } + yield return (folder, file); } } } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Shell/HierarchyIdTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Shell/HierarchyIdTests.cs index 12621c00b2..1156f89afc 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Shell/HierarchyIdTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/Shell/HierarchyIdTests.cs @@ -1,173 +1,172 @@ // 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.md file in the project root for more information. -namespace Microsoft.VisualStudio.Shell +namespace Microsoft.VisualStudio.Shell; + +public class HierarchyIdTests { - public class HierarchyIdTests - { - [Fact] - public void Constructor1_ReturnsEmpty() - { - var hierarchyId = new HierarchyId(); - - Assert.True(hierarchyId.IsEmpty); - } - - [Theory] - [InlineData(0)] - [InlineData((uint)VSConstants.VSITEMID.Nil)] - [InlineData((uint)VSConstants.VSITEMID.Root)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void Constructor_ValueAsId_SetsId(uint id) - { - var hierarchyId = new HierarchyId(id); - - Assert.Equal(id, hierarchyId.Id); - } - - [Fact] - public void IsRoot_WhenIdIsRoot_ReturnsTrue() - { - var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Root); - - Assert.True(hierarchyId.IsRoot); - } - - [Theory] - [InlineData(0)] - [InlineData((uint)VSConstants.VSITEMID.Nil)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void IsRoot_WhenIdIsNotRoot_ReturnsFalse(uint id) - { - var hierarchyId = new HierarchyId(id); - - Assert.False(hierarchyId.IsRoot); - } - - [Fact] - public void IsSelection_WhenIdIsSelection_ReturnsTrue() - { - var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Selection); - - Assert.True(hierarchyId.IsSelection); - } - - [Theory] - [InlineData(0)] - [InlineData((uint)VSConstants.VSITEMID.Nil)] - [InlineData((uint)VSConstants.VSITEMID.Root)] - public void IsSelection_WhenIdIsNotSelection_ReturnsFalse(uint id) - { - var hierarchyId = new HierarchyId(id); - - Assert.False(hierarchyId.IsSelection); - } - - [Fact] - public void IsEmpty_WhenIdIsEmpty_ReturnsTrue() - { - var hierarchyId = new HierarchyId(0); - - Assert.True(hierarchyId.IsEmpty); - } - - [Theory] - [InlineData((uint)VSConstants.VSITEMID.Nil)] - [InlineData((uint)VSConstants.VSITEMID.Root)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void IsEmpty_WhenIdIsNotEmpty_ReturnsFalse(uint id) - { - var hierarchyId = new HierarchyId(id); - - Assert.False(hierarchyId.IsEmpty); - } - - [Fact] - public void IsNil_WhenIdIsNil_ReturnsTrue() - { - var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Nil); - - Assert.True(hierarchyId.IsNil); - } - - [Theory] - [InlineData(0)] - [InlineData((uint)VSConstants.VSITEMID.Root)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void IsEmpty_WhenIdIsNotNil_ReturnsFalse(uint id) - { - var hierarchyId = new HierarchyId(id); - - Assert.False(hierarchyId.IsNil); - } - - [Fact] - public void IsNilOrEmpty_WhenIdIsNil_ReturnsTrue() - { - var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Nil); - - Assert.True(hierarchyId.IsNilOrEmpty); - } - - [Fact] - public void IsNilOrEmpty_WhenIdIsEmpty_ReturnsTrue() - { - var hierarchyId = new HierarchyId(0); - - Assert.True(hierarchyId.IsNilOrEmpty); - } - - [Theory] - [InlineData((uint)VSConstants.VSITEMID.Root)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void IsEmpty_WhenIdIsNotNilOrEmpty_ReturnsFalse(uint id) - { - var hierarchyId = new HierarchyId(id); - - Assert.False(hierarchyId.IsNilOrEmpty); - } - - [Fact] - public void Root_IsRoot_ReturnsTrue() - { - Assert.True(HierarchyId.Root.IsRoot); - } - - [Fact] - public void Selection_IsSelection_ReturnsTrue() - { - Assert.True(HierarchyId.Selection.IsSelection); - } - - [Fact] - public void Nil_IsNil_ReturnsTrue() - { - Assert.True(HierarchyId.Nil.IsNil); - } - - [Theory] - [InlineData(0)] - [InlineData((uint)VSConstants.VSITEMID.Nil)] - [InlineData((uint)VSConstants.VSITEMID.Root)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void Implicit_ToUInt32_ReturnsId(uint id) - { - var hierarchyId = new HierarchyId(id); - - uint result = hierarchyId; - - Assert.Equal(id, result); - } - - [Theory] - [InlineData(0)] - [InlineData((uint)VSConstants.VSITEMID.Nil)] - [InlineData((uint)VSConstants.VSITEMID.Root)] - [InlineData((uint)VSConstants.VSITEMID.Selection)] - public void Implicit_ToHierarchyId_SetsId(uint id) - { - HierarchyId hierarchyId = id; - - Assert.Equal(id, hierarchyId.Id); - } + [Fact] + public void Constructor1_ReturnsEmpty() + { + var hierarchyId = new HierarchyId(); + + Assert.True(hierarchyId.IsEmpty); + } + + [Theory] + [InlineData(0)] + [InlineData((uint)VSConstants.VSITEMID.Nil)] + [InlineData((uint)VSConstants.VSITEMID.Root)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void Constructor_ValueAsId_SetsId(uint id) + { + var hierarchyId = new HierarchyId(id); + + Assert.Equal(id, hierarchyId.Id); + } + + [Fact] + public void IsRoot_WhenIdIsRoot_ReturnsTrue() + { + var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Root); + + Assert.True(hierarchyId.IsRoot); + } + + [Theory] + [InlineData(0)] + [InlineData((uint)VSConstants.VSITEMID.Nil)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void IsRoot_WhenIdIsNotRoot_ReturnsFalse(uint id) + { + var hierarchyId = new HierarchyId(id); + + Assert.False(hierarchyId.IsRoot); + } + + [Fact] + public void IsSelection_WhenIdIsSelection_ReturnsTrue() + { + var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Selection); + + Assert.True(hierarchyId.IsSelection); + } + + [Theory] + [InlineData(0)] + [InlineData((uint)VSConstants.VSITEMID.Nil)] + [InlineData((uint)VSConstants.VSITEMID.Root)] + public void IsSelection_WhenIdIsNotSelection_ReturnsFalse(uint id) + { + var hierarchyId = new HierarchyId(id); + + Assert.False(hierarchyId.IsSelection); + } + + [Fact] + public void IsEmpty_WhenIdIsEmpty_ReturnsTrue() + { + var hierarchyId = new HierarchyId(0); + + Assert.True(hierarchyId.IsEmpty); + } + + [Theory] + [InlineData((uint)VSConstants.VSITEMID.Nil)] + [InlineData((uint)VSConstants.VSITEMID.Root)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void IsEmpty_WhenIdIsNotEmpty_ReturnsFalse(uint id) + { + var hierarchyId = new HierarchyId(id); + + Assert.False(hierarchyId.IsEmpty); + } + + [Fact] + public void IsNil_WhenIdIsNil_ReturnsTrue() + { + var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Nil); + + Assert.True(hierarchyId.IsNil); + } + + [Theory] + [InlineData(0)] + [InlineData((uint)VSConstants.VSITEMID.Root)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void IsEmpty_WhenIdIsNotNil_ReturnsFalse(uint id) + { + var hierarchyId = new HierarchyId(id); + + Assert.False(hierarchyId.IsNil); + } + + [Fact] + public void IsNilOrEmpty_WhenIdIsNil_ReturnsTrue() + { + var hierarchyId = new HierarchyId((uint)VSConstants.VSITEMID.Nil); + + Assert.True(hierarchyId.IsNilOrEmpty); + } + + [Fact] + public void IsNilOrEmpty_WhenIdIsEmpty_ReturnsTrue() + { + var hierarchyId = new HierarchyId(0); + + Assert.True(hierarchyId.IsNilOrEmpty); + } + + [Theory] + [InlineData((uint)VSConstants.VSITEMID.Root)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void IsEmpty_WhenIdIsNotNilOrEmpty_ReturnsFalse(uint id) + { + var hierarchyId = new HierarchyId(id); + + Assert.False(hierarchyId.IsNilOrEmpty); + } + + [Fact] + public void Root_IsRoot_ReturnsTrue() + { + Assert.True(HierarchyId.Root.IsRoot); + } + + [Fact] + public void Selection_IsSelection_ReturnsTrue() + { + Assert.True(HierarchyId.Selection.IsSelection); + } + + [Fact] + public void Nil_IsNil_ReturnsTrue() + { + Assert.True(HierarchyId.Nil.IsNil); + } + + [Theory] + [InlineData(0)] + [InlineData((uint)VSConstants.VSITEMID.Nil)] + [InlineData((uint)VSConstants.VSITEMID.Root)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void Implicit_ToUInt32_ReturnsId(uint id) + { + var hierarchyId = new HierarchyId(id); + + uint result = hierarchyId; + + Assert.Equal(id, result); + } + + [Theory] + [InlineData(0)] + [InlineData((uint)VSConstants.VSITEMID.Nil)] + [InlineData((uint)VSConstants.VSITEMID.Root)] + [InlineData((uint)VSConstants.VSITEMID.Selection)] + public void Implicit_ToHierarchyId_SetsId(uint id) + { + HierarchyId hierarchyId = id; + + Assert.Equal(id, hierarchyId.Id); } } From 719cba8ce70fd4faca907c207847164805be2559 Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Wed, 11 Dec 2024 11:04:33 +1100 Subject: [PATCH 5/6] Make block-scoped namespaces an error --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 1f863f6b71..42339a6ddb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -133,6 +133,9 @@ dotnet_naming_style.pascal_case_and_prefix_with_I_style.capitalization # CSharp code style settings: [*.cs] +# Use file-scoped namespaces +csharp_style_namespace_declarations = file_scoped:error + # Prefer "var" only when the type is apparent csharp_style_var_for_built_in_types = false:suggestion csharp_style_var_when_type_is_apparent = true:suggestion From 660d65b68f824556b2b9de4c3d1371510c5e0f3f Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Wed, 11 Dec 2024 12:34:33 +1100 Subject: [PATCH 6/6] Add .git-blame-ignore-revs This file tells git/GitHub to ignore certain commits from the blame view. The file is configured to exclude a recent solution-wide conversion from block-scoped to file-scoped namespaces, which touched most lines in the repo. With this, GitHub's blame view will ignore the change, making browsing changes on GitHub easier. --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..0f0cc921f7 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# convert solution to use file-scoped namespaces +e13927f27b47320d8179af850b1ea31e8b24e12b